using System.Security.Cryptography; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using SIGCM2.Application.Abstractions.Security; using SIGCM2.Infrastructure.Security; using Xunit; namespace SIGCM2.TestSupport; /// /// WebApplicationFactory for integration tests against SIGCM2.Api. /// Uses SIGCM2_Test database (separate from production SIGCM2). /// public sealed class TestWebAppFactory : WebApplicationFactory, IAsyncLifetime { private const string TestConnectionString = "Server=TECNICA3;Database=SIGCM2_Test;User Id=desarrollo;Password=desarrollo2026;TrustServerCertificate=True;"; // Resolved once — absolute paths independent of working directory private static readonly string RepoRoot = ResolveRepoRoot(); private static readonly string PrivateKeyPath = Path.Combine(RepoRoot, "src", "api", "SIGCM2.Api", "keys", "private.pem"); private static readonly string PublicKeyPath = Path.Combine(RepoRoot, "src", "api", "SIGCM2.Api", "keys", "public.pem"); private readonly SqlTestFixture _dbFixture = new(TestConnectionString); protected override void ConfigureWebHost(IWebHostBuilder builder) { // Step 1: Override configuration BEFORE services are built builder.ConfigureAppConfiguration((ctx, config) => { // Clear all existing sources and rebuild with test values // This ensures our paths win over appsettings.json config.AddInMemoryCollection(new Dictionary { ["ConnectionStrings:SqlServer"] = TestConnectionString, ["Jwt:Issuer"] = "sigcm2.api", ["Jwt:Audience"] = "sigcm2.web", ["Jwt:AccessTokenMinutes"] = "60", ["Jwt:PrivateKeyPath"] = PrivateKeyPath, ["Jwt:PublicKeyPath"] = PublicKeyPath, ["Jwt:PrivateKey"] = null, ["Jwt:PublicKey"] = null, ["Cors:AllowedOrigins:0"] = "http://localhost:5173", ["Serilog:MinimumLevel:Default"] = "Warning", }); }); builder.UseEnvironment("Testing"); } public async Task InitializeAsync() { await _dbFixture.InitializeAsync(); } public new async Task DisposeAsync() { await _dbFixture.DisposeAsync(); await base.DisposeAsync(); } private static string ResolveRepoRoot() { // Walk up from AppContext.BaseDirectory looking for SIGCM2.slnx var dir = new DirectoryInfo(AppContext.BaseDirectory); while (dir is not null) { if (dir.GetFiles("SIGCM2.slnx").Length > 0) return dir.FullName; dir = dir.Parent; } // Walk up from assembly location var assemblyLocation = typeof(TestWebAppFactory).Assembly.Location; dir = new DirectoryInfo(Path.GetDirectoryName(assemblyLocation)!); while (dir is not null) { if (dir.GetFiles("SIGCM2.slnx").Length > 0) return dir.FullName; dir = dir.Parent; } // Known absolute path (last resort for this machine) const string knownPath = @"E:\SIG-CM2.0"; if (Directory.Exists(knownPath) && File.Exists(Path.Combine(knownPath, "SIGCM2.slnx"))) return knownPath; throw new InvalidOperationException( $"Could not find repo root containing SIGCM2.slnx. " + $"AppContext.BaseDirectory: {AppContext.BaseDirectory}"); } }