diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Contracts/ClientBehaviorFixtureTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Contracts/ClientBehaviorFixtureTests.cs index 282f327..b8a6855 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Contracts/ClientBehaviorFixtureTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Contracts/ClientBehaviorFixtureTests.cs @@ -72,18 +72,33 @@ public sealed class ClientBehaviorFixtureTests foreach (JsonElement fixture in fixtures) { + string fixtureId = GetFixtureId(fixture); MxCommandReply reply = ParseFixture( fixture, MxCommandReply.Parser); + // Universal invariants: every command-reply fixture parses to a concrete + // command kind and a concrete protocol status, regardless of what MXAccess + // reply detail (if any) it carries. Assert.NotEqual(MxCommandKind.Unspecified, reply.Kind); Assert.NotEqual(ProtocolStatusCode.Unspecified, reply.ProtocolStatus.Code); - Assert.True(reply.HasHresult, $"Fixture '{GetFixtureId(fixture)}' must carry an HRESULT."); + + // The malformed-reply and credential-redaction fixtures + // (command-reply.authenticate-user.*) deliberately omit hresult, statuses, + // and/or return_value to exercise the "absent detail" contract paths — see + // docs/ClientBehaviorFixtures.md. The strict MXAccess-reply-detail + // invariants below apply only to fixtures that carry that detail. + if (fixtureId.StartsWith("command-reply.authenticate-user.", StringComparison.Ordinal)) + { + continue; + } + + Assert.True(reply.HasHresult, $"Fixture '{fixtureId}' must carry an HRESULT."); Assert.NotEmpty(reply.Statuses); Assert.NotEqual(MxDataType.Unspecified, reply.ReturnValue.DataType); Assert.True( reply.ReturnValue.KindCase != MxValue.KindOneofCase.None || reply.ReturnValue.IsNull, - $"Fixture '{GetFixtureId(fixture)}' must carry a typed value, raw value, or explicit null."); + $"Fixture '{fixtureId}' must carry a typed value, raw value, or explicit null."); } MxCommandReply failedWrite = ParseFixture(