fix(alarms): fetch/poll ceilings; truncation-semantics docs; log-format conformance

This commit is contained in:
Joseph Doherty
2026-08-15 17:19:41 -04:00
parent 7c9add3d73
commit b5ea6bb461
11 changed files with 254 additions and 40 deletions
@@ -177,9 +177,10 @@ public sealed class GatewayOptionsValidatorTests
}
/// <summary>
/// A poll cadence below the 100 ms floor must fail validation. Both
/// values are stamped onto every worker launch environment, so they
/// are validated whether or not the central alarm monitor is enabled.
/// A poll cadence outside the 100 ms – 1 h range must fail validation.
/// Both values are stamped onto every worker launch environment, so
/// they are validated whether or not the central alarm monitor is
/// enabled.
/// </summary>
/// <param name="pollIntervalMilliseconds">Cadence under test.</param>
/// <param name="alarmsEnabled">Whether the central alarm monitor is on.</param>
@@ -188,7 +189,12 @@ public sealed class GatewayOptionsValidatorTests
[InlineData(0, false)]
[InlineData(-1, false)]
[InlineData(99, true)]
public void Validate_Fails_WhenAlarmPollIntervalBelowFloor(
// Above the one-hour ceiling the cadence stops being a cadence: int.MaxValue
// milliseconds is ~24 days, which silently disables alarm polling.
[InlineData(3_600_001, false)]
[InlineData(int.MaxValue, false)]
[InlineData(int.MaxValue, true)]
public void Validate_Fails_WhenAlarmPollIntervalOutOfRange(
int pollIntervalMilliseconds,
bool alarmsEnabled)
{
@@ -210,9 +216,12 @@ public sealed class GatewayOptionsValidatorTests
}
/// <summary>
/// A per-fetch cap below the 64-record floor must fail validation. The
/// cap doubles as the truncation-detection threshold in the worker, so
/// a tiny cap would make almost every fetch read as truncated.
/// A per-fetch cap outside the 64 – 65,536 range must fail validation.
/// The cap doubles as the truncation-detection threshold in the worker,
/// so a tiny cap would make almost every fetch read as truncated; and
/// the worker is a 32-bit process that materializes each reply as one
/// BSTR plus a full XmlDocument, so an unbounded cap is an
/// out-of-memory fault on the STA rather than a slow poll.
/// </summary>
/// <param name="maxAlarmsPerFetch">Cap under test.</param>
/// <param name="alarmsEnabled">Whether the central alarm monitor is on.</param>
@@ -221,7 +230,10 @@ public sealed class GatewayOptionsValidatorTests
[InlineData(0, false)]
[InlineData(-1, false)]
[InlineData(63, true)]
public void Validate_Fails_WhenMaxAlarmsPerFetchBelowFloor(
[InlineData(65_537, false)]
[InlineData(int.MaxValue, false)]
[InlineData(int.MaxValue, true)]
public void Validate_Fails_WhenMaxAlarmsPerFetchOutOfRange(
int maxAlarmsPerFetch,
bool alarmsEnabled)
{
@@ -242,9 +254,15 @@ public sealed class GatewayOptionsValidatorTests
f => f.Contains("MxGateway:Alarms:MaxAlarmsPerFetch", StringComparison.Ordinal));
}
/// <summary>Verifies the floor values themselves are accepted.</summary>
[Fact]
public void Validate_Succeeds_AtAlarmPollCadenceAndFetchCapFloors()
/// <summary>Verifies the boundary values themselves are accepted at both ends.</summary>
/// <param name="pollIntervalMilliseconds">Cadence under test.</param>
/// <param name="maxAlarmsPerFetch">Cap under test.</param>
[Theory]
[InlineData(100, 64)] // floors
[InlineData(3_600_000, 65_536)] // ceilings
public void Validate_Succeeds_AtAlarmPollCadenceAndFetchCapBoundaries(
int pollIntervalMilliseconds,
int maxAlarmsPerFetch)
{
GatewayOptions options = CloneWithAlarms(
ValidOptions(),
@@ -252,8 +270,8 @@ public sealed class GatewayOptionsValidatorTests
{
Enabled = true,
DefaultArea = "Galaxy",
PollIntervalMilliseconds = 100,
MaxAlarmsPerFetch = 64,
PollIntervalMilliseconds = pollIntervalMilliseconds,
MaxAlarmsPerFetch = maxAlarmsPerFetch,
});
ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, options);