135 lines
4.1 KiB
C#
135 lines
4.1 KiB
C#
using System.Runtime.CompilerServices;
|
|
using ZB.MOM.WW.GalaxyRepository;
|
|
|
|
namespace ZB.MOM.WW.GalaxyRepository.Tests;
|
|
|
|
/// <summary>
|
|
/// In-memory <see cref="IGalaxyRepository"/> returning canned rowsets. Counts the heavy
|
|
/// hierarchy/attribute reads so tests can assert deploy-gated skips, and can be flipped to
|
|
/// throw so the failure path is exercisable.
|
|
/// </summary>
|
|
internal sealed class FakeGalaxyRepository : IGalaxyRepository
|
|
{
|
|
private readonly IReadOnlyList<GalaxyHierarchyRow> _hierarchy;
|
|
private readonly IReadOnlyList<GalaxyAttributeRow> _attributes;
|
|
|
|
public FakeGalaxyRepository(
|
|
IReadOnlyList<GalaxyHierarchyRow> hierarchy,
|
|
IReadOnlyList<GalaxyAttributeRow> attributes,
|
|
DateTime? deployTime)
|
|
{
|
|
_hierarchy = hierarchy;
|
|
_attributes = attributes;
|
|
DeployTime = deployTime;
|
|
}
|
|
|
|
/// <summary>The deploy time returned by <see cref="GetLastDeployTimeAsync"/>; mutate to simulate a redeploy.</summary>
|
|
public DateTime? DeployTime { get; set; }
|
|
|
|
/// <summary>When set, every query throws this exception (simulates an unreachable database).</summary>
|
|
public Exception? ThrowOnQuery { get; set; }
|
|
|
|
public int HierarchyReadCount { get; private set; }
|
|
|
|
public int AttributeReadCount { get; private set; }
|
|
|
|
public Task<bool> TestConnectionAsync(CancellationToken ct = default) =>
|
|
ThrowOnQuery is null ? Task.FromResult(true) : throw ThrowOnQuery;
|
|
|
|
public Task<DateTime?> GetLastDeployTimeAsync(CancellationToken ct = default)
|
|
{
|
|
if (ThrowOnQuery is not null)
|
|
{
|
|
throw ThrowOnQuery;
|
|
}
|
|
|
|
return Task.FromResult(DeployTime);
|
|
}
|
|
|
|
public Task<List<GalaxyHierarchyRow>> GetHierarchyAsync(CancellationToken ct = default)
|
|
{
|
|
if (ThrowOnQuery is not null)
|
|
{
|
|
throw ThrowOnQuery;
|
|
}
|
|
|
|
HierarchyReadCount++;
|
|
return Task.FromResult(_hierarchy.ToList());
|
|
}
|
|
|
|
public Task<List<GalaxyAttributeRow>> GetAttributesAsync(CancellationToken ct = default)
|
|
{
|
|
if (ThrowOnQuery is not null)
|
|
{
|
|
throw ThrowOnQuery;
|
|
}
|
|
|
|
AttributeReadCount++;
|
|
return Task.FromResult(_attributes.ToList());
|
|
}
|
|
}
|
|
|
|
/// <summary>Records published deploy events so tests can assert publication.</summary>
|
|
internal sealed class RecordingDeployNotifier : IGalaxyDeployNotifier
|
|
{
|
|
public List<GalaxyDeployEventInfo> Published { get; } = [];
|
|
|
|
public GalaxyDeployEventInfo? Latest { get; private set; }
|
|
|
|
public void Publish(GalaxyDeployEventInfo info)
|
|
{
|
|
Published.Add(info);
|
|
Latest = info;
|
|
}
|
|
|
|
public async IAsyncEnumerable<GalaxyDeployEventInfo> SubscribeAsync(
|
|
[EnumeratorCancellation] CancellationToken cancellationToken)
|
|
{
|
|
if (Latest is { } latest)
|
|
{
|
|
yield return latest;
|
|
}
|
|
|
|
await Task.CompletedTask.ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// In-memory <see cref="IGalaxyHierarchySnapshotStore"/>. Pre-seed <see cref="Snapshot"/>
|
|
/// to exercise the restore path; reads <see cref="SaveAsync"/> back to assert persistence.
|
|
/// </summary>
|
|
internal sealed class FakeSnapshotStore : IGalaxyHierarchySnapshotStore
|
|
{
|
|
public GalaxyHierarchySnapshot? Snapshot { get; set; }
|
|
|
|
public int SaveCount { get; private set; }
|
|
|
|
public int LoadCount { get; private set; }
|
|
|
|
public Task SaveAsync(GalaxyHierarchySnapshot snapshot, CancellationToken cancellationToken)
|
|
{
|
|
SaveCount++;
|
|
Snapshot = snapshot;
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task<GalaxyHierarchySnapshot?> TryLoadAsync(CancellationToken cancellationToken)
|
|
{
|
|
LoadCount++;
|
|
return Task.FromResult(Snapshot);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A <see cref="TimeProvider"/> whose UTC clock is fixed (and advanceable) so the cache's
|
|
/// staleness projection (which fires after a 5-minute threshold) is deterministic.
|
|
/// </summary>
|
|
internal sealed class StubTimeProvider(DateTimeOffset start) : TimeProvider
|
|
{
|
|
private DateTimeOffset _now = start;
|
|
|
|
public override DateTimeOffset GetUtcNow() => _now;
|
|
|
|
public void Advance(TimeSpan delta) => _now += delta;
|
|
}
|