using ZB.MOM.WW.MxGateway.Server.Diagnostics; namespace ZB.MOM.WW.MxGateway.Tests.Diagnostics; public sealed class GatewayLogRedactorSeamTests { /// Redacting a log property containing a bearer API key masks the secret while preserving the key id prefix. [Fact] public void Redact_MasksApiKeyInClientIdentity() { GatewayLogRedactorSeam redactor = new(); Dictionary props = new() { ["ClientIdentity"] = "Bearer mxgw_operator01_super-secret" }; redactor.Redact(props); Assert.Equal("Bearer mxgw_operator01_[redacted]", props["ClientIdentity"]); } /// A command value carried by a credential-bearing command never reaches a sink in the clear. [Fact] public void Redact_MasksCommandValueForCredentialBearingCommand() { GatewayLogRedactorSeam redactor = new(); Dictionary props = new() { ["CommandMethod"] = "AuthenticateUser", ["CommandValue"] = "operator01:hunter2" }; redactor.Redact(props); Assert.Equal(GatewayLogRedactor.RedactedValue, props["CommandValue"]); } /// /// Value logging is off by default, so an ordinary command value is masked too — the seam /// exposes no opt-in, matching 's default. /// [Fact] public void Redact_MasksCommandValueForOrdinaryCommand() { GatewayLogRedactorSeam redactor = new(); Dictionary props = new() { ["CommandMethod"] = "Read", ["CommandValue"] = "plaintext-tag-value" }; redactor.Redact(props); Assert.Equal(GatewayLogRedactor.RedactedValue, props["CommandValue"]); } /// A command value with no accompanying method is still masked — an unknown method cannot be cleared. [Fact] public void Redact_MasksCommandValueWhenCommandMethodAbsent() { GatewayLogRedactorSeam redactor = new(); Dictionary props = new() { ["CommandValue"] = "plaintext-tag-value" }; redactor.Redact(props); Assert.Equal(GatewayLogRedactor.RedactedValue, props["CommandValue"]); } /// A null command value stays null rather than becoming the redaction placeholder. [Fact] public void Redact_LeavesNullCommandValueNull() { GatewayLogRedactorSeam redactor = new(); Dictionary props = new() { ["CommandValue"] = null }; redactor.Redact(props); Assert.Null(props["CommandValue"]); } /// An event carrying no command value is untouched — the seam must not invent the property. [Fact] public void Redact_DoesNotAddCommandValueWhenAbsent() { GatewayLogRedactorSeam redactor = new(); Dictionary props = new() { ["ClientIdentity"] = "anonymous" }; redactor.Redact(props); Assert.False(props.ContainsKey("CommandValue")); } /// Any property whose name contains "password" is masked regardless of casing. [Fact] public void Redact_PasswordNamedProperty_IsRedacted() { GatewayLogRedactorSeam redactor = new(); Dictionary props = new() { ["OperatorPassword"] = "pw-secret", ["verify_user_PASSWORD"] = "pw-secret", ["OperatorUser"] = "alice", }; redactor.Redact(props); Assert.Equal("[redacted]", props["OperatorPassword"]); Assert.Equal("[redacted]", props["verify_user_PASSWORD"]); Assert.Equal("alice", props["OperatorUser"]); } }