using System.Runtime.CompilerServices; using ZB.MOM.WW.HistorianGateway.Contracts.Grpc; namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests; /// /// Reusable in-memory test double for . Every method returns /// from a public settable result field and records its call arguments into public fields, so the /// later driver tasks (T7/T8/T11/T12/T14) can drive behaviour and assert on what the driver sent /// without a live gateway. Throw fields let a test simulate transport faults (e.g. an /// RpcException) per operation; reads share . /// public sealed class FakeHistorianGatewayClient : IHistorianGatewayClient { // ---- ReadRaw ------------------------------------------------------------------------------- public IReadOnlyList RawSamples = Array.Empty(); public string? LastReadRawTag; public DateTime LastReadRawStartUtc; public DateTime LastReadRawEndUtc; public int LastReadRawMaxValues; public int ReadRawCallCount; // ---- ReadAggregate ------------------------------------------------------------------------- public IReadOnlyList AggregateSamples = Array.Empty(); public string? LastAggregateTag; public DateTime LastAggregateStartUtc; public DateTime LastAggregateEndUtc; public RetrievalMode LastAggregateMode; public TimeSpan LastAggregateInterval; public int ReadAggregateCallCount; // ---- ReadAtTime ---------------------------------------------------------------------------- public IReadOnlyList AtTimeSamples = Array.Empty(); public string? LastReadAtTimeTag; public IReadOnlyList? LastReadAtTimeTimestamps; public int ReadAtTimeCallCount; // ---- ReadEvents ---------------------------------------------------------------------------- public IReadOnlyList Events = Array.Empty(); public string? LastReadEventsSourceName; public DateTime LastReadEventsStartUtc; public DateTime LastReadEventsEndUtc; public int LastReadEventsMaxEvents; public int ReadEventsCallCount; /// Thrown (deferred to first enumeration) by every read method when set. public Exception? ThrowOnRead; // ---- WriteLiveValues ----------------------------------------------------------------------- public WriteAck WriteLiveValuesResult = new() { Success = true }; public string? LastWriteLiveTag; public IReadOnlyList? LastWriteLiveValues; public int WriteLiveValuesCallCount; public Exception? WriteLiveValuesThrows; // ---- SendEvent ----------------------------------------------------------------------------- public WriteAck SendEventResult = new() { Success = true }; public HistorianEvent? LastSendEvent; public int SendEventCallCount; public Exception? SendEventThrows; // ---- EnsureTags ---------------------------------------------------------------------------- public TagOperationResults EnsureTagsResult = new(); public IReadOnlyList? LastEnsureDefinitions; public int EnsureTagsCallCount; public Exception? EnsureTagsThrows; // ---- Probe --------------------------------------------------------------------------------- public bool ProbeResult = true; public int ProbeCallCount; public Exception? ProbeThrows; // ---- GetConnectionStatus ------------------------------------------------------------------- public ConnectionStatus ConnectionStatus = new(); public int GetConnectionStatusCallCount; public Exception? GetConnectionStatusThrows; // ---- Dispose ------------------------------------------------------------------------------- public int DisposeCallCount; public IAsyncEnumerable ReadRawAsync( string tag, DateTime startUtc, DateTime endUtc, int maxValues, CancellationToken ct) { LastReadRawTag = tag; LastReadRawStartUtc = startUtc; LastReadRawEndUtc = endUtc; LastReadRawMaxValues = maxValues; ReadRawCallCount++; return ToAsyncStream(RawSamples, ThrowOnRead, ct); } public IAsyncEnumerable ReadAggregateAsync( string tag, DateTime startUtc, DateTime endUtc, RetrievalMode mode, TimeSpan interval, CancellationToken ct) { LastAggregateTag = tag; LastAggregateStartUtc = startUtc; LastAggregateEndUtc = endUtc; LastAggregateMode = mode; LastAggregateInterval = interval; ReadAggregateCallCount++; return ToAsyncStream(AggregateSamples, ThrowOnRead, ct); } public Task> ReadAtTimeAsync( string tag, IReadOnlyList timestampsUtc, CancellationToken ct) { LastReadAtTimeTag = tag; LastReadAtTimeTimestamps = timestampsUtc; ReadAtTimeCallCount++; return ThrowOnRead is not null ? Task.FromException>(ThrowOnRead) : Task.FromResult(AtTimeSamples); } public IAsyncEnumerable ReadEventsAsync( string? sourceName, DateTime startUtc, DateTime endUtc, int maxEvents, CancellationToken ct) { LastReadEventsSourceName = sourceName; LastReadEventsStartUtc = startUtc; LastReadEventsEndUtc = endUtc; LastReadEventsMaxEvents = maxEvents; ReadEventsCallCount++; return ToAsyncStream(Events, ThrowOnRead, ct); } public Task WriteLiveValuesAsync( string tag, IReadOnlyList values, CancellationToken ct) { LastWriteLiveTag = tag; LastWriteLiveValues = values; WriteLiveValuesCallCount++; return WriteLiveValuesThrows is not null ? Task.FromException(WriteLiveValuesThrows) : Task.FromResult(WriteLiveValuesResult); } public Task SendEventAsync(HistorianEvent evt, CancellationToken ct) { LastSendEvent = evt; SendEventCallCount++; return SendEventThrows is not null ? Task.FromException(SendEventThrows) : Task.FromResult(SendEventResult); } public Task EnsureTagsAsync( IReadOnlyList definitions, CancellationToken ct) { LastEnsureDefinitions = definitions; EnsureTagsCallCount++; return EnsureTagsThrows is not null ? Task.FromException(EnsureTagsThrows) : Task.FromResult(EnsureTagsResult); } public Task ProbeAsync(CancellationToken ct) { ProbeCallCount++; return ProbeThrows is not null ? Task.FromException(ProbeThrows) : Task.FromResult(ProbeResult); } public Task GetConnectionStatusAsync(CancellationToken ct) { GetConnectionStatusCallCount++; return GetConnectionStatusThrows is not null ? Task.FromException(GetConnectionStatusThrows) : Task.FromResult(ConnectionStatus); } public ValueTask DisposeAsync() { DisposeCallCount++; return ValueTask.CompletedTask; } private static async IAsyncEnumerable ToAsyncStream( IReadOnlyList items, Exception? error, [EnumeratorCancellation] CancellationToken ct) { if (error is not null) { throw error; } foreach (var item in items) { ct.ThrowIfCancellationRequested(); yield return item; } await ValueTask.CompletedTask.ConfigureAwait(false); } }