Resolve Contracts-016..017

Contracts-016 (Conventions): QueryActiveAlarmsRequest.session_id header
replaced with the unambiguous "Clients may leave session_id empty; the
gateway currently ignores it and serves the session-less central-monitor
cache. A future version may use it to scope the snapshot to one
session." Removes the ambiguity that the prior "reserved for future
use" wording introduced.

Contracts-017 (Documentation): The rpc QueryActiveAlarms comment now
includes the alarm_filter_prefix description: "QueryActiveAlarmsRequest.alarm_filter_prefix
optionally narrows the snapshot to alarms whose alarm_full_reference
starts with the given prefix; an empty prefix returns the full set."

Both are proto-comment-only changes — no wire-format impact, no field
renumbering, and the regenerated MxaccessGateway.cs / MxaccessGatewayGrpc.cs
carry only the doc-comment delta. Added the additive-only regression
guard QueryActiveAlarmsRequest_PinsFieldNumbersAndRoundTripsPrefixFilter
to ProtobufContractRoundTripTests — pins
session_id=1 / client_correlation_id=2 / alarm_filter_prefix=3 by
descriptor lookup and round-trips the message with and without the
filter populated.

Verification: dotnet build src/ZB.MOM.WW.MxGateway.slnx clean;
ProtobufContractRoundTripTests 40/40 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-24 03:20:13 -04:00
parent 327e9c5f94
commit bd1d1f1c0e
5 changed files with 65 additions and 11 deletions
@@ -437,6 +437,46 @@ public sealed class ProtobufContractRoundTripTests
Assert.Equal(withFilter, StreamAlarmsRequest.Parser.ParseFrom(withFilter.ToByteArray()));
}
/// <summary>
/// Verifies that <c>QueryActiveAlarmsRequest</c> pins the additive-only field numbering
/// (<c>session_id = 1</c>, <c>client_correlation_id = 2</c>, <c>alarm_filter_prefix = 3</c>)
/// advertised in its proto comment, that the message round-trips with the optional
/// <c>alarm_filter_prefix</c> populated (the filter semantic the public RPC comment
/// documents), and that <c>QueryActiveAlarms</c> remains on the public service surface.
/// </summary>
[Fact]
public void QueryActiveAlarmsRequest_PinsFieldNumbersAndRoundTripsPrefixFilter()
{
var service = Assert.Single(
MxaccessGatewayReflection.Descriptor.Services,
descriptor => descriptor.Name == "MxAccessGateway");
Assert.Contains(service.Methods, method => method.Name == "QueryActiveAlarms");
var fields = QueryActiveAlarmsRequest.Descriptor.Fields;
Assert.Equal(1, fields[QueryActiveAlarmsRequest.SessionIdFieldNumber].FieldNumber);
Assert.Equal(2, fields[QueryActiveAlarmsRequest.ClientCorrelationIdFieldNumber].FieldNumber);
Assert.Equal(3, fields[QueryActiveAlarmsRequest.AlarmFilterPrefixFieldNumber].FieldNumber);
Assert.Equal("session_id", fields[QueryActiveAlarmsRequest.SessionIdFieldNumber].Name);
Assert.Equal("client_correlation_id", fields[QueryActiveAlarmsRequest.ClientCorrelationIdFieldNumber].Name);
Assert.Equal("alarm_filter_prefix", fields[QueryActiveAlarmsRequest.AlarmFilterPrefixFieldNumber].Name);
var withoutFilter = new QueryActiveAlarmsRequest
{
ClientCorrelationId = "client-correlation-10",
};
var withFilter = new QueryActiveAlarmsRequest
{
ClientCorrelationId = "client-correlation-11",
AlarmFilterPrefix = "Tank01.",
};
Assert.Equal(withoutFilter, QueryActiveAlarmsRequest.Parser.ParseFrom(withoutFilter.ToByteArray()));
var parsedWithFilter = QueryActiveAlarmsRequest.Parser.ParseFrom(withFilter.ToByteArray());
Assert.Equal(withFilter, parsedWithFilter);
Assert.Equal("Tank01.", parsedWithFilter.AlarmFilterPrefix);
}
/// <summary>Verifies that an MxValue carrying a raw_value bytes payload round-trips.</summary>
[Fact]
public void MxValue_RoundTripsRawValueBytesPayload()