Merge pull request 'test(live): assert FU-1 alarm SendEvent->ReadEvents round-trip (gateway C4 fixed)' (#425) from fix/fu1-alarm-source-roundtrip into master
v2-ci / build (push) Failing after 40s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped

This commit was merged in pull request #425.
This commit is contained in:
2026-06-27 16:51:55 -04:00
@@ -186,35 +186,30 @@ public sealed class GatewayLiveIntegrationTests(GatewayLiveFixture fixture) : IC
HistorianWriteOutcome.Ack,
"the alarm SendEvent must be acked — needs the gateway write scope (historian:write) and SendEvent path.");
// Read the event back over a recent window for the source. The SQL event write can lag, so poll.
// Read the event back over a recent window for the source. The SQL event write can lag (the
// live event view has variable flush latency), so poll generously.
await using var dataSource = _fx.CreateDataSource();
IReadOnlyList<HistoricalEvent> events = Array.Empty<HistoricalEvent>();
var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(15);
var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(60);
do
{
var read = await dataSource.ReadEventsAsync(
source, eventUtc - TimeSpan.FromMinutes(5), DateTime.UtcNow + TimeSpan.FromMinutes(1), maxEvents: 0, ct);
events = read.Events;
if (events.Count > 0) break;
await Task.Delay(TimeSpan.FromSeconds(1), ct);
await Task.Delay(TimeSpan.FromSeconds(2), ct);
}
while (DateTime.UtcNow < deadline);
// The SendEvent itself is the OtOpcUa contract and is asserted above. The source-filtered
// READBACK of a just-sent event is a historian/gateway property, not an OtOpcUa one: verified
// live, the gateway's SQL event reader works and source-filtering works for events that carry a
// Source_Object (real Galaxy-sourced events read back by source correctly), but an ad-hoc
// SendEvent is recorded in dbo.Events WITHOUT a Source_Object — so a source-filtered read of a
// freshly-sent event finds nothing. Reading existing Galaxy alarm/event history by source works;
// round-tripping OtOpcUa's OWN sends by source needs the gateway's SendEvent to populate the
// event source. So when no event comes back, skip with that reason rather than failing.
if (events.Count == 0)
{
Assert.Skip(
$"Send acked, but a source-filtered ReadEvents returned 0 for source '{source}'. The SQL event " +
"reader works (Galaxy-sourced events read back by source); an ad-hoc SendEvent is stored without " +
"a Source_Object, so its source-filtered readback is empty until the gateway populates the event source.");
}
// The source-filtered READBACK of OtOpcUa's own just-sent alarm now round-trips: the gateway
// threads the wire event's SourceName into the `source_object` event property, so an ad-hoc
// SendEvent is recorded in dbo.Events WITH a Source_Object and a source-filtered ReadEvents
// finds it (gateway C4 fix, 2026-06-27; pending.md C4 / histsdk #3). Previously this branch
// skipped because ad-hoc sends landed with Source_Object=NULL; that limitation is now closed,
// so the round-trip is asserted.
events.ShouldNotBeEmpty(
$"a source-filtered ReadEvents must return the just-sent alarm for source '{source}': the gateway now " +
"populates Source_Object from the event SourceName (C4 fix), so OtOpcUa's own alarm sends round-trip.");
var exactMatch = events.Any(e => string.Equals(e.EventId, alarmId, StringComparison.Ordinal));
TestContext.Current.SendDiagnosticMessage(