merge: observability (register rows 33, 54, 56, 63, 66) — LocalDb backlog KPI history + sustained-threshold badge, honest stream-subscriber gauge rename + real data-connection gauges, central metrics port on the rig, ASPNETCORE_ENVIRONMENT on site containers, import-session rejection counter

This commit is contained in:
Joseph Doherty
2026-08-15 17:56:14 -04:00
38 changed files with 1417 additions and 52 deletions
@@ -346,6 +346,103 @@ public class HealthPageTests : BunitContext
});
}
// ------------------------------------------------------------------
// Sustained LocalDb replication backlog badge (register row 33)
// ------------------------------------------------------------------
[Fact]
public void Renders_ReplicationBacklogBadge_WhenBacklogSustainedPastTheDwell()
{
SeedStates(new SiteHealthState
{
SiteId = "site-a",
IsOnline = true,
LastHeartbeatAt = DateTimeOffset.UtcNow,
// Default dwell is 15 minutes; 30 minutes above threshold is well past it.
LocalDbBacklogAboveSince = DateTimeOffset.UtcNow.AddMinutes(-30),
});
var cut = RenderHealthPage();
cut.WaitForAssertion(() =>
{
Assert.Contains("data-test=\"localdb-backlog-badge\"", cut.Markup);
Assert.Contains("Replication backlog", cut.Markup);
});
}
[Fact]
public void DoesNotRender_ReplicationBacklogBadge_DuringAPurgeBurst()
{
// THE false-positive this badge is shaped to avoid. The daily site_events purge is
// CDC-captured, so the backlog spikes and then drains. Two minutes above the
// threshold is a purge burst, not a fault, and must stay silent — otherwise the
// badge fires on every replicated site every day and gets ignored.
SeedStates(new SiteHealthState
{
SiteId = "site-a",
IsOnline = true,
LastHeartbeatAt = DateTimeOffset.UtcNow,
LocalDbBacklogAboveSince = DateTimeOffset.UtcNow.AddMinutes(-2),
});
var cut = RenderHealthPage();
cut.WaitForAssertion(() => Assert.Contains("site-a", cut.Markup));
Assert.DoesNotContain("data-test=\"localdb-backlog-badge\"", cut.Markup);
}
[Fact]
public void DoesNotRender_ReplicationBacklogBadge_ForAnUnreplicatedSite()
{
// Rig site-b/site-c report a null backlog forever, so the aggregator never gives them
// an anchor. A deliberately-unreplicated site must never be badged.
SeedStates(new SiteHealthState
{
SiteId = "site-b",
IsOnline = true,
LastHeartbeatAt = DateTimeOffset.UtcNow,
LocalDbBacklogAboveSince = null,
});
var cut = RenderHealthPage();
cut.WaitForAssertion(() => Assert.Contains("site-b", cut.Markup));
Assert.DoesNotContain("data-test=\"localdb-backlog-badge\"", cut.Markup);
}
[Fact]
public void DoesNotRender_ReplicationBacklogBadge_ForAnOfflineSite()
{
// An offline site's last-known anchor is stale by definition; the Offline badge is the
// signal that matters, and stacking a backlog warning on top is noise.
SeedStates(new SiteHealthState
{
SiteId = "site-a",
IsOnline = false,
LastHeartbeatAt = DateTimeOffset.UtcNow.AddMinutes(-30),
LocalDbBacklogAboveSince = DateTimeOffset.UtcNow.AddMinutes(-30),
});
var cut = RenderHealthPage();
cut.WaitForAssertion(() => Assert.Contains("site-a", cut.Markup));
Assert.DoesNotContain("data-test=\"localdb-backlog-badge\"", cut.Markup);
}
[Fact]
public void Renders_LocalDbBacklogTrendChart()
{
// The retrospective half of row 33: a point-in-time tile can never answer "did that
// spike drain?", which is the only question that distinguishes healthy from faulty.
SeedSites("site-a");
var cut = RenderHealthPage();
cut.WaitForAssertion(() =>
Assert.Contains("LocalDb Replication Backlog", cut.Markup));
}
[Fact]
public void Renders_OfflineSince_ForOfflineSiteWithStatusChange()
{