diff --git a/src/XtremeIdiots.Portal.Server.Events.Processor.App.Tests/Startup/RepositoryApiClientRegistrationTests.cs b/src/XtremeIdiots.Portal.Server.Events.Processor.App.Tests/Startup/RepositoryApiClientRegistrationTests.cs new file mode 100644 index 0000000..9a12e6e --- /dev/null +++ b/src/XtremeIdiots.Portal.Server.Events.Processor.App.Tests/Startup/RepositoryApiClientRegistrationTests.cs @@ -0,0 +1,88 @@ +using Microsoft.Extensions.DependencyInjection; + +using XtremeIdiots.Portal.Repository.Abstractions.Interfaces.V1; +using XtremeIdiots.Portal.Repository.Api.Client.V1; + +namespace XtremeIdiots.Portal.Server.Events.Processor.App.Tests.Startup; + +/// +/// Startup-time DI resolution tests for the Repository API client registration used by the +/// Processor App. These tests replicate the production registration in +/// Program.cs and prove the container can build and resolve every typed sub-client +/// the app depends on (e.g. ). They exercise real DI +/// resolution rather than inspecting option flags so that any regression in the +/// services.AddRepositoryApiClient(...) composition — such as the +/// WithCaching(c => c.UseLibraryDefaults()) policy expression rejection that +/// crashed portal-sync / portal-repository-func on Repository 4.2.21 — is caught here +/// before it reaches production. +/// +public class RepositoryApiClientRegistrationTests +{ + private const string BaseUrl = "https://repository-api.test.local"; + private const string Audience = "api://repository-api-test"; + + [Fact] + public void ProductionRegistration_BuildsAndResolvesRepositoryClient() + { + var provider = BuildProductionServiceProvider(); + using var scope = provider.CreateScope(); + + var repositoryClient = scope.ServiceProvider.GetRequiredService(); + + Assert.NotNull(repositoryClient); + } + + [Fact] + public void ProductionRegistration_ResolvesAdminActionsSubClient() + { + var provider = BuildProductionServiceProvider(); + using var scope = provider.CreateScope(); + + var repositoryClient = scope.ServiceProvider.GetRequiredService(); + IAdminActionsApi adminActions = repositoryClient.AdminActions.V1; + + Assert.NotNull(adminActions); + Assert.NotNull(scope.ServiceProvider.GetRequiredService()); + } + + [Fact] + public void ProductionRegistration_ResolvesRepresentativeSubClients() + { + var provider = BuildProductionServiceProvider(); + using var scope = provider.CreateScope(); + + var repositoryClient = scope.ServiceProvider.GetRequiredService(); + + // Every sub-client the processor actually calls at runtime must be resolvable. + // Touching .V1 on each versioned facade forces the DI graph for the underlying + // typed client to be materialised — which is where the client-side caching + // policy composition failure surfaces on start-up. + Assert.NotNull(repositoryClient.AdminActions.V1); + Assert.NotNull(repositoryClient.Players.V1); + Assert.NotNull(repositoryClient.GameServers.V1); + Assert.NotNull(repositoryClient.GameServersEvents.V1); + Assert.NotNull(repositoryClient.GameServersStats.V1); + Assert.NotNull(repositoryClient.ChatMessages.V1); + Assert.NotNull(repositoryClient.Maps.V1); + Assert.NotNull(repositoryClient.ConnectedPlayers.V1); + Assert.NotNull(repositoryClient.RecentPlayers.V1); + Assert.NotNull(repositoryClient.GlobalConfigurations.V1); + Assert.NotNull(repositoryClient.GameServerConfigurations.V1); + Assert.NotNull(repositoryClient.LiveStatus.V1); + } + + private static ServiceProvider BuildProductionServiceProvider() + { + var services = new ServiceCollection(); + + // Mirror the Repository API client registration in + // src/XtremeIdiots.Portal.Server.Events.Processor.App/Program.cs. + // Any deviation here (in particular re-introducing consumer-side WithCaching) + // must also be applied to Program.cs. + services.AddRepositoryApiClient(options => options + .WithBaseUrl(BaseUrl) + .WithEntraIdAuthentication(Audience)); + + return services.BuildServiceProvider(validateScopes: true); + } +} diff --git a/src/XtremeIdiots.Portal.Server.Events.Processor.App/Program.cs b/src/XtremeIdiots.Portal.Server.Events.Processor.App/Program.cs index 4bf84b6..aa243e6 100644 --- a/src/XtremeIdiots.Portal.Server.Events.Processor.App/Program.cs +++ b/src/XtremeIdiots.Portal.Server.Events.Processor.App/Program.cs @@ -92,8 +92,7 @@ services.AddRepositoryApiClient(options => options .WithBaseUrl(configuration["RepositoryApi:BaseUrl"] ?? throw new InvalidOperationException("RepositoryApi:BaseUrl is required")) - .WithEntraIdAuthentication(configuration["RepositoryApi:ApplicationAudience"] ?? throw new InvalidOperationException("RepositoryApi:ApplicationAudience is required")) - .WithCaching(c => c.UseLibraryDefaults())); + .WithEntraIdAuthentication(configuration["RepositoryApi:ApplicationAudience"] ?? throw new InvalidOperationException("RepositoryApi:ApplicationAudience is required"))); services.AddServersApiClient(options => options .WithBaseUrl(configuration["ServersIntegrationApi:BaseUrl"] ?? throw new InvalidOperationException("ServersIntegrationApi:BaseUrl is required"))