Files
scadaproj/ZB.MOM.WW.Secrets/tests/ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.Tests/DependencyInjection/SingleNodeClusterFixture.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

50 lines
1.7 KiB
C#

using Akka.Actor;
using Akka.Cluster;
using Akka.Configuration;
namespace ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.Tests.DependencyInjection;
/// <summary>
/// One self-joined single-node cluster shared by a test class — the miniature app node the
/// replication actor's DistributedPubSub mediator needs. Tests that spawn the actor must give each
/// container its own <c>Secrets:Replication:ActorName</c>, since actor names are unique per system.
/// </summary>
public sealed class SingleNodeClusterFixture : IAsyncLifetime
{
/// <summary>The joined, Up actor system.</summary>
public ActorSystem System { get; private set; } = null!;
/// <inheritdoc />
public async Task InitializeAsync()
{
System = ActorSystem.Create("zb-secrets-order", ConfigurationFactory.ParseString("""
akka {
loglevel = WARNING
actor.provider = cluster
remote.dot-netty.tcp {
hostname = "127.0.0.1"
public-hostname = "127.0.0.1"
port = 0
}
}
""").WithFallback(AkkaSecretsReplication.SerializationConfig));
Cluster cluster = Cluster.Get(System);
cluster.Join(cluster.SelfAddress);
DateTime deadline = DateTime.UtcNow + TimeSpan.FromSeconds(20);
while (cluster.State.Members.All(m => m.Status != MemberStatus.Up))
{
if (DateTime.UtcNow > deadline)
{
throw new TimeoutException("Single-node cluster never reached Up.");
}
await Task.Delay(100);
}
}
/// <inheritdoc />
public async Task DisposeAsync() => await System.Terminate();
}