test(galaxy): #872 pin the detected_by rule per source
v2-ci / unit-tests (push) Has been cancelled
v2-ci / unit-tests (push) Has been cancelled
The rule fix itself landed in 1a08b4f7 (detail-0 failure rows route on
category); its test only exercised raw_detected_by = 3. Add per-source
coverage for every MxStatusSource value mxaccessgw's MapSource emits
(-1, 0..5, and an out-of-range value) across the failure categories, plus
the live-captured shape the old rule got wrong (OperationalError detected
by the responding automation object, mxaccessgw docs/AlarmProbeFindings.md)
and a RequestingLmx CommunicationError positive control. All 10 new cases
fail against the old 'detail 0 && raw_detected_by != 0 => comms' rule.
Comment in FromMxStatus now cites MapSource and says why NMX (2/3) is not
a transport signal. Item 1 (Warning/Pending) stays open pending a capture.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -133,6 +133,9 @@ internal static class StatusCodeMap
|
||||
// the belief that a non-zero source meant a pre-MXAccess transport failure — but every detected_by value
|
||||
// names an MXAccess layer (0 requesting LMX, 1 responding LMX, 2/3 NMX, 4/5 automation object; the
|
||||
// gateway's own failures arrive as ProtocolStatus, never as a status row), so the category decides (#872).
|
||||
// The numbering is mxaccessgw's MxStatusProxyConverter.MapSource; NMX (2/3) names the inter-node message
|
||||
// layer that noticed the failure, not a transport fault — a real comms loss is category CommunicationError
|
||||
// or detail 2/5, which FromCategory already maps to BadCommunicationError.
|
||||
return FromCategory(status, logger);
|
||||
}
|
||||
|
||||
|
||||
@@ -140,18 +140,72 @@ public sealed class StatusCodeMapTests
|
||||
logger.Entries.Count.ShouldBe(1);
|
||||
}
|
||||
|
||||
// ===== #872: detected_by never decides the code — the category does =====
|
||||
// raw_detected_by is MXAccess's MxStatusSource, decoded by mxaccessgw's MxStatusProxyConverter.MapSource:
|
||||
// -1 Unknown, 0 RequestingLmx, 1 RespondingLmx, 2 RequestingNmx, 3 RespondingNmx,
|
||||
// 4 RequestingAutomationObject, 5 RespondingAutomationObject, anything else Unknown. Every value names the
|
||||
// MXAccess layer that noticed the failure, not the kind of failure — the live write-refusal capture in
|
||||
// mxaccessgw docs/AlarmProbeFindings.md is an OperationalError detected by RespondingAutomationObject (5).
|
||||
// A transport/network failure shows up as category CommunicationError (or detail 2/5), and the gateway's own
|
||||
// failures arrive as ProtocolStatus, never as a status row. The old rule (detail 0 and raw_detected_by != 0
|
||||
// => BadCommunicationError) turned every one of those object/LMX-detected failures into a comms error.
|
||||
|
||||
/// <summary>
|
||||
/// #872: detected_by names an MXAccess layer (3 = responding NMX), not a transport failure, so a detail-0
|
||||
/// failure row routes on its category — a communication-category row is still BadCommunicationError.
|
||||
/// #872: a detail-0 failure row maps on its category for every detected_by source — including the
|
||||
/// NMX sources, which name the layer that noticed the failure, not a transport failure.
|
||||
/// </summary>
|
||||
/// <param name="rawDetectedBy">The raw MxStatusSource value.</param>
|
||||
[Theory]
|
||||
[InlineData(MxStatusCategory.Unspecified, 0x80000000u)]
|
||||
[InlineData(MxStatusCategory.CommunicationError, 0x80050000u)]
|
||||
[InlineData(MxStatusCategory.ConfigurationError, 0x80890000u)]
|
||||
public void FromMxStatus_SuccessZero_DetailZero_RoutesByCategory_NotDetectedBy(MxStatusCategory category, uint expected)
|
||||
[InlineData(-1)] // Unknown
|
||||
[InlineData(0)] // RequestingLmx
|
||||
[InlineData(1)] // RespondingLmx
|
||||
[InlineData(2)] // RequestingNmx
|
||||
[InlineData(3)] // RespondingNmx
|
||||
[InlineData(4)] // RequestingAutomationObject
|
||||
[InlineData(5)] // RespondingAutomationObject
|
||||
[InlineData(77)] // outside the documented range -> Unknown
|
||||
public void FromMxStatus_DetailZero_EverySource_RoutesByCategory(int rawDetectedBy)
|
||||
{
|
||||
var s = new MxStatusProxy { Success = 0, Detail = 0, RawDetectedBy = 3, Category = category };
|
||||
StatusCodeMap.FromMxStatus(s).ShouldBe(expected);
|
||||
Map(MxStatusCategory.OperationalError).ShouldBe(StatusCodeMap.Bad);
|
||||
Map(MxStatusCategory.SoftwareError).ShouldBe(StatusCodeMap.Bad);
|
||||
Map(MxStatusCategory.OtherError).ShouldBe(StatusCodeMap.Bad);
|
||||
Map(MxStatusCategory.Unspecified).ShouldBe(StatusCodeMap.Bad);
|
||||
Map(MxStatusCategory.ConfigurationError).ShouldBe(StatusCodeMap.BadConfigurationError);
|
||||
Map(MxStatusCategory.SecurityError).ShouldBe(StatusCodeMap.BadUserAccessDenied);
|
||||
Map(MxStatusCategory.CommunicationError).ShouldBe(StatusCodeMap.BadCommunicationError);
|
||||
|
||||
uint Map(MxStatusCategory category) => StatusCodeMap.FromMxStatus(
|
||||
new MxStatusProxy { Success = 0, Detail = 0, RawDetectedBy = rawDetectedBy, Category = category });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #872: the shape the old rule got wrong — an operational failure the responding automation object
|
||||
/// detected (raw 5, the live write-refusal source) is not a communication error.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FromMxStatus_DetailZero_RespondingAutomationObject_OperationalError_IsNotCommunicationError()
|
||||
{
|
||||
var s = new MxStatusProxy
|
||||
{
|
||||
Success = 0, Detail = 0, Category = MxStatusCategory.OperationalError,
|
||||
DetectedBy = MxStatusSource.RespondingAutomationObject, RawDetectedBy = 5,
|
||||
};
|
||||
StatusCodeMap.FromMxStatus(s).ShouldBe(StatusCodeMap.Bad);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #872 positive control: a communication-category row stays BadCommunicationError when detected by
|
||||
/// the requesting LMX (raw 0) — the source the old rule treated as "never a comms error".
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FromMxStatus_DetailZero_RequestingLmx_CommunicationError_IsCommunicationError()
|
||||
{
|
||||
var s = new MxStatusProxy
|
||||
{
|
||||
Success = 0, Detail = 0, Category = MxStatusCategory.CommunicationError,
|
||||
DetectedBy = MxStatusSource.RequestingLmx, RawDetectedBy = 0,
|
||||
};
|
||||
StatusCodeMap.FromMxStatus(s).ShouldBe(StatusCodeMap.BadCommunicationError);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that all-zero status maps to Bad status code.</summary>
|
||||
|
||||
Reference in New Issue
Block a user