using System.Collections.Concurrent; using ZB.MOM.WW.Secrets.Abstractions; namespace ZB.MOM.WW.Secrets.Ui.Tests.Fakes; /// /// A hand-written test double (the family does not use Moq) that keeps /// an in-memory table of rows and records every and /// call so a test can assert on the mutation trail. /// public sealed class RecordingSecretStore : ISecretStore { private readonly ConcurrentDictionary _rows = new(); private readonly ConcurrentQueue _upserts = new(); private readonly ConcurrentQueue<(SecretName Name, string? Actor)> _deletes = new(); /// Rows upserted, in call order. public IReadOnlyList Upserts => _upserts.ToArray(); /// Delete calls (name + recorded actor), in call order. public IReadOnlyList<(SecretName Name, string? Actor)> Deletes => _deletes.ToArray(); /// When set, throws this to drive the mutation-failure path. public Exception? UpsertFault { get; set; } /// When set, throws this to drive the mutation-failure path. public Exception? DeleteFault { get; set; } /// Seeds a row so and can return it. /// The row to seed. public void Seed(StoredSecret row) => _rows[row.Name.Value] = row; /// public Task GetAsync(SecretName name, CancellationToken ct) => Task.FromResult(_rows.TryGetValue(name.Value, out StoredSecret? row) ? row : null); /// public Task UpsertAsync(StoredSecret row, CancellationToken ct) { if (UpsertFault is not null) { throw UpsertFault; } _rows[row.Name.Value] = row; _upserts.Enqueue(row); return Task.CompletedTask; } /// public Task DeleteAsync(SecretName name, string? actor, CancellationToken ct) { if (DeleteFault is not null) { throw DeleteFault; } _deletes.Enqueue((name, actor)); return Task.FromResult(_rows.TryRemove(name.Value, out _)); } /// public Task> ListAsync(bool includeDeleted, CancellationToken ct) { IReadOnlyList projection = _rows.Values .Where(r => includeDeleted || !r.IsDeleted) .Select(r => new SecretMetadata { Name = r.Name, Description = r.Description, ContentType = r.ContentType, KekId = r.KekId, Revision = r.Revision, IsDeleted = r.IsDeleted, CreatedUtc = r.CreatedUtc, UpdatedUtc = r.UpdatedUtc, CreatedBy = r.CreatedBy, UpdatedBy = r.UpdatedBy, }) .ToArray(); return Task.FromResult(projection); } /// public Task> GetManifestAsync(CancellationToken ct) => throw new NotSupportedException(); /// public Task ApplyReplicatedAsync(StoredSecret row, CancellationToken ct) => throw new NotSupportedException(); }