using ZB.MOM.WW.ScadaBridge.LoadHarness; namespace ZB.MOM.WW.ScadaBridge.PerformanceTests.TargetScale; /// /// Keeps the target-scale load harness (deferred-work register #25 / Phase-8 WP-4) /// honest at CI scale. /// /// /// The full protocol — 10 sites x 500 instances x 75 tags sustained for 20 minutes — /// deliberately lives in the standalone ZB.MOM.WW.ScadaBridge.LoadHarness /// executable, NOT here: perf tests in this project run as part of an ordinary /// dotnet test ZB.MOM.WW.ScadaBridge.slnx (the Category=Performance /// trait enables a filter, it does not exclude by default), and a 20-minute test /// would be intolerable there. What this test protects is that the harness still /// compiles, wires up, and produces coherent measurements — so #25's evidence can be /// regenerated on demand rather than bit-rotting. /// /// /// Results doc: docs/plans/2026-08-15-target-scale-load-test-results.md. /// Design memo: docs/plans/2026-08-15-target-scale-load-test-design.md. /// /// public class TargetScaleHarnessSmokeTests { /// /// Runs the harness at ~1/1000th of target scale for a few seconds and asserts the /// pipeline is intact end to end: tag updates reach live stream subscribers, the /// central health aggregator tracks every site, debug snapshots answer, the /// store-and-forward buffer drains to empty, and a stalled subscriber does not cost /// the healthy ones any events. /// /// A task representing the test run. [Trait("Category", "Performance")] [Fact] public async Task Harness_AtSmokeScale_ProducesCoherentMeasurements() { var config = new HarnessConfig { Sites = 2, InstancesPerSite = 10, TagsPerInstance = 5, TagUpdatePeriod = TimeSpan.FromSeconds(1), SettleDuration = TimeSpan.FromSeconds(5), SustainDuration = TimeSpan.FromSeconds(15), SampleInterval = TimeSpan.FromSeconds(2), // Shortened from the production 30 s only because the smoke window is 20 s. HealthReportInterval = TimeSpan.FromSeconds(2), DebugProbeInterval = TimeSpan.FromSeconds(2), SubscribeSettleDuration = TimeSpan.FromSeconds(5), StreamProbesPerSite = 3, StoreAndForwardDrainMessages = 200, SlowSubscriberEvents = 2_000, ResultsPath = Path.Combine(Path.GetTempPath(), $"loadharness-smoke-{Guid.NewGuid():N}.json"), }; using var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(10)); var result = await HarnessRun.ExecuteAsync(config, _ => { }, cancellation.Token); // Tag updates flowed all the way through DCL -> InstanceActor -> site stream -> // StreamRelayActor -> bounded channel -> subscriber. Assert.True(result.TagUpdateLatency.Count > 0, "No tag update latency samples — the DCL -> stream -> subscriber path did not carry traffic."); Assert.True(result.StreamProbeReceived > 0, "Live stream subscribers received nothing."); Assert.Equal(0, result.DriverSkippedNoCallback); // Observability held up. Assert.Equal(config.Sites, result.SitesTrackedByAggregator); Assert.True(result.DebugSnapshotsCompleted > 0, "No debug snapshot completed."); Assert.Equal(0, result.DebugSnapshotTimeouts); // Store-and-forward drained completely (register row 50, first half). Assert.NotNull(result.StoreAndForwardDrain); Assert.Equal(0, result.StoreAndForwardDrain!.ResidualDepth); Assert.True(result.StoreAndForwardDrain.DrainPerSecond > 0); // A stalled subscriber costs the healthy ones nothing (register row 50, second // half). This is the design's isolation claim, asserted rather than assumed. Assert.NotNull(result.SlowSubscriber); Assert.Equal(1.0, result.SlowSubscriber!.HealthyMinDeliveryRatio, precision: 2); Assert.True(result.SlowSubscriber.SlowDeliveryRatio < 1.0, "The deliberately stalled subscriber kept up, so the probe proved nothing."); } }