using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.InMemory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using LittleShop.Data; using LittleShop.Services; using Microsoft.Extensions.DependencyInjection.Extensions; using Moq; using System.Linq; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; using System.Collections.Generic; using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; namespace LittleShop.Tests.Infrastructure; public class TestWebApplicationFactory : WebApplicationFactory { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { // Remove the existing DbContext registration var contextDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(LittleShopContext)); if (contextDescriptor != null) services.Remove(contextDescriptor); var optionsDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions)); if (optionsDescriptor != null) services.Remove(optionsDescriptor); // Add InMemory database for testing with unique name per test run var databaseName = $"InMemoryDbForTesting_{Guid.NewGuid()}"; services.AddDbContext(options => options.UseInMemoryDatabase(databaseName) .ConfigureWarnings(warnings => warnings.Default(WarningBehavior.Ignore))); // Add test configuration var configuration = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { {"ConnectionStrings:DefaultConnection", $"Data Source={databaseName}.db"}, {"Jwt:Key", "test-key-that-is-at-least-32-characters-long-for-security"}, {"Jwt:Issuer", "LittleShop"}, {"Jwt:Audience", "LittleShop"}, {"SilverPay:BaseUrl", "http://test.example.com"}, {"SilverPay:ApiKey", "test-api-key"}, {"CORS:AllowedOrigins:0", "http://localhost:3000"} }) .Build(); services.AddSingleton(configuration); // Add test authentication services.AddAuthentication("Test") .AddScheme("Test", options => { }); // Mock external services that might cause issues in tests services.Replace(ServiceDescriptor.Scoped(_ => Mock.Of())); services.Replace(ServiceDescriptor.Scoped(_ => Mock.Of())); services.Replace(ServiceDescriptor.Scoped(_ => Mock.Of())); services.Replace(ServiceDescriptor.Scoped(_ => Mock.Of())); services.Replace(ServiceDescriptor.Scoped(_ => Mock.Of())); services.Replace(ServiceDescriptor.Scoped(_ => Mock.Of())); // Keep real implementations for business logic services services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); // Add validation service services.TryAddSingleton(); // Build service provider var sp = services.BuildServiceProvider(); // Create scope for database initialization using (var scope = sp.CreateScope()) { var scopedServices = scope.ServiceProvider; var db = scopedServices.GetRequiredService(); var logger = scopedServices.GetRequiredService>(); // Ensure database is created db.Database.EnsureCreated(); try { // Seed test data if needed SeedTestData(db); } catch (Exception ex) { logger.LogError(ex, "An error occurred seeding the database with test data."); } } }); builder.UseEnvironment("Testing"); } private static void SeedTestData(LittleShopContext context) { // Seed test data will be added as needed for specific tests context.SaveChanges(); } } public class TestAuthenticationHandler : AuthenticationHandler { public TestAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder) { } protected override Task HandleAuthenticateAsync() { var claims = new[] { new Claim(ClaimTypes.Name, "TestUser"), new Claim(ClaimTypes.NameIdentifier, "123"), new Claim(ClaimTypes.Role, "Admin") }; var identity = new ClaimsIdentity(claims, "Test"); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, "Test"); return Task.FromResult(AuthenticateResult.Success(ticket)); } }