using System; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using ReC.Application.Common.Dto; using ReC.Client; namespace ReC.Tests.Client; public abstract class RecClientTestBase : IDisposable { private readonly WebApplicationFactory _factory; private readonly ServiceProvider _serviceProvider; protected RecClientTestBase() { var apiContentRoot = LocateApiContentRoot(); _factory = new WebApplicationFactory() .WithWebHostBuilder(builder => { builder.UseEnvironment("Development"); builder.UseContentRoot(apiContentRoot); }); _ = _factory.CreateClient(); var services = new ServiceCollection(); services.AddLogging(); services.AddSingleton(_factory.Services.GetRequiredService()); services.AddRecClient(client => { client.BaseAddress = new Uri("http://localhost"); }); services.AddHttpClient(ReCClient.ClientName) .ConfigurePrimaryHttpMessageHandler(() => _factory.Server.CreateHandler()); _serviceProvider = services.BuildServiceProvider(); } protected IServiceProvider ServiceProvider => _serviceProvider; protected IConfiguration Configuration => _factory.Services.GetRequiredService(); protected (ReCClient Client, IServiceScope Scope) CreateScopedClient() { var scope = _serviceProvider.CreateScope(); var client = scope.ServiceProvider.GetRequiredService(); return (client, scope); } /// /// Resolves a usable profile id for tests that require an existing profile in the database. /// Prefers the configured FakeProfileId value; otherwise asks the server for the first /// available profile via the standard GET api/Profile endpoint. Returns null /// when no profile is configured and none can be discovered. /// protected async Task TryResolveProfileIdAsync() { var configured = Configuration.GetValue("FakeProfileId"); if (configured is > 0) return configured; try { var (client, scope) = CreateScopedClient(); using var _ = scope; var profiles = await client.Profiles.GetAsync(); return profiles?.FirstOrDefault()?.Id; } catch { return null; } } public void Dispose() { _serviceProvider.Dispose(); _factory.Dispose(); } private static string LocateApiContentRoot() { var current = new DirectoryInfo(AppContext.BaseDirectory); while (current is not null) { var candidate = Path.Combine(current.FullName, "src", "ReC.API"); if (File.Exists(Path.Combine(candidate, "appsettings.json"))) return candidate; current = current.Parent; } throw new DirectoryNotFoundException("Could not locate src/ReC.API content root from the test base directory."); } }