Skip to content

Commit f05bbbc

Browse files
committed
test(cli): assert the reference shape as a list, so a failure names the field
The fingerprint was compared as a sha256 digest, which reports "expected 0add… to be ab12…" on failure. That says the shape moved but not what moved, leaving whoever hit it to rebuild the shape set by hand to find the one added field. A guard that fires this rarely has to be legible the one time it does. The shape is 56 (path, kind) pairs, so it is asserted as the sorted list itself and vitest prints the difference. Measured, by adding a field to the corpus without bumping the version: + "$.mutationProbe:boolean" which names the offending field directly. Same coverage, same derivation from the live object, no digest to reconcile by hand, and one fewer import.
1 parent e30139d commit f05bbbc

1 file changed

Lines changed: 75 additions & 17 deletions

File tree

‎packages/cli/test/reference.test.ts‎

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { createHash } from "node:crypto";
21
import { readFile } from "node:fs/promises";
32
import { join, sep } from "node:path";
43

@@ -74,25 +73,29 @@ function shapeOf(value: unknown, path: string, into: Set<string>): void {
7473
}
7574

7675
/**
77-
* A structural fingerprint of the reference payload's SHAPE, derived from the
78-
* live object rather than hand-copied -- so it moves when `Reference` /
79-
* `ReferenceRule` / `ReferenceTests` / `ReferenceLayout` change shape, and
80-
* cannot silently rot the way a hand-maintained field list would.
76+
* Every (path, kind) pair the reference payload reaches, sorted.
77+
*
78+
* Derived from the live object rather than hand-copied, so it moves when
79+
* `Reference` / `ReferenceRule` / `ReferenceTests` / `ReferenceLayout` change
80+
* shape and cannot silently rot the way a hand-maintained field list would.
8181
*
8282
* This is the check the `v1 -> v2` episode (see `REFERENCE_VERSION`'s own
83-
* comment, and CLAUDE.md's account of it) needed and didn't have: `tests`
83+
* comment, and CLAUDE.md's account of it) needed and did not have: `tests`
8484
* went from an array to `{ grouping, files, cases? }` and nothing forced
85-
* `REFERENCE_VERSION` to move with it. A future change of the same kind now
86-
* changes this hash, and the fix is the same one either way -- bump
87-
* `REFERENCE_VERSION`, update the expected hash below, and say why in the
88-
* commit that touches both.
85+
* `REFERENCE_VERSION` to move with it. A change of that kind now fails here,
86+
* and the fix is to bump `REFERENCE_VERSION`, update the list below, and say
87+
* why in the commit that touches both.
88+
*
89+
* ASSERTED AS A LIST, NOT A HASH, ON PURPOSE. A digest comparison reports
90+
* "expected 0add… to be ab12…", which says the shape moved but not what moved,
91+
* leaving whoever hit it to rebuild the shape set by hand to find the one added
92+
* field. A guard that fires this rarely has to be legible the one time it does,
93+
* and 56 lines is a small price for a diff that names the field.
8994
*/
90-
function shapeFingerprint(value: unknown): string {
95+
function shapePaths(value: unknown): string[] {
9196
const paths = new Set<string>();
9297
shapeOf(value, "$", paths);
93-
return createHash("sha256")
94-
.update([...paths].toSorted().join("\n"))
95-
.digest("hex");
98+
return [...paths].toSorted();
9699
}
97100

98101
describe("the demo reference payload", () => {
@@ -319,9 +322,64 @@ describe("the demo reference payload", () => {
319322
// Bump `REFERENCE_VERSION`, update its doc comment to say what changed and
320323
// why, regenerate `assets/reference.json` (`pnpm --filter @taskless/cli
321324
// reference`), and update the hash below to match.
322-
expect(shapeFingerprint(reference)).toBe(
323-
"0add32751f84d71a2684d7586a4d1b1d2fd4dbf543efeabc3a842b57b27ad787"
324-
);
325+
expect(shapePaths(reference)).toEqual([
326+
"$.constraints:array",
327+
"$.constraints[].enforcedBy:string",
328+
"$.constraints[].engine:string",
329+
"$.constraints[].id:string",
330+
"$.constraints[].rationale:string",
331+
"$.constraints[].summary:string",
332+
"$.constraints[]:object",
333+
"$.layout.engines.runtime.capturesDirectory:string",
334+
"$.layout.engines.runtime.fixtureLayout:string",
335+
"$.layout.engines.runtime.ruleConfigFile:null",
336+
"$.layout.engines.runtime.ruleFile:string",
337+
"$.layout.engines.runtime:object",
338+
"$.layout.engines.sg.capturesDirectory:null",
339+
"$.layout.engines.sg.fixtureLayout:string",
340+
"$.layout.engines.sg.ruleConfigFile:null",
341+
"$.layout.engines.sg.ruleFile:string",
342+
"$.layout.engines.sg:object",
343+
"$.layout.engines.vale.capturesDirectory:null",
344+
"$.layout.engines.vale.fixtureLayout:string",
345+
"$.layout.engines.vale.ruleConfigFile:string",
346+
"$.layout.engines.vale.ruleFile:string",
347+
"$.layout.engines.vale:object",
348+
"$.layout.engines:object",
349+
"$.layout.ruleDirectory:string",
350+
"$.layout.rulesRoot:string",
351+
"$.layout.testsDirectory:string",
352+
"$.layout:object",
353+
"$.protocol:array",
354+
"$.protocol[]:string",
355+
"$.rules:array",
356+
"$.rules[].directory:string",
357+
"$.rules[].engine:string",
358+
"$.rules[].id:string",
359+
"$.rules[].prompt:string",
360+
"$.rules[].rule:array",
361+
"$.rules[].ruleFile:string",
362+
"$.rules[].rule[].content:string",
363+
"$.rules[].rule[].path:string",
364+
"$.rules[].rule[]:object",
365+
"$.rules[].signature:string",
366+
"$.rules[].tests.cases:array",
367+
"$.rules[].tests.cases[].bucket:string",
368+
"$.rules[].tests.cases[].files:array",
369+
"$.rules[].tests.cases[].files[]:string",
370+
"$.rules[].tests.cases[].name:string",
371+
"$.rules[].tests.cases[].path:string",
372+
"$.rules[].tests.cases[]:object",
373+
"$.rules[].tests.files:array",
374+
"$.rules[].tests.files[].content:string",
375+
"$.rules[].tests.files[].path:string",
376+
"$.rules[].tests.files[]:object",
377+
"$.rules[].tests.grouping:string",
378+
"$.rules[].tests:object",
379+
"$.rules[]:object",
380+
"$.version:number",
381+
"$:object",
382+
]);
325383
});
326384

327385
it("carries the prompt each rule answers", async () => {

0 commit comments

Comments
 (0)