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"]);
}
}