0b4a1682c8
- StreamAlarms: slow subscribers are completed with AlarmFeedSubscriberOverflowException and surface as ResourceExhausted (like a StreamEvents overflow) instead of warning-with-stack + Unavailable. - GatewayAlarmMonitor.StreamAsync reuses the memoized alarm-set projection instead of cloning the whole set under the broadcast lock per attach; the prefix filter runs outside the lock. - StreamEvents/StreamAlarms pass the call token to response writes; a write failure after client cancel maps to Cancelled without a warning. - Every MxCommandReply.correlation_id now echoes the request's client_correlation_id (previously only constraint-denied replies did; normal replies leaked the internal envelope id).
68 lines
2.3 KiB
C#
68 lines
2.3 KiB
C#
using System.Runtime.CompilerServices;
|
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
|
using ZB.MOM.WW.MxGateway.Server.Alarms;
|
|
|
|
namespace ZB.MOM.WW.MxGateway.Tests.TestSupport;
|
|
|
|
/// <summary>
|
|
/// <see cref="IGatewayAlarmService"/> test double — serves a scripted
|
|
/// active-alarm set and acknowledges every request with an OK status,
|
|
/// so gRPC service tests can exercise the alarm handlers without the
|
|
/// real gateway alarm monitor or a worker.
|
|
/// </summary>
|
|
public sealed class FakeGatewayAlarmService : IGatewayAlarmService
|
|
{
|
|
/// <inheritdoc />
|
|
public GatewayAlarmMonitorState State { get; set; } = GatewayAlarmMonitorState.Monitoring;
|
|
|
|
/// <inheritdoc />
|
|
public string? LastError { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public int? WorkerProcessId { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public IReadOnlyList<ActiveAlarmSnapshot> CurrentAlarms { get; set; } = [];
|
|
|
|
/// <inheritdoc />
|
|
public bool SnapshotTruncated { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public async IAsyncEnumerable<AlarmFeedMessage> StreamAsync(
|
|
string? alarmFilterPrefix,
|
|
[EnumeratorCancellation] CancellationToken cancellationToken)
|
|
{
|
|
foreach (ActiveAlarmSnapshot alarm in CurrentAlarms)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
yield return new AlarmFeedMessage { ActiveAlarm = alarm };
|
|
}
|
|
|
|
yield return new AlarmFeedMessage { SnapshotComplete = true };
|
|
await Task.CompletedTask.ConfigureAwait(false);
|
|
if (StreamFault is not null)
|
|
{
|
|
throw StreamFault;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets an exception the feed throws after its snapshot, standing in for the monitor
|
|
/// completing a subscriber with a fault (for example a slow-subscriber overflow).
|
|
/// </summary>
|
|
public Exception? StreamFault { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public Task<AcknowledgeAlarmReply> AcknowledgeAsync(
|
|
AcknowledgeAlarmRequest request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Task.FromResult(new AcknowledgeAlarmReply
|
|
{
|
|
CorrelationId = request.ClientCorrelationId,
|
|
ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.Ok },
|
|
DiagnosticMessage = string.Empty,
|
|
});
|
|
}
|
|
}
|