fix(feed): reflect content updates in source health confirmation and freshness

Ensure lastConfirmedAt never precedes contentChangedAt for fetched snapshots and calculate game-level lastConfirmedAt using effective pull times.
This commit is contained in:
Lucas Winther
2026-09-18 09:30:31 +02:00
parent d0f30c11b7
commit 157b9fe20c
4 changed files with 64 additions and 6 deletions
+10 -2
View File
@@ -47,12 +47,20 @@ async function latestFixture(adapterId: string, game: GameId) {
async function documentFor(adapterId: string, game: GameId) {
const cached = await snapshots.read(adapterId);
if (cached !== null) {
const rawConfirmed = cached.state.lastConfirmedAt;
const contentChangedAt = cached.meta.contentChangedAt;
const lastConfirmedAt =
rawConfirmed !== null &&
contentChangedAt !== null &&
Date.parse(rawConfirmed) >= Date.parse(contentChangedAt)
? rawConfirmed
: (contentChangedAt ?? rawConfirmed);
return {
file: snapshots.bodyPath(adapterId),
html: cached.html,
at: freshnessAt(cached),
lastConfirmedAt: cached.state.lastConfirmedAt,
contentChangedAt: cached.meta.contentChangedAt,
lastConfirmedAt,
contentChangedAt,
};
}
const { file, html } = await latestFixture(adapterId, game);
+6 -1
View File
@@ -86,7 +86,12 @@ export function sourceHealth(
// When the bytes were last confirmed live; a fixture's capture date when
// this source has never been refreshed.
lastSuccessAt: at,
lastConfirmedAt: timestamps?.lastConfirmedAt ?? null,
lastConfirmedAt:
timestamps?.lastConfirmedAt != null &&
(timestamps.contentChangedAt ?? at) != null &&
Date.parse(timestamps.lastConfirmedAt) < Date.parse(timestamps.contentChangedAt ?? at!)
? (timestamps.contentChangedAt ?? at)
: (timestamps?.lastConfirmedAt ?? null),
contentChangedAt: timestamps?.contentChangedAt ?? at,
eventCount,
parsedCount,
+13 -3
View File
@@ -167,11 +167,21 @@ export function freshness(
let lastConfirmedAt: string | null = null;
let hasConfirmed = false;
for (const s of sourcesInfo) {
const pullAt =
s.lastConfirmedAt !== null &&
s.contentChangedAt !== null &&
Date.parse(s.lastConfirmedAt) >= Date.parse(s.contentChangedAt)
? s.lastConfirmedAt
: (s.lastConfirmedAt ??
(s.contentChangedAt && now - Date.parse(s.contentChangedAt) <= STALE_AFTER_MS
? s.contentChangedAt
: null));
if (!hasConfirmed) {
lastConfirmedAt = s.lastConfirmedAt;
lastConfirmedAt = pullAt;
hasConfirmed = true;
} else if (lastConfirmedAt !== null && (s.lastConfirmedAt === null || s.lastConfirmedAt < lastConfirmedAt)) {
lastConfirmedAt = s.lastConfirmedAt;
} else if (lastConfirmedAt !== null && (pullAt === null || pullAt < lastConfirmedAt)) {
lastConfirmedAt = pullAt;
}
}
+35
View File
@@ -202,6 +202,31 @@ describe("freshness", () => {
expect(staleEndfield.sources[0]?.contentChangedAt).toBe(OLD_SITE);
});
test("differentiates blame across multiple sources when one updated recently and another confirmed 304", () => {
const PULL_TIME = new Date(NOW - 11 * HOUR).toISOString();
const OLD_SITE = new Date(NOW - 60 * HOUR).toISOString();
const RECENT_CONTENT = new Date(NOW - 1 * HOUR).toISOString();
const result = freshness(
[
{
...source("endfield", OLD_SITE, "endfield-wikigg-events"),
lastConfirmedAt: PULL_TIME,
contentChangedAt: OLD_SITE,
},
{
...source("endfield", RECENT_CONTENT, "endfield-game8-events"),
lastConfirmedAt: null,
contentChangedAt: RECENT_CONTENT,
},
],
NOW,
);
expect(result.stale).toHaveLength(1);
const staleEndfield = result.stale[0]!;
expect(staleEndfield.lastConfirmedAt).toBe(PULL_TIME);
expect(staleEndfield.contentChangedAt).toBe(OLD_SITE);
});
test("an empty feed reports nothing rather than throwing", () => {
expect(freshness([], NOW)).toEqual({ refreshedAt: null, stale: [] });
});
@@ -394,4 +419,14 @@ describe("recording a source's health at build time", () => {
expect(health.statesNoEvents).toBe(false);
expect(brokenSources([health])).toEqual([]);
});
test("lastConfirmedAt is never older than contentChangedAt", () => {
const OLD_CONFIRM = "2026-09-12T00:00:00.000Z";
const NEW_CONTENT = "2026-09-18T00:00:00.000Z";
const health = sourceHealth(adapter(), FULL, NEW_CONTENT, 1, {
lastConfirmedAt: OLD_CONFIRM,
contentChangedAt: NEW_CONTENT,
});
expect(health.lastConfirmedAt).toBe(NEW_CONTENT);
});
});