using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using ZB.MOM.WW.Secrets.Abstractions; using ZB.MOM.WW.Secrets.DependencyInjection; using ZB.MOM.WW.Secrets.Replication; using ZB.MOM.WW.Secrets.Replicator.SqlServer.DependencyInjection; using ZB.MOM.WW.Secrets.Sqlite; namespace ZB.MOM.WW.Secrets.Replicator.SqlServer.Tests.DependencyInjection; /// /// scadaproj#30 (Secrets-001): the SQL-Server extensions must produce the same graph whether the /// application calls AddZbSecrets before or after them. /// /// /// Before the fix both modes put their registrations in with TryAdd ahead of their own /// internal AddZbSecrets call, so an application that had ALREADY called AddZbSecrets /// — the order the README teaches, and the only way to pass contentRootPath — silently kept /// the SQLite store (shared-store mode) or the no-op replicator (hub mode). Every test here builds the /// real container and resolves, in both orders. /// public sealed class RegistrationOrderTests { public enum Order { CoreFirst, ExtensionFirst, } private static IConfiguration Config() => new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["Secrets:SqlitePath"] = Path.Combine(Path.GetTempPath(), $"zb-order-{Guid.NewGuid():N}.db"), ["Secrets:RunMigrationsOnStartup"] = "false", ["Secrets:MasterKey:Source"] = "Environment", ["Secrets:MasterKey:EnvVarName"] = "ZB_SECRETS_TEST_KEY", ["Secrets:SqlServer:ConnectionString"] = "Server=unused;Database=x;", }).Build(); private static ServiceProvider Build(Order order, Action extension) { IConfiguration config = Config(); var services = new ServiceCollection(); services.AddLogging(); if (order == Order.CoreFirst) { services.AddZbSecrets(config, "Secrets"); extension(services, config); } else { extension(services, config); services.AddZbSecrets(config, "Secrets"); } return services.BuildServiceProvider(); } [Theory] [InlineData(Order.CoreFirst)] [InlineData(Order.ExtensionFirst)] public void SharedStore_mode_wins_in_either_order(Order order) { using ServiceProvider provider = Build(order, (s, c) => s.AddZbSecretsSqlServerStore(c)); Assert.IsType(provider.GetRequiredService()); Assert.IsType(provider.GetRequiredService()); // Exactly one store registration is visible, so nothing enumerating the seam can pick up the // displaced SQLite store either. Assert.IsType(Assert.Single(provider.GetServices())); } [Theory] [InlineData(Order.CoreFirst)] [InlineData(Order.ExtensionFirst)] public void HubReplication_mode_publishes_through_the_hub_in_either_order(Order order) { using ServiceProvider provider = Build(order, (s, c) => s.AddZbSecretsSqlServerReplication(c)); Assert.IsType(provider.GetRequiredService()); Assert.IsType(Assert.Single(provider.GetServices())); Assert.IsType(provider.GetRequiredService()); } }