diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/OpcUaClientDriverWireTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/OpcUaClientDriverWireTests.cs index 054674da5..cf41ec863 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/OpcUaClientDriverWireTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/OpcUaClientDriverWireTests.cs @@ -310,19 +310,27 @@ public sealed class OpcUaClientDriverWireTests var before = Seen().Length; await drv.UnsubscribeAlarmsAsync(first, ct); await drv.SubscribeAlarmsAsync([AlarmRef], ct); - await Task.Delay(1500, ct); // let the second refresh's events land before the control transition + // No sleep: ConditionRefresh has completed server-side when SubscribeAlarmsAsync returns, so its events + // are already queued on the subscription AHEAD of anything projected below. A re-delivery would + // therefore surface before the control events and be caught by the exact-sequence assertion. // Positive control: an upstream acknowledge arrives as exactly one Acknowledge, after which nothing else. rig.Project(native: true, new AlarmConditionSnapshot(Active: true, Acknowledged: true, Confirmed: true, Enabled: true, AlarmShelvingKind.Unshelved, 800, "already hot")); await RecordedEvents.WaitUntilAsync(() => Seen().Any(e => e.Kind == AlarmTransitionKind.Acknowledge)); - await Task.Delay(500, ct); + // Trailing barrier instead of a quiet period: a further, distinguishable upstream change (new message) is + // projected after the Acknowledge and awaited. Events reach the driver in queue order, so anything stray + // that was going to follow the Acknowledge must arrive before this marker and is caught below. + rig.Project(native: true, new AlarmConditionSnapshot(Active: true, Acknowledged: true, Confirmed: true, + Enabled: true, AlarmShelvingKind.Unshelved, 800, "barrier")); + await RecordedEvents.WaitUntilAsync(() => Seen().Any(e => e.Message == "barrier")); // #728: record each event's full shape on failure, not only its Kind — the extra Unspecified that failed CI // could be a changed message/severity, a non-condition event or a quality-only annotation, and the Kind alone // cannot tell them apart. var after = Seen().Skip(before).ToArray(); - after.Select(e => e.Kind).ShouldBe([AlarmTransitionKind.Acknowledge], - "the second refresh re-delivered an unchanged state and must have emitted nothing; events after the " + + after.Select(e => e.Kind).ShouldBe([AlarmTransitionKind.Acknowledge, AlarmTransitionKind.Unspecified], + "the second refresh re-delivered an unchanged state and must have emitted nothing (only the Acknowledge " + + "and the trailing barrier's message change are expected); events after the " + "resubscribe: " + string.Join(" | ", after.Select(Describe))); }