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;
using ZB.MOM.WW.Secrets.Sqlite;
namespace ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.Tests.DependencyInjection;
///
/// Registration tests for AddZbSecretsAkkaReplication.
///
/// These exist because 0.2.0 shipped with Akka replication silently inert and every other test in
/// this assembly still passed. The bug was pure registration ORDER: the extension called
/// AddZbSecrets first, which does
/// TryAddSingleton<ISecretReplicator, NoOpSecretReplicator>(), so the package's own
/// TryAddSingleton<ISecretReplicator> found a descriptor already present and was
/// silently discarded. Writes then published into a no-op sink and no replication actor was ever
/// spawned — with no exception and no log line anywhere.
///
///
/// The equivalent SQL-Server package always had a test asserting its replicator type, and always
/// had the correct order. That asymmetry is exactly why the defect landed in one package and not
/// the other, so the gap is closed here.
///
///
/// These assert against the because the 0.2.0 defect was entirely
/// about which descriptor wins, and the descriptor is where it is visible. Resolution through a
/// BUILT provider — where the scadaproj#1 singleton-cycle deadlock lived — is covered by
/// , and end-to-end actor behaviour by
/// TwoNodeClusterReplicationTests. (An earlier revision of this comment claimed a
/// provider-based test was impossible because DistributedPubSub.Get needs a joined
/// cluster; the hang it was avoiding was actually that DI deadlock, and a single self-joined
/// node is all the cluster the actor needs.)
///
///
public sealed class AddZbSecretsAkkaReplicationTests
{
private static IServiceCollection Register()
{
IConfigurationRoot config = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary
{
["Secrets:SqlitePath"] = Path.Combine(Path.GetTempPath(), $"zb-akkadi-{Guid.NewGuid():N}.db"),
["Secrets:Replication:AnnounceInterval"] = "00:00:30",
})
.Build();
var services = new ServiceCollection();
services.AddLogging();
services.AddZbSecretsAkkaReplication(config, "Secrets", "Secrets:Replication");
return services;
}
private static ServiceDescriptor Descriptor(IServiceCollection services) =>
services.Single(d => d.ServiceType == typeof(T));
[Fact]
public void ISecretReplicator_is_not_bound_to_the_no_op_sink()
{
// THE REGRESSION TEST. Before the fix this descriptor was NoOpSecretReplicator, so every
// replicated write went nowhere. A no-op secret replicator is the worst failure mode
// available: the cluster reports healthy and silently never converges.
ServiceDescriptor descriptor = Descriptor(Register());
Assert.NotEqual(typeof(NoOpSecretReplicator), descriptor.ImplementationType);
}
[Fact]
public void ISecretReplicator_is_registered_by_this_package_via_a_factory()
{
// The package registers through a factory (it needs the lazily-created actor ref), whereas
// the core no-op is registered by concrete type. A factory registration is therefore proof
// that this package's descriptor won the TryAdd race rather than core's.
ServiceDescriptor descriptor = Descriptor(Register());
Assert.NotNull(descriptor.ImplementationFactory);
Assert.Null(descriptor.ImplementationType);
}
[Fact]
public void ISecretStore_is_decorated_so_writes_actually_publish()
{
// AddSingleton (not TryAdd) means the decorator is appended, and the LAST registration for
// a service type is what the container resolves. If this regressed to the bare SQLite
// store, writes would never publish.
ServiceDescriptor last = Register()
.Last(d => d.ServiceType == typeof(ISecretStore));
Assert.NotNull(last.ImplementationFactory);
}
[Fact]
public void The_undecorated_concrete_store_is_registered()
{
// ReplicatingSecretStore resolves SqliteSecretStore by concrete type. Core registering it
// only behind ISecretStore was the OTHER defect this library shipped, so pin it here too.
IServiceCollection services = Register();
Assert.Contains(services, d => d.ServiceType == typeof(SqliteSecretStore));
}
[Fact]
public void The_store_migrator_seam_is_registered()
{
IServiceCollection services = Register();
Assert.Contains(services, d => d.ServiceType == typeof(ISecretsStoreMigrator));
}
}