feat(ops): pipeline-health dashboard
- Add a /health ops page: snapshot freshness (last-capture-at, colour-coded), 24h vs total snapshots + anomalies, events tracked, sports covered, and the four worker on/off states. Nav entry under System; localized en+ru. - New IPipelineHealthService + ISnapshotRepository.GetLatestCapturedAtAsync (max CapturedAt via indexed ORDER BY/LIMIT 1), with a real-SQLite round-trip test.
This commit is contained in:
@@ -366,6 +366,29 @@ public sealed class RoundTripTests : IDisposable
|
||||
many.Keys.Select(k => k.Value).Should().BeEquivalentTo(new[] { "Q1", "Q3" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Snapshot_GetLatestCapturedAt_ReturnsMostRecentOrNull()
|
||||
{
|
||||
// Empty store → null.
|
||||
(await _snapshotRepo.GetLatestCapturedAtAsync()).Should().BeNull();
|
||||
|
||||
await _eventRepo.AddAsync(BuildEvent("L100"));
|
||||
await _eventRepo.SaveChangesAsync();
|
||||
|
||||
var older = new DateTimeOffset(2026, 5, 1, 12, 0, 0, MoscowOffset);
|
||||
var newer = new DateTimeOffset(2026, 5, 9, 18, 30, 0, MoscowOffset);
|
||||
OddsSnapshot Snap(DateTimeOffset at) =>
|
||||
new(new EventId("L100"), at, OddsSource.Live,
|
||||
new List<Bet> { new(MatchScope.Instance, BetType.Win, Side.Side1, null, new OddsRate(1.90m)) });
|
||||
|
||||
await _snapshotRepo.AddAsync(Snap(older));
|
||||
await _snapshotRepo.AddAsync(Snap(newer));
|
||||
await _snapshotRepo.SaveChangesAsync();
|
||||
_fixture.DbContext.ChangeTracker.Clear();
|
||||
|
||||
(await _snapshotRepo.GetLatestCapturedAtAsync()).Should().Be(newer);
|
||||
}
|
||||
|
||||
// ── Helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
private static Event BuildEvent(string id, DateTimeOffset? scheduledAt = null) =>
|
||||
|
||||
Reference in New Issue
Block a user