using ScadaLink.Commons.Types.Enums; namespace ScadaLink.HealthMonitoring.Tests; public class SiteHealthCollectorTests { private readonly SiteHealthCollector _collector = new(); [Fact] public void CollectReport_ReturnsZeroCounters_WhenNoErrorsRecorded() { var report = _collector.CollectReport("site-1"); Assert.Equal("site-1", report.SiteId); Assert.Equal(0, report.ScriptErrorCount); Assert.Equal(0, report.AlarmEvaluationErrorCount); Assert.Equal(0, report.DeadLetterCount); } [Fact] public void IncrementScriptError_AccumulatesBetweenReports() { _collector.IncrementScriptError(); _collector.IncrementScriptError(); _collector.IncrementScriptError(); var report = _collector.CollectReport("site-1"); Assert.Equal(3, report.ScriptErrorCount); } [Fact] public void IncrementAlarmError_AccumulatesBetweenReports() { _collector.IncrementAlarmError(); _collector.IncrementAlarmError(); var report = _collector.CollectReport("site-1"); Assert.Equal(2, report.AlarmEvaluationErrorCount); } [Fact] public void IncrementDeadLetter_AccumulatesBetweenReports() { _collector.IncrementDeadLetter(); var report = _collector.CollectReport("site-1"); Assert.Equal(1, report.DeadLetterCount); } [Fact] public void CollectReport_ResetsCounters_AfterCollection() { _collector.IncrementScriptError(); _collector.IncrementAlarmError(); _collector.IncrementDeadLetter(); var first = _collector.CollectReport("site-1"); Assert.Equal(1, first.ScriptErrorCount); Assert.Equal(1, first.AlarmEvaluationErrorCount); Assert.Equal(1, first.DeadLetterCount); var second = _collector.CollectReport("site-1"); Assert.Equal(0, second.ScriptErrorCount); Assert.Equal(0, second.AlarmEvaluationErrorCount); Assert.Equal(0, second.DeadLetterCount); } [Fact] public void UpdateConnectionHealth_ReflectedInReport() { _collector.UpdateConnectionHealth("opc-1", ConnectionHealth.Connected); _collector.UpdateConnectionHealth("opc-2", ConnectionHealth.Disconnected); var report = _collector.CollectReport("site-1"); Assert.Equal(2, report.DataConnectionStatuses.Count); Assert.Equal(ConnectionHealth.Connected, report.DataConnectionStatuses["opc-1"]); Assert.Equal(ConnectionHealth.Disconnected, report.DataConnectionStatuses["opc-2"]); } [Fact] public void ConnectionHealth_NotResetAfterCollect() { _collector.UpdateConnectionHealth("opc-1", ConnectionHealth.Connected); _collector.CollectReport("site-1"); var second = _collector.CollectReport("site-1"); Assert.Single(second.DataConnectionStatuses); Assert.Equal(ConnectionHealth.Connected, second.DataConnectionStatuses["opc-1"]); } [Fact] public void RemoveConnection_RemovesFromReport() { _collector.UpdateConnectionHealth("opc-1", ConnectionHealth.Connected); _collector.UpdateTagResolution("opc-1", 10, 8); _collector.RemoveConnection("opc-1"); var report = _collector.CollectReport("site-1"); Assert.Empty(report.DataConnectionStatuses); Assert.Empty(report.TagResolutionCounts); } [Fact] public void UpdateTagResolution_ReflectedInReport() { _collector.UpdateTagResolution("opc-1", 50, 45); var report = _collector.CollectReport("site-1"); Assert.Single(report.TagResolutionCounts); Assert.Equal(50, report.TagResolutionCounts["opc-1"].TotalSubscribed); Assert.Equal(45, report.TagResolutionCounts["opc-1"].SuccessfullyResolved); } [Fact] public void StoreAndForwardBufferDepths_IsEmptyPlaceholder() { var report = _collector.CollectReport("site-1"); Assert.Empty(report.StoreAndForwardBufferDepths); } [Fact] public void CollectReport_IncludesUtcTimestamp() { var before = DateTimeOffset.UtcNow; var report = _collector.CollectReport("site-1"); var after = DateTimeOffset.UtcNow; Assert.InRange(report.ReportTimestamp, before, after); } [Fact] public void CollectReport_SequenceNumberIsZero_CallerAssignsIt() { var report = _collector.CollectReport("site-1"); Assert.Equal(0, report.SequenceNumber); } [Fact] public async Task ThreadSafety_ConcurrentIncrements() { const int iterations = 10_000; var tasks = new[] { Task.Run(() => { for (int i = 0; i < iterations; i++) _collector.IncrementScriptError(); }), Task.Run(() => { for (int i = 0; i < iterations; i++) _collector.IncrementAlarmError(); }), Task.Run(() => { for (int i = 0; i < iterations; i++) _collector.IncrementDeadLetter(); }) }; await Task.WhenAll(tasks); var report = _collector.CollectReport("site-1"); Assert.Equal(iterations, report.ScriptErrorCount); Assert.Equal(iterations, report.AlarmEvaluationErrorCount); Assert.Equal(iterations, report.DeadLetterCount); } }