/*
Unit tests for freshnesspbt — freshness.pbt covering calculateAgeDays — property-based (AC-7); isAssessmentStale; isItemStale.
- Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only.
- RFC-1311: Compass header sweep — declares the freshness.pbt suite's contract for test-file scaffold coverage.
*/
import { describe, expect, it } from "vitest";
import fc from "fast-check";
import {
calculateAgeDays,
isAssessmentStale,
isItemStale,
DEFAULT_FRESHNESS_WINDOW_DAYS,
} from "../freshness.ts";
const validTimestamp = fc.integer({ min: 0, max: Date.now() });
const validDateFromTs = (ts: number) => new Date(ts);
describe("calculateAgeDays — property-based (AC-7)", () => {
it("is pure and deterministic — same input always produces same output", () => {
fc.assert(
fc.property(validTimestamp, validTimestamp, (observedTs, referenceTs) => {
const observed = validDateFromTs(observedTs);
const reference = validDateFromTs(referenceTs);
const a = calculateAgeDays(observed.toISOString(), reference);
const b = calculateAgeDays(observed.toISOString(), reference);
expect(a).toBe(b);
}),
{ numRuns: 50 },
);
});
it("returns non-negative age when reference >= observed", () => {
fc.assert(
fc.property(
validTimestamp,
fc.integer({ min: 0, max: 365 * 10 }),
(observedTs, daysAfter) => {
const observed = validDateFromTs(observedTs);
const reference = new Date(observed.getTime() + daysAfter * 86400000);
const age = calculateAgeDays(observed.toISOString(), reference);
expect(age).toBeGreaterThanOrEqual(0);
},
),
{ numRuns: 50 },
);
});
it("returns negative age when reference < observed", () => {
fc.assert(
fc.property(
validTimestamp.filter((ts) => ts > 365 * 10 * 86400000),
fc.integer({ min: 1, max: 365 * 10 }),
(observedTs, daysBefore) => {
const observed = validDateFromTs(observedTs);
const reference = new Date(observed.getTime() - daysBefore * 86400000);
const age = calculateAgeDays(observed.toISOString(), reference);
expect(age).toBeLessThan(0);
},
),
{ numRuns: 50 },
);
});
it("returns Number.MAX_SAFE_INTEGER for invalid date strings", () => {
fc.assert(
fc.property(
fc.string().filter((s) => isNaN(new Date(s).getTime())),
(invalid) => {
expect(calculateAgeDays(invalid)).toBe(Number.MAX_SAFE_INTEGER);
},
),
{ numRuns: 50 },
);
});
});
describe("isAssessmentStale", () => {
it("returns true when age exceeds maxAgeDays", () => {
const reference = new Date("2026-09-12T00:00:00Z");
const assessment = {
observedAt: "2026-06-01T00:00:00Z",
freshness: { maxAgeDays: 90 },
};
expect(isAssessmentStale(assessment, reference)).toBe(true);
});
it("returns false when age is within maxAgeDays", () => {
const reference = new Date("2026-09-12T00:00:00Z");
const assessment = {
observedAt: "2026-08-01T00:00:00Z",
freshness: { maxAgeDays: 90 },
};
expect(isAssessmentStale(assessment, reference)).toBe(false);
});
});
describe("isItemStale", () => {
it("returns false when retrievedAt is missing", () => {
expect(isItemStale({}, DEFAULT_FRESHNESS_WINDOW_DAYS)).toBe(false);
});
it("returns true when age exceeds maxAgeDays", () => {
const reference = new Date("2026-09-12T00:00:00Z");
const item = { retrievedAt: "2026-01-01T00:00:00Z" };
expect(isItemStale(item, 180, reference)).toBe(true);
});
it("returns false when age is within maxAgeDays", () => {
const reference = new Date("2026-09-12T00:00:00Z");
const item = { retrievedAt: "2026-08-01T00:00:00Z" };
expect(isItemStale(item, 180, reference)).toBe(false);
});
});