Files
scadaproj/ZB.MOM.WW.Secrets/tests/ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.Tests/DependencyInjection/RegistrationOrderTests.cs
T
Joseph Doherty 015c556ced fix(secrets): SqlServer/Akka registration is order-independent; bump 0.6.6 (scadaproj#30)
AddZbSecretsSqlServerStore / AddZbSecretsSqlServerReplication (and the Akka
extension, same defect class) TryAdd-ed their ISecretStore, ISecretsStoreMigrator
and ISecretReplicator ahead of their internal AddZbSecrets call. An application
that had already called AddZbSecrets therefore silently kept the SQLite store
(shared-store mode) or NoOpSecretReplicator (hub/Akka modes).

They now call AddZbSecrets first and RemoveAll + AddSingleton their own
registrations; a later AddZbSecrets only TryAdds, so either order yields the
same graph with exactly one ISecretStore registration.

- tests: RegistrationOrderTests (SqlServer + Akka) build and resolve the real
  container in both orders; core-first fails without the fix
- Directory.Build.props 0.6.5 -> 0.6.6 (NOT packed or published)
- code-reviews: Secrets-001 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:00:05 -05:00

62 lines
2.5 KiB
C#

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.AkkaDotNet.DependencyInjection;
namespace ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.Tests.DependencyInjection;
/// <summary>
/// scadaproj#30 (Secrets-001), Akka half: the package must bind its own replicator and decorator
/// whether the application calls <c>AddZbSecrets</c> before or after it. Until 0.6.6 the package
/// TryAdd-ed its replicator ahead of its internal <c>AddZbSecrets</c>, which fixed the 0.2.0 order
/// bug only for the package's own call — an application that called <c>AddZbSecrets</c> FIRST still
/// got <c>NoOpSecretReplicator</c>. Builds the real container against a joined cluster and resolves.
/// </summary>
public sealed class RegistrationOrderTests(SingleNodeClusterFixture cluster)
: IClassFixture<SingleNodeClusterFixture>
{
public enum Order
{
CoreFirst,
ExtensionFirst,
}
[Theory]
[InlineData(Order.CoreFirst)]
[InlineData(Order.ExtensionFirst)]
public void Replication_is_bound_in_either_order(Order order)
{
string dbPath = Path.Combine(Path.GetTempPath(), $"zb-akka-order-{Guid.NewGuid():N}.db");
IConfigurationRoot config = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["Secrets:SqlitePath"] = dbPath,
["Secrets:RunMigrationsOnStartup"] = "false",
["Secrets:Replication:AnnounceInterval"] = "00:00:30",
["Secrets:Replication:ActorName"] = $"zb-secret-replication-{order}",
})
.Build();
var services = new ServiceCollection();
services.AddLogging();
services.AddSingleton(cluster.System);
if (order == Order.CoreFirst)
{
services.AddZbSecrets(config, "Secrets");
services.AddZbSecretsAkkaReplication(config, "Secrets", "Secrets:Replication");
}
else
{
services.AddZbSecretsAkkaReplication(config, "Secrets", "Secrets:Replication");
services.AddZbSecrets(config, "Secrets");
}
using ServiceProvider provider = services.BuildServiceProvider();
Assert.IsType<AkkaSecretReplicator>(provider.GetRequiredService<ISecretReplicator>());
Assert.IsType<ReplicatingSecretStore>(Assert.Single(provider.GetServices<ISecretStore>()));
}
}