Files
IKAR/ikar_server/src/Ikar.Server.Tests/ChannelDtoMapperTests.cs
T
sevenhill 1ef2b7420e
Tests / test (push) Canceled after 0s
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Native crypto / native-crypto (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
Port Komet UI improvements and implement IKAR ghost mode
Adapt video editing, decrypted message actions, custom themes and gestures from PR 166. Track visible server presence per session and preserve delivery for invisible clients.
2026-10-07 22:07:05 +03:00

272 lines
9.6 KiB
C#

using Ikar.Server.Data.Entities;
using Ikar.Server.Infrastructure;
using Ikar.Shared;
namespace Ikar.Server.Tests;
public sealed class ChannelDtoMapperTests
{
[Fact]
public void ChannelDetails_HideOtherSubscribersFromRegularSubscriber()
{
var owner = CreateUser("owner", "Owner");
var subscriber = CreateUser("subscriber", "Subscriber");
var hiddenSubscriber = CreateUser("hidden", "Hidden Subscriber");
var channel = CreateChannel(owner, subscriber, hiddenSubscriber);
var subscriberDetails = channel.ToDetailsDto(subscriber.Id, new PresenceTracker());
var ownerDetails = channel.ToDetailsDto(owner.Id, new PresenceTracker());
Assert.Equal(3, subscriberDetails.SubscriberCount);
Assert.Equal("Product news and release notes.", subscriberDetails.Description);
Assert.Equal(1, subscriberDetails.AdminCount);
Assert.False(subscriberDetails.CanPostAsChannel);
Assert.False(subscriberDetails.CanSendMessages);
Assert.Contains(subscriberDetails.Members!, member => member.User.Id == owner.Id);
Assert.Contains(subscriberDetails.Members!, member => member.User.Id == subscriber.Id);
Assert.DoesNotContain(subscriberDetails.Members!, member => member.User.Id == hiddenSubscriber.Id);
Assert.Equal(3, ownerDetails.Members!.Count);
Assert.True(ownerDetails.CanPostAsChannel);
}
[Fact]
public void PublicChannelDetails_AllowPreviewAndSubscribe_ForNonMember()
{
var owner = CreateUser("owner", "Owner");
var subscriber = CreateUser("subscriber", "Subscriber");
var viewer = CreateUser("viewer", "Viewer");
var channel = CreateChannel(owner, subscriber);
channel.PublicUsername = "ikar_updates";
var details = channel.ToDetailsDto(viewer.Id, new PresenceTracker());
Assert.Equal(2, details.SubscriberCount);
Assert.False(details.IsSubscribed);
Assert.True(details.CanSubscribe);
Assert.False(details.CanSendMessages);
Assert.False(details.CanPostAsChannel);
var visibleMember = Assert.Single(details.Members!);
Assert.Equal(owner.Id, visibleMember.User.Id);
}
[Fact]
public void PublicChannelPreview_HidesContactOnlyProfileFields_ForNonMember()
{
var owner = CreateUser("owner", "Owner");
owner.PhoneNumber = "+10000000001";
owner.AvatarStoragePath = "owner-avatar.png";
owner.PublicLastSeenUnixMs = DateTimeOffset.UtcNow.AddMinutes(-5).ToUnixTimeMilliseconds();
owner.PrivacySettings = new UserPrivacySettings
{
UserId = owner.Id,
User = owner,
PhoneNumberVisibility = "contacts",
LastSeenVisibility = "contacts",
ProfilePhotoVisibility = "contacts"
};
owner.Sessions.Add(new UserSession
{
Id = Guid.NewGuid(),
UserId = owner.Id,
User = owner,
LastSeenAt = DateTimeOffset.UtcNow.AddMinutes(-5)
});
var viewer = CreateUser("viewer", "Viewer");
var channel = CreateChannel(owner);
channel.PublicUsername = "ikar_updates";
var details = channel.ToDetailsDto(viewer.Id, new PresenceTracker());
var visibleMember = Assert.Single(details.Members!);
Assert.Equal(owner.Id, visibleMember.User.Id);
Assert.Null(visibleMember.User.PhoneNumber);
Assert.Null(visibleMember.User.LastSeenAt);
Assert.Null(visibleMember.User.AvatarPath);
}
[Fact]
public void ChannelDetails_ShowsContactOnlyProfileFields_ForSubscriber()
{
var owner = CreateUser("owner", "Owner");
owner.PhoneNumber = "+10000000001";
owner.AvatarStoragePath = "owner-avatar.png";
owner.PublicLastSeenUnixMs = DateTimeOffset.UtcNow.AddMinutes(-5).ToUnixTimeMilliseconds();
owner.PrivacySettings = new UserPrivacySettings
{
UserId = owner.Id,
User = owner,
PhoneNumberVisibility = "contacts",
LastSeenVisibility = "contacts",
ProfilePhotoVisibility = "contacts"
};
owner.Sessions.Add(new UserSession
{
Id = Guid.NewGuid(),
UserId = owner.Id,
User = owner,
LastSeenAt = DateTimeOffset.UtcNow.AddMinutes(-5)
});
var subscriber = CreateUser("subscriber", "Subscriber");
var channel = CreateChannel(owner, subscriber);
var details = channel.ToDetailsDto(subscriber.Id, new PresenceTracker());
var ownerMember = Assert.Single(details.Members!, member => member.User.Id == owner.Id);
Assert.Equal(owner.PhoneNumber, ownerMember.User.PhoneNumber);
Assert.NotNull(ownerMember.User.LastSeenAt);
Assert.NotNull(ownerMember.User.AvatarPath);
}
[Fact]
public void ChannelPost_MasksSenderForRegularSubscriber_AndKeepsViewCount()
{
var owner = CreateUser("owner", "Owner");
var subscriber = CreateUser("subscriber", "Subscriber");
var channel = CreateChannel(owner, subscriber);
var post = new Message
{
Id = Guid.NewGuid(),
ChatId = channel.Id,
Chat = channel,
SenderId = owner.Id,
Sender = owner,
Text = "Channel post",
PostedAsChannel = true,
SentAt = DateTimeOffset.UtcNow
};
post.Views.Add(new MessageView
{
MessageId = post.Id,
ViewerUserId = subscriber.Id,
ViewedAt = DateTimeOffset.UtcNow
});
var subscriberDto = post.ToDto(new PresenceTracker(), subscriber.Id);
var ownerDto = post.ToDto(new PresenceTracker(), owner.Id);
Assert.True(subscriberDto.PostedAsChannel);
Assert.Equal(channel.Id, subscriberDto.Sender.Id);
Assert.Equal("Ikar Updates", subscriberDto.DisplayAuthorName);
Assert.Equal(1, subscriberDto.ViewCount);
Assert.Equal(owner.Id, ownerDto.Sender.Id);
}
[Fact]
public void MessageAttachment_MapsExplicitKindToVoiceNote()
{
var owner = CreateUser("owner", "Owner");
var channel = CreateChannel(owner);
var message = new Message
{
Id = Guid.NewGuid(),
ChatId = channel.Id,
Chat = channel,
SenderId = owner.Id,
Sender = owner,
Text = string.Empty,
SentAt = DateTimeOffset.UtcNow
};
message.Attachments.Add(new MessageAttachment
{
Id = Guid.NewGuid(),
MessageId = message.Id,
Message = message,
OriginalFileName = "VOICE.OGG",
ContentType = "application/ogg",
Kind = AttachmentKind.VoiceNote,
StoragePath = "VOICE.OGG",
FileSizeBytes = 4096
});
var dto = message.ToDto(new PresenceTracker(), owner.Id);
var attachment = Assert.Single(dto.Attachments);
Assert.Equal(AttachmentKind.VoiceNote, attachment.Kind);
}
[Fact]
public void MessageAttachment_InfersVoiceNoteFromOggLikeFileName()
{
var owner = CreateUser("owner", "Owner");
var channel = CreateChannel(owner);
var message = new Message
{
Id = Guid.NewGuid(),
ChatId = channel.Id,
Chat = channel,
SenderId = owner.Id,
Sender = owner,
Text = string.Empty,
SentAt = DateTimeOffset.UtcNow
};
message.Attachments.Add(new MessageAttachment
{
Id = Guid.NewGuid(),
MessageId = message.Id,
Message = message,
OriginalFileName = "Правила ogg",
ContentType = "application/octet-stream",
StoragePath = "rules",
FileSizeBytes = 4096
});
var dto = message.ToDto(new PresenceTracker(), owner.Id);
var attachment = Assert.Single(dto.Attachments);
Assert.Equal(AttachmentKind.VoiceNote, attachment.Kind);
}
private static Chat CreateChannel(User owner, params User[] subscribers)
{
var channel = new Chat
{
Id = Guid.NewGuid(),
Type = ChatType.Channel,
Title = "Ikar Updates",
Description = "Product news and release notes.",
CreatedById = owner.Id,
CreatedBy = owner,
CreatedAt = DateTimeOffset.UtcNow
};
channel.Members.Add(new ChatMember
{
ChatId = channel.Id,
Chat = channel,
UserId = owner.Id,
User = owner,
IsOwner = true,
Role = ChatMemberRole.Owner,
CanPinMessages = true,
CanDeleteMessages = true,
CanInviteUsers = true,
CanPostMessages = true
});
foreach (var subscriber in subscribers)
{
channel.Members.Add(new ChatMember
{
ChatId = channel.Id,
Chat = channel,
UserId = subscriber.Id,
User = subscriber,
Role = ChatMemberRole.Member,
CanPinMessages = false,
CanDeleteMessages = false,
CanInviteUsers = false,
CanPostMessages = false
});
}
return channel;
}
private static User CreateUser(string username, string displayName) =>
new()
{
Id = Guid.NewGuid(),
Username = username,
NormalizedUsername = username.ToUpperInvariant(),
DisplayName = displayName,
PasswordHash = "test"
};
}