using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Shogi.BackEnd.Identity; using System.Net.Http.Json; using System.Text.Json; namespace Shogi.AcceptanceTests.TestSetup; /// /// Integration test fixture using WebApplicationFactory for in-process testing. /// public class AatTestFixture : WebApplicationFactory, IAsyncLifetime { protected static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web); private const string TestPassword = "TestPassword123!"; public HttpClient HttpClient { get; private set; } = null!; public HttpClient OtherHttpClient { get; private set; } = null!; protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.UseEnvironment("Development"); builder.ConfigureTestServices(services => { // Remove all EF Core and database provider related services var descriptorsToRemove = services .Where(d => d.ServiceType.Namespace != null && (d.ServiceType.Namespace.StartsWith("Microsoft.EntityFrameworkCore") || d.ServiceType == typeof(ApplicationDbContext) || d.ServiceType == typeof(DbContextOptions))) .ToList(); foreach (var descriptor in descriptorsToRemove) { services.Remove(descriptor); } // Add DbContext with InMemory database services.AddDbContext(options => options.UseInMemoryDatabase("IntegrationTestDb_" + Guid.NewGuid().ToString())); }); } public async Task InitializeAsync() { this.HttpClient = this.CreateClient(); this.OtherHttpClient = this.CreateClient(); // Ensure the in-memory database is created using (var scope = this.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); await db.Database.EnsureCreatedAsync(); } await this.SetupTestAccountsAndLogin(); } private async Task SetupTestAccountsAndLogin() { // Register and login first test account await RegisterAndLogin(this.HttpClient, "aat-account@test.com", TestPassword); // Register and login second test account await RegisterAndLogin(this.OtherHttpClient, "aat-account-2@test.com", TestPassword); } private static async Task RegisterAndLogin(HttpClient client, string email, string password) { // Try to register (may already exist) await client.PostAsJsonAsync( new Uri("register", UriKind.Relative), new { email, password }, options: SerializerOptions); // Login var loginResponse = await client.PostAsJsonAsync( new Uri("login", UriKind.Relative), new { email, password }, options: SerializerOptions); if (loginResponse.IsSuccessStatusCode) { var tokenResponse = await loginResponse.Content.ReadFromJsonAsync(SerializerOptions); if (tokenResponse?.AccessToken != null) { client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokenResponse.AccessToken); } } } async Task IAsyncLifetime.DisposeAsync() { this.HttpClient?.Dispose(); this.OtherHttpClient?.Dispose(); await base.DisposeAsync(); } private class LoginResponse { public string AccessToken { get; set; } = string.Empty; } }