Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "node --import tsx --test src/demo/trace-health-check.test.ts src/lib/config.test.ts src/dashboard/fixture-health-check.test.ts src/lib/round-status.test.ts src/components/dashboard/RoundStatusCard.test.tsx",
"test": "node --import tsx --test src/demo/trace-health-check.test.ts src/lib/config.test.ts src/dashboard/fixture-health-check.test.ts src/lib/round-status.test.ts src/components/dashboard/RoundStatusCard.test.tsx src/hooks/useDashboardData.test.ts",
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"dependencies": {
Expand Down
55 changes: 55 additions & 0 deletions apps/web/src/hooks/useDashboardData.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import assert from "node:assert/strict";
import { test } from "node:test";

import { isStale } from "./useDashboardData";

// Mirrors STALE_THRESHOLD_MS in useDashboardData.ts (5 minutes). Kept as a
// local constant rather than imported so these tests pin the *contract*
// (fresh below the threshold, stale above it) without depending on that
// value being exported.
const STALE_THRESHOLD_MS = 5 * 60 * 1000;
const NOW_MS = Date.parse("2026-06-15T12:00:00.000Z");

test("invalid or missing timestamps are treated as stale", () => {
assert.equal(isStale("not-a-date", NOW_MS), true, "unparseable string");
assert.equal(isStale("", NOW_MS), true, "empty string");
assert.equal(isStale("abcdef", NOW_MS), true, "garbage string");
assert.equal(isStale("2026-13-45T00:00:00Z", NOW_MS), true, "invalid calendar date");
assert.equal(isStale(null, NOW_MS), true, "null");
assert.equal(isStale(undefined, NOW_MS), true, "undefined");
});

test("valid fresh timestamps are not stale", () => {
const fetchedAt = new Date(NOW_MS - 30_000).toISOString(); // 30s ago
assert.equal(isStale(fetchedAt, NOW_MS), false);
});

test("valid old timestamps beyond the threshold are stale", () => {
const fetchedAt = new Date(NOW_MS - STALE_THRESHOLD_MS - 60_000).toISOString(); // 6 min ago
assert.equal(isStale(fetchedAt, NOW_MS), true);
});

test("a timestamp equal to now is fresh", () => {
const fetchedAt = new Date(NOW_MS).toISOString();
assert.equal(isStale(fetchedAt, NOW_MS), false);
});

test("a future timestamp is fresh (unchanged pre-existing behavior)", () => {
const fetchedAt = new Date(NOW_MS + 60_000).toISOString(); // 1 min in the future
assert.equal(isStale(fetchedAt, NOW_MS), false);
});

test("boundary: exactly at the stale threshold is still fresh (strict greater-than)", () => {
const fetchedAt = new Date(NOW_MS - STALE_THRESHOLD_MS).toISOString();
assert.equal(isStale(fetchedAt, NOW_MS), false);
});

test("boundary: one millisecond past the stale threshold is stale", () => {
const fetchedAt = new Date(NOW_MS - STALE_THRESHOLD_MS - 1).toISOString();
assert.equal(isStale(fetchedAt, NOW_MS), true);
});

test("boundary: one millisecond inside the stale threshold is fresh", () => {
const fetchedAt = new Date(NOW_MS - STALE_THRESHOLD_MS + 1).toISOString();
assert.equal(isStale(fetchedAt, NOW_MS), false);
});
21 changes: 20 additions & 1 deletion apps/web/src/hooks/useDashboardData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,27 @@ export interface UseDashboardDataResult {
refetch: () => void;
}

function isStale(fetchedAt: string, nowMs: number): boolean {
/**
* Determine whether dashboard data fetched at `fetchedAt` should be treated
* as stale relative to `nowMs`.
*
* A missing or unparseable `fetchedAt` is treated as stale rather than
* fresh: `Date.parse()` returns `NaN` for invalid input, and every
* comparison against `NaN` (including `>`) evaluates to `false` in
* JavaScript -- so without an explicit check, malformed timestamp data
* would silently be reported as fresh instead of triggering the staleness
* warning it's meant to guard against.
*/
export function isStale(fetchedAt: string | null | undefined, nowMs: number): boolean {
if (!fetchedAt) {
return true;
}

const fetchedTime = Date.parse(fetchedAt);
if (!Number.isFinite(fetchedTime)) {
return true;
}

return nowMs - fetchedTime > STALE_THRESHOLD_MS;
}

Expand Down