using ChatBot.Data; using ChatBot.Services; using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Moq; namespace ChatBot.Tests.Services; public class DatabaseInitializationServiceExceptionTests { [Fact] public async Task StartAsync_WhenDatabaseDoesNotExist_ShouldRetryWithMigration() { // Arrange var dbPath = $"TestDb_{Guid.NewGuid()}.db"; // Ensure database does not exist if (File.Exists(dbPath)) { File.Delete(dbPath); } var services = new ServiceCollection(); services.AddDbContext(options => options.UseSqlite($"Data Source={dbPath}") .ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning)) ); var serviceProvider = services.BuildServiceProvider(); var loggerMock = new Mock>(); var service = new DatabaseInitializationService(serviceProvider, loggerMock.Object); try { // Act await service.StartAsync(CancellationToken.None); // Assert - database should be created File.Exists(dbPath).Should().BeTrue(); loggerMock.Verify( x => x.Log( LogLevel.Information, It.IsAny(), It.Is( (v, t) => v.ToString()!.Contains("Database initialization completed successfully") ), It.IsAny(), It.IsAny>() ), Times.Once ); } finally { // Cleanup serviceProvider.Dispose(); GC.Collect(); GC.WaitForPendingFinalizers(); if (File.Exists(dbPath)) { try { File.Delete(dbPath); } catch { /* Ignore cleanup errors */ } } } } [Fact] public async Task StartAsync_WhenCanConnectThrowsSpecificException_ShouldHandleGracefully() { // Arrange var dbPath = $"TestDb_{Guid.NewGuid()}.db"; var services = new ServiceCollection(); // Use SQLite with a valid connection string services.AddDbContext(options => options.UseSqlite($"Data Source={dbPath}") .ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning)) ); var serviceProvider = services.BuildServiceProvider(); var loggerMock = new Mock>(); var service = new DatabaseInitializationService(serviceProvider, loggerMock.Object); try { // Act await service.StartAsync(CancellationToken.None); // Assert - should complete successfully even if database didn't exist initially loggerMock.Verify( x => x.Log( LogLevel.Information, It.IsAny(), It.Is( (v, t) => v.ToString()!.Contains("Database initialization completed successfully") ), It.IsAny(), It.IsAny>() ), Times.Once ); } finally { // Cleanup serviceProvider.Dispose(); GC.Collect(); GC.WaitForPendingFinalizers(); if (File.Exists(dbPath)) { try { File.Delete(dbPath); } catch { /* Ignore cleanup errors */ } } } } [Fact] public async Task StartAsync_WithCanceledToken_ShouldThrowOperationCanceledException() { // Arrange var serviceProviderMock = new Mock(); var loggerMock = new Mock>(); var cts = new CancellationTokenSource(); cts.Cancel(); // Cancel before starting serviceProviderMock .Setup(x => x.GetService(typeof(IServiceScopeFactory))) .Returns((IServiceScopeFactory)null!); var service = new DatabaseInitializationService( serviceProviderMock.Object, loggerMock.Object ); // Act & Assert var act = async () => await service.StartAsync(cts.Token); await act.Should().ThrowAsync(); } }