c4f97d0e87
Extend CommunicationOptionsValidator with eager bounds for the four T4 live-alarm-cache options (linger >= 0, reconcile > 0, seed concurrency 1..64, subscribers-per-site >= 1). Enforce the per-site viewer cap fail-safe in SiteAlarmLiveCacheService.Subscribe (reject excess viewers with a no-op disposable rather than growing the list or throwing into the Blazor render path). Surface two telemetry instruments on the existing ScadaBridgeTelemetry meter: an active-aggregator observable gauge and a reconnect counter, wired from the aggregator actor's PreStart/PostStop and its NodeA<->NodeB flip / reconcile-driven reopen. Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
95 lines
3.1 KiB
C#
95 lines
3.1 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.SiteConnectionUpGauge_*</c>; reads are
|
|
/// relative to a baseline so the process-wide static instruments are robust to any
|
|
/// parallel test interleaving.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|