test: cover dashboard needsValidation and detail() nextAction regression risk
Round 3 of the TechLog contract task added two pieces of genuinely new logic with no assertion on their output: the totals.needsValidation count in getDashboard(), and the detail() rewrite that avoids a self-reference when computing WorkingCopyDetail.nextAction. Both would have passed every existing test if they regressed. - needsValidation is checked against an independent count derived from listDocuments()'s per-document nextAction, with sanity bounds so a filter that always returns 0 or the full count can't pass silently. - detail()'s nextAction is asserted on two fixtures whose expected value is justified by the asserted preconditions alongside it (published-at-current-version -> NONE; INVALID-but-current-validation -> FIX_VALIDATION). Verified locally that hardcoding nextAction to a constant in detail() makes the second assertion fail, and that zeroing needsValidation's count makes the dashboard assertion fail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
639e1a49c9
commit
a0e0be6522
@@ -597,3 +597,45 @@ test("publication history, immutable snapshots, conflict fixtures and 404 bodies
|
|||||||
isProblem(404, "PUBLICATION_EVENT_NOT_FOUND"),
|
isProblem(404, "PUBLICATION_EVENT_NOT_FOUND"),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("dashboard totals.needsValidation matches the documents whose next action is to validate or fix validation", async () => {
|
||||||
|
const gateway = gatewayAt();
|
||||||
|
const dashboard = await gateway.getDashboard();
|
||||||
|
const documents = await gateway.listDocuments({ limit: 100 });
|
||||||
|
|
||||||
|
const expected = documents.items.filter(
|
||||||
|
(item) =>
|
||||||
|
item.nextAction === "VALIDATE" || item.nextAction === "FIX_VALIDATION",
|
||||||
|
).length;
|
||||||
|
// Cross-checked against `listDocuments`, not against the dashboard's own
|
||||||
|
// arithmetic, so a broken `needsValidation` filter shows up as a mismatch
|
||||||
|
// instead of being restated. Both branches (>0 and <total) must hold, or a
|
||||||
|
// filter that always returns 0 or the full count would still pass silently.
|
||||||
|
assert.ok(expected > 0);
|
||||||
|
assert.ok(expected < documents.items.length);
|
||||||
|
assert.equal(dashboard.totals.needsValidation, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getDocument's nextAction reflects the assembled WorkingCopyDetail, not a cached or default value", async () => {
|
||||||
|
const gateway = gatewayAt();
|
||||||
|
|
||||||
|
// Published at exactly the current version (fixtures.ts: publishedVersion 4
|
||||||
|
// === document version 4) is deriveNextAction's first, clock-independent
|
||||||
|
// branch: nextAction must be "NONE" regardless of validation or preview state.
|
||||||
|
const redis = await gateway.getDocument(FIXTURE_IDS.redisAdapterCase);
|
||||||
|
assert.equal(redis.document.version, 4);
|
||||||
|
assert.equal(redis.currentPublication?.status, "PUBLISHED");
|
||||||
|
assert.equal(redis.currentPublication?.publishedVersion, 4);
|
||||||
|
assert.equal(redis.nextAction, "NONE");
|
||||||
|
|
||||||
|
// Not published, and validated INVALID at the current version within the
|
||||||
|
// validity window: nextAction must be "FIX_VALIDATION", which only follows
|
||||||
|
// from currentValidation actually being read and its freshness actually
|
||||||
|
// computed - a value a hardcoded or wrongly-sourced default would not produce.
|
||||||
|
const edgeToken = await gateway.getDocument(FIXTURE_IDS.edgeTokenQuestion);
|
||||||
|
assert.equal(edgeToken.document.version, 2);
|
||||||
|
assert.equal(edgeToken.currentPublication, null);
|
||||||
|
assert.equal(edgeToken.currentValidation?.status, "INVALID");
|
||||||
|
assert.equal(edgeToken.currentValidation?.validatedVersion, 2);
|
||||||
|
assert.equal(edgeToken.nextAction, "FIX_VALIDATION");
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user