feat(gateway): forward operator credentials on alarm acknowledgement

Claude-Session: https://claude.ai/code/session_016J6isfcgqy4SB2RK3ZvFvT
This commit is contained in:
Joseph Doherty
2026-10-19 19:40:23 -04:00
parent d6db0ff40d
commit 2858e42e80
13 changed files with 192 additions and 5 deletions
@@ -164,6 +164,45 @@ public sealed class GatewayOptionsValidatorTests
Tls = source.Tls,
};
/// <summary>Verifies AckClientName defaults to "MxGateway" and passes validation.</summary>
[Fact]
public void Validate_Succeeds_WithDefaultAckClientName()
{
Assert.Equal("MxGateway", new AlarmsOptions().AckClientName);
ValidateOptionsResult result = new GatewayOptionsValidator()
.Validate(null, CloneWithAlarms(ValidOptions(), new AlarmsOptions()));
Assert.True(result.Succeeded);
}
/// <summary>Verifies an empty, over-long, or control-character AckClientName fails validation.</summary>
/// <param name="name">Client name under test.</param>
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData("bad\nname")]
public void Validate_Fails_WhenAckClientNameInvalid(string name)
{
ValidateOptionsResult result = new GatewayOptionsValidator()
.Validate(null, CloneWithAlarms(ValidOptions(), new AlarmsOptions { AckClientName = name }));
Assert.False(result.Succeeded);
Assert.Contains(
result.Failures!,
f => f.Contains("MxGateway:Alarms:AckClientName", StringComparison.Ordinal));
}
/// <summary>Verifies a 65-character AckClientName fails and a 64-character one passes.</summary>
[Fact]
public void Validate_AckClientNameLengthBoundary()
{
GatewayOptionsValidator validator = new();
Assert.False(validator.Validate(
null, CloneWithAlarms(ValidOptions(), new AlarmsOptions { AckClientName = new string('a', 65) })).Succeeded);
Assert.True(validator.Validate(
null, CloneWithAlarms(ValidOptions(), new AlarmsOptions { AckClientName = new string('a', 64) })).Succeeded);
}
/// <summary>Verifies the alarm poll cadence and per-fetch cap defaults pass validation.</summary>
[Fact]
public void Validate_Succeeds_WithDefaultAlarmPollCadenceAndFetchCap()