using System.Collections.Concurrent; using ZB.MOM.WW.Secrets.Abstractions; namespace ZB.MOM.WW.Secrets.Tests.Fakes; /// /// An in-memory test double that counts calls so /// a test can prove a resolver cache hit (or miss). Only the read path exercised by /// DefaultSecretResolver is meaningfully implemented; the mutation / replication members /// throw so an accidental dependency on them fails loudly rather than silently no-ops. /// public sealed class CountingSecretStore : ISecretStore { private readonly ConcurrentDictionary _rows = new(StringComparer.Ordinal); private int _getCount; /// The number of times has been invoked. public int GetCount => Volatile.Read(ref _getCount); /// Seeds (or overwrites) a row keyed by its normalized name. /// The encrypted row to seed. public void Seed(StoredSecret row) => _rows[row.Name.Value] = row; /// public Task GetAsync(SecretName name, CancellationToken ct) { Interlocked.Increment(ref _getCount); _rows.TryGetValue(name.Value, out StoredSecret? row); return Task.FromResult(row); } /// public Task UpsertAsync(StoredSecret row, CancellationToken ct) => throw new NotSupportedException(); /// public Task DeleteAsync(SecretName name, string? actor, CancellationToken ct) => throw new NotSupportedException(); /// public Task> ListAsync(bool includeDeleted, CancellationToken ct) => throw new NotSupportedException(); /// public Task> GetManifestAsync(CancellationToken ct) => throw new NotSupportedException(); /// public Task ApplyReplicatedAsync(StoredSecret row, CancellationToken ct) => throw new NotSupportedException(); }