using ChatBot.Data; using ChatBot.Migrations; using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace ChatBot.Tests.Data; public class MigrationsTests : IDisposable { private readonly ServiceProvider _serviceProvider; private readonly ChatBotDbContext _dbContext; public MigrationsTests() { var services = new ServiceCollection(); // Add in-memory database with unique name per test services.AddDbContext(options => options.UseInMemoryDatabase(Guid.NewGuid().ToString()) ); _serviceProvider = services.BuildServiceProvider(); _dbContext = _serviceProvider.GetRequiredService(); } [Fact] public void InitialCreateMigration_ShouldHaveCorrectName() { // Arrange var migration = new InitialCreate(); // Assert migration.Should().NotBeNull(); migration.GetType().Name.Should().Be("InitialCreate"); } [Fact] public void InitialCreateMigration_ShouldInheritFromMigration() { // Arrange var migration = new InitialCreate(); // Assert migration.Should().BeAssignableTo(); } [Fact] public void InitialCreateMigration_ShouldBeInstantiable() { // Arrange & Act var migration = new InitialCreate(); // Assert migration.Should().NotBeNull(); } [Fact] public void InitialCreateMigration_ShouldHaveCorrectConstants() { // Arrange var migration = new InitialCreate(); // Act & Assert // Use reflection to access private constants var migrationType = typeof(InitialCreate); var chatSessionsTableNameField = migrationType.GetField( "ChatSessionsTableName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static ); chatSessionsTableNameField.Should().NotBeNull(); chatSessionsTableNameField!.GetValue(null).Should().Be("chat_sessions"); var chatMessagesTableNameField = migrationType.GetField( "ChatMessagesTableName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static ); chatMessagesTableNameField.Should().NotBeNull(); chatMessagesTableNameField!.GetValue(null).Should().Be("chat_messages"); var chatSessionsIdColumnField = migrationType.GetField( "ChatSessionsIdColumn", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static ); chatSessionsIdColumnField.Should().NotBeNull(); chatSessionsIdColumnField!.GetValue(null).Should().Be("id"); var chatMessagesSessionIdColumnField = migrationType.GetField( "ChatMessagesSessionIdColumn", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static ); chatMessagesSessionIdColumnField.Should().NotBeNull(); chatMessagesSessionIdColumnField!.GetValue(null).Should().Be("session_id"); var integerTypeField = migrationType.GetField( "IntegerType", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static ); integerTypeField.Should().NotBeNull(); integerTypeField!.GetValue(null).Should().Be("integer"); } [Fact] public async Task InitialCreateMigration_ShouldApplySuccessfullyToDatabase() { // Arrange var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; using var context = new ChatBotDbContext(options); // Act await context.Database.EnsureCreatedAsync(); // Assert var canConnect = await context.Database.CanConnectAsync(); canConnect.Should().BeTrue(); // Verify tables exist by trying to query them var sessions = await context.ChatSessions.ToListAsync(); var messages = await context.ChatMessages.ToListAsync(); sessions.Should().NotBeNull(); messages.Should().NotBeNull(); } [Fact] public async Task InitialCreateMigration_ShouldCreateCorrectTableStructure() { // Arrange var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; using var context = new ChatBotDbContext(options); // Act await context.Database.EnsureCreatedAsync(); // Assert var model = context.Model; // Check chat_sessions entity var chatSessionEntity = model.FindEntityType( typeof(ChatBot.Models.Entities.ChatSessionEntity) ); chatSessionEntity.Should().NotBeNull(); chatSessionEntity!.GetTableName().Should().Be("chat_sessions"); // Check chat_messages entity var chatMessageEntity = model.FindEntityType( typeof(ChatBot.Models.Entities.ChatMessageEntity) ); chatMessageEntity.Should().NotBeNull(); chatMessageEntity!.GetTableName().Should().Be("chat_messages"); // Check foreign key relationship var foreignKeys = chatMessageEntity.GetForeignKeys(); foreignKeys.Should().HaveCount(1); var foreignKey = foreignKeys.First(); foreignKey.PrincipalEntityType.Should().Be(chatSessionEntity); foreignKey.Properties.Should().HaveCount(1); foreignKey.Properties.First().Name.Should().Be("SessionId"); foreignKey.DeleteBehavior.Should().Be(DeleteBehavior.Cascade); } [Fact] public async Task InitialCreateMigration_ShouldCreateIndexes() { // Arrange var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; using var context = new ChatBotDbContext(options); // Act await context.Database.EnsureCreatedAsync(); // Assert var model = context.Model; // Check chat_sessions entity indexes var chatSessionEntity = model.FindEntityType( typeof(ChatBot.Models.Entities.ChatSessionEntity) ); chatSessionEntity.Should().NotBeNull(); var sessionIndexes = chatSessionEntity!.GetIndexes().ToList(); sessionIndexes.Should().NotBeEmpty(); // Check chat_messages entity indexes var chatMessageEntity = model.FindEntityType( typeof(ChatBot.Models.Entities.ChatMessageEntity) ); chatMessageEntity.Should().NotBeNull(); var messageIndexes = chatMessageEntity!.GetIndexes().ToList(); messageIndexes.Should().NotBeEmpty(); } [Fact] public async Task InitialCreateMigration_ShouldCreateUniqueConstraintOnSessionId() { // Arrange var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; using var context = new ChatBotDbContext(options); // Act await context.Database.EnsureCreatedAsync(); // Assert var model = context.Model; // Check chat_sessions entity has unique index on SessionId var chatSessionEntity = model.FindEntityType( typeof(ChatBot.Models.Entities.ChatSessionEntity) ); chatSessionEntity.Should().NotBeNull(); var sessionIdProperty = chatSessionEntity!.FindProperty("SessionId"); sessionIdProperty.Should().NotBeNull(); var uniqueIndexes = chatSessionEntity .GetIndexes() .Where(i => i.IsUnique && i.Properties.Contains(sessionIdProperty)) .ToList(); uniqueIndexes.Should().NotBeEmpty(); } [Fact] public async Task InitialCreateMigration_ShouldCreateCompositeIndexOnSessionIdAndMessageOrder() { // Arrange var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; using var context = new ChatBotDbContext(options); // Act await context.Database.EnsureCreatedAsync(); // Assert var model = context.Model; // Check chat_messages entity has composite index var chatMessageEntity = model.FindEntityType( typeof(ChatBot.Models.Entities.ChatMessageEntity) ); chatMessageEntity.Should().NotBeNull(); var sessionIdProperty = chatMessageEntity!.FindProperty("SessionId"); var messageOrderProperty = chatMessageEntity.FindProperty("MessageOrder"); sessionIdProperty.Should().NotBeNull(); messageOrderProperty.Should().NotBeNull(); var compositeIndexes = chatMessageEntity .GetIndexes() .Where(i => i.Properties.Count == 2 && i.Properties.Contains(sessionIdProperty) && i.Properties.Contains(messageOrderProperty) ) .ToList(); compositeIndexes.Should().NotBeEmpty(); } [Fact] public async Task InitialCreateMigration_ShouldSupportCascadeDelete() { // Arrange var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; using var context = new ChatBotDbContext(options); // Act await context.Database.EnsureCreatedAsync(); // Create test data var session = new ChatBot.Models.Entities.ChatSessionEntity { SessionId = "test-session", ChatId = 12345L, ChatType = "private", CreatedAt = DateTime.UtcNow, LastUpdatedAt = DateTime.UtcNow, }; context.ChatSessions.Add(session); await context.SaveChangesAsync(); var message = new ChatBot.Models.Entities.ChatMessageEntity { SessionId = session.Id, Content = "Test message", Role = "user", CreatedAt = DateTime.UtcNow, MessageOrder = 1, }; context.ChatMessages.Add(message); await context.SaveChangesAsync(); // Verify data exists var messageCount = await context.ChatMessages.CountAsync(); messageCount.Should().Be(1); // Test cascade delete context.ChatSessions.Remove(session); await context.SaveChangesAsync(); // Assert - message should be deleted due to cascade var remainingMessageCount = await context.ChatMessages.CountAsync(); remainingMessageCount.Should().Be(0); } public void Dispose() { _dbContext?.Dispose(); _serviceProvider?.Dispose(); } }