Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.Communication.Tests/SiteAlarmLiveCacheTelemetryTests.cs
T

107 lines
3.8 KiB
C#

using System.Diagnostics.Metrics;
using ZB.MOM.WW.ScadaBridge.Commons.Observability;
namespace ZB.MOM.WW.ScadaBridge.Communication.Tests;
/// <summary>
/// Telemetry guards for the aggregated live alarm cache (plan #10, Task 6): the
/// active-aggregator observable gauge reflects Started/Stopped, and the reconnect
/// counter increments on each recorded reconnect. Mirrors the MeterListener read
/// pattern used by <c>SiteStreamGrpcServerTests.SiteStreamSubscribersActiveGauge_*</c>; reads are
/// relative to a baseline so the process-wide static instruments are robust to any
/// parallel test interleaving.
/// </summary>
/// <remarks>
/// Register row 102's shape: a fixed-baseline delta only survives contributors that hold
/// constant or increase — a parallel class's actors moving the SAME process-global gauge
/// and then STOPPING after this test captures its baseline makes <c>baseline + N</c>
/// unreachable forever. <see cref="SiteAlarmAggregatorActor"/> calls
/// <see cref="ScadaBridgeTelemetry.LiveAlarmAggregatorStarted"/>/
/// <see cref="ScadaBridgeTelemetry.LiveAlarmAggregatorStopped"/> from its own
/// PreStart/PostStop, so every actor <c>Grpc.SiteAlarmAggregatorActorTests</c> spins up
/// moves this exact gauge — serialized into the same collection (row-96 pattern) so the
/// two classes never interleave.
/// </remarks>
[Collection("SiteAlarmAggregatorGauge")]
public class SiteAlarmLiveCacheTelemetryTests
{
private static long ReadGauge(string instrumentName)
{
long observed = 0;
using var listener = new MeterListener();
listener.InstrumentPublished = (instrument, l) =>
{
if (instrument.Meter.Name == ScadaBridgeTelemetry.MeterName &&
instrument.Name == instrumentName)
{
l.EnableMeasurementEvents(instrument);
}
};
listener.SetMeasurementEventCallback<long>((_, measurement, _, _) => observed = measurement);
listener.Start();
listener.RecordObservableInstruments();
return observed;
}
private static long ReadCounter(string instrumentName, Action act)
{
long delta = 0;
using var listener = new MeterListener();
listener.InstrumentPublished = (instrument, l) =>
{
if (instrument.Meter.Name == ScadaBridgeTelemetry.MeterName &&
instrument.Name == instrumentName)
{
l.EnableMeasurementEvents(instrument);
}
};
listener.SetMeasurementEventCallback<long>((_, measurement, _, _) => delta += measurement);
listener.Start();
act();
return delta;
}
[Fact]
public void ActiveAggregatorGauge_ReflectsStartAndStop()
{
const string gauge = "scadabridge.site.alarm_cache.aggregators.active";
var baseline = ReadGauge(gauge);
ScadaBridgeTelemetry.LiveAlarmAggregatorStarted();
try
{
Assert.Equal(baseline + 1, ReadGauge(gauge));
ScadaBridgeTelemetry.LiveAlarmAggregatorStarted();
try
{
Assert.Equal(baseline + 2, ReadGauge(gauge));
}
finally
{
ScadaBridgeTelemetry.LiveAlarmAggregatorStopped();
}
Assert.Equal(baseline + 1, ReadGauge(gauge));
}
finally
{
ScadaBridgeTelemetry.LiveAlarmAggregatorStopped();
}
Assert.Equal(baseline, ReadGauge(gauge));
}
[Fact]
public void ReconnectCounter_IncrementsOnEachRecordedReconnect()
{
var delta = ReadCounter("scadabridge.site.alarm_cache.reconnects", () =>
{
ScadaBridgeTelemetry.RecordLiveAlarmStreamReconnect();
ScadaBridgeTelemetry.RecordLiveAlarmStreamReconnect();
});
Assert.Equal(2, delta);
}
}