using ZB.MOM.WW.Secrets.Abstractions;
namespace ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.Tests.Fakes;
///
/// Wraps a store so every operation genuinely yields the thread before completing.
///
///
///
/// This exists to close a blind spot that let a real bug through. A local SQLite store is fast
/// enough that await on it frequently completes synchronously, which leaves the
/// caller on the actor's own mailbox thread — and therefore leaves the actor context (a
/// [ThreadStatic]) intact. Code that illegally touches actor context after an await
/// then works by accident, and the whole suite passes.
///
///
/// Under a slower store, a contended one, or simply a differently-timed thread pool, the same
/// await goes properly asynchronous, the continuation lands on a pool thread with no actor
/// context, and the code throws. That is the worst shape of bug: green in tests, broken in
/// production, timing-dependent either way. Forcing the async path makes it deterministic.
///
///
/// The store to wrap.
public sealed class GenuinelyAsyncSecretStore(ISecretStore inner) : ISecretStore
{
private static async Task YieldRealAsync()
{
// Task.Yield alone can still be scheduled inline by some schedulers; a real (if tiny) delay
// guarantees the continuation is posted to the thread pool.
await Task.Delay(5).ConfigureAwait(false);
await Task.Yield();
}
///
public async Task GetAsync(SecretName name, CancellationToken ct)
{
await YieldRealAsync().ConfigureAwait(false);
return await inner.GetAsync(name, ct).ConfigureAwait(false);
}
///
public async Task UpsertAsync(StoredSecret row, CancellationToken ct)
{
await YieldRealAsync().ConfigureAwait(false);
await inner.UpsertAsync(row, ct).ConfigureAwait(false);
}
///
public async Task DeleteAsync(SecretName name, string? actor, CancellationToken ct)
{
await YieldRealAsync().ConfigureAwait(false);
return await inner.DeleteAsync(name, actor, ct).ConfigureAwait(false);
}
///
public async Task> ListAsync(bool includeDeleted, CancellationToken ct)
{
await YieldRealAsync().ConfigureAwait(false);
return await inner.ListAsync(includeDeleted, ct).ConfigureAwait(false);
}
///
public async Task> GetManifestAsync(CancellationToken ct)
{
await YieldRealAsync().ConfigureAwait(false);
return await inner.GetManifestAsync(ct).ConfigureAwait(false);
}
///
public async Task ApplyReplicatedAsync(StoredSecret row, CancellationToken ct)
{
await YieldRealAsync().ConfigureAwait(false);
await inner.ApplyReplicatedAsync(row, ct).ConfigureAwait(false);
}
///
public async Task ApplyRewrapAsync(
StoredSecret rewrappedRow, byte[] expectedCurrentWrappedDek, CancellationToken ct)
{
await YieldRealAsync().ConfigureAwait(false);
return await inner.ApplyRewrapAsync(rewrappedRow, expectedCurrentWrappedDek, ct)
.ConfigureAwait(false);
}
}