using MessageService.Domain.Entities; using MessageService.Domain.Enums; using MessageService.Infrastructure; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Xunit; namespace MessageService.Tests; public sealed class ConversationModelTests { [Fact] public void Conversation_read_state_and_unread_count_are_consistent() { var conversation = new Conversation( Guid.NewGuid(), Guid.NewGuid(), string.Empty, "target", null, 0, ChatType.PRIVATE, string.Empty); conversation.IncrementUnread(); conversation.IncrementUnread(); conversation.MarkAsRead(12); Assert.Equal(0, conversation.UnreadCount); Assert.Equal(12, conversation.LastReadSequenceId); } [Fact] public void Marking_conversation_read_does_not_change_last_message_activity() { var conversation = new Conversation( Guid.NewGuid(), Guid.NewGuid(), string.Empty, "target", null, 1, ChatType.PRIVATE, "old"); var messageTime = new DateTimeOffset(2026, 9, 13, 8, 30, 0, TimeSpan.Zero); conversation.UpdateLastMessage("new", messageTime); conversation.MarkAsRead(42); Assert.Equal(messageTime, conversation.LastMessageTime); Assert.Equal(42, conversation.LastReadSequenceId); Assert.Equal(0, conversation.UnreadCount); } [Fact] public void Active_conversation_key_is_generated_and_unique() { using var db = CreateContext(); var entity = db.Model.FindEntityType(typeof(Conversation)); var property = entity!.FindProperty("ActiveConversationKey"); var index = entity.GetIndexes().Single(item => item.Properties.Contains(property!)); Assert.NotNull(property!.GetComputedColumnSql()); Assert.True(index.IsUnique); } [Fact] public void Migration_script_contains_deduplication_and_active_unique_index() { using var db = CreateContext(); var migrator = db.Database.GetService(); var script = migrator.GenerateScript( "20260909000100_ApiAlignmentFixes", "20260911000100_ConversationUniqueness"); Assert.Contains("conversation_dedup", script); Assert.Contains("UX_conversations_ActiveConversationKey", script); Assert.Contains("ROW_NUMBER() OVER", script); } [Fact] public void Activity_migration_backfills_latest_message_time_and_adds_search_index() { using var db = CreateContext(); var migrator = db.Database.GetService(); var script = migrator.GenerateScript( "20260911000100_ConversationUniqueness", "20260913000100_ConversationActivityAndMessageSearch"); Assert.Contains("LastMessageTime", script); Assert.Contains("MAX(`message`.`CreationTime`)", script); Assert.Contains("IX_messages_StreamKey_MsgType_State_SequenceId", script); } private static MessageDbContext CreateContext() { const string connectionString = "Server=localhost;Database=im_message_tests;User=test;Password=test"; var options = new DbContextOptionsBuilder() .UseMySql(connectionString, new MySqlServerVersion(new Version(8, 0, 36))) .Options; return new MessageDbContext(options, null!); } }