|
| 1 | +import { readFileSync } from "node:fs"; |
| 2 | +import { describe, expect, it } from "vitest"; |
| 3 | + |
| 4 | +import { FOCUS_MANIFEST_TOP_LEVEL_FIELDS } from "../../packages/loopover-engine/src/focus-manifest"; |
| 5 | + |
| 6 | +// #9860 (item 4): FOCUS_MANIFEST_TOP_LEVEL_FIELDS is a hand-kept list of 28 keys whose doc comment claims it |
| 7 | +// is "every top-level key `parseFocusManifest` below actually reads". Nothing checked that claim, and it is |
| 8 | +// load-bearing in both directions: |
| 9 | +// |
| 10 | +// • A key the parser reads but the list omits is silently reported to operators as an UNKNOWN FIELD -- |
| 11 | +// the config-lint/validator path warns about a setting that in fact works. |
| 12 | +// • A key in the list the parser never reads is worse: the validator blesses it, the runtime ignores it, |
| 13 | +// and an operator's setting does nothing while every surface says it is fine. |
| 14 | +// |
| 15 | +// #9813 and #9821 each shipped a bug from missing one of this registry's touchpoints. This computes the |
| 16 | +// relation instead of trusting the comment. |
| 17 | +// |
| 18 | +// SOURCE-SCANNED rather than exercised through the parser, because the failure is a key the parser reads and |
| 19 | +// the list forgot -- which by construction produces no observable behaviour difference to assert on. The |
| 20 | +// scan is narrow: `record` is the single local `parseFocusManifest` binds the raw object to, and the function |
| 21 | +// body is delimited by the two exported functions around it. |
| 22 | +const SOURCE = "packages/loopover-engine/src/focus-manifest.ts"; |
| 23 | + |
| 24 | +/** Every `record.<key>` / `record["<key>"]` read inside parseFocusManifest's body. */ |
| 25 | +function topLevelKeysReadByParser(source: string): Set<string> { |
| 26 | + const start = source.indexOf("export function parseFocusManifest(raw: unknown"); |
| 27 | + const end = source.indexOf("export function parseFocusManifestContent"); |
| 28 | + expect(start).toBeGreaterThan(-1); |
| 29 | + expect(end).toBeGreaterThan(start); |
| 30 | + const body = source.slice(start, end); |
| 31 | + const keys = new Set<string>(); |
| 32 | + for (const match of body.matchAll(/\brecord\.([A-Za-z_][A-Za-z0-9_]*)/g)) keys.add(match[1]!); |
| 33 | + for (const match of body.matchAll(/\brecord\["([^"]+)"\]/g)) keys.add(match[1]!); |
| 34 | + return keys; |
| 35 | +} |
| 36 | + |
| 37 | +describe("FOCUS_MANIFEST_TOP_LEVEL_FIELDS is the set the parser actually reads (#9860)", () => { |
| 38 | + const source = readFileSync(SOURCE, "utf8"); |
| 39 | + |
| 40 | + it("declares every key parseFocusManifest reads", () => { |
| 41 | + // Missing here => the validator calls a working setting an unknown field. |
| 42 | + const read = topLevelKeysReadByParser(source); |
| 43 | + const declared = new Set<string>(FOCUS_MANIFEST_TOP_LEVEL_FIELDS); |
| 44 | + expect([...read].filter((key) => !declared.has(key)).sort()).toEqual([]); |
| 45 | + }); |
| 46 | + |
| 47 | + it("declares nothing the parser ignores", () => { |
| 48 | + // Present here but unread => the validator blesses a setting that silently does nothing at runtime, |
| 49 | + // which is the more dangerous direction: every surface reports success. |
| 50 | + const read = topLevelKeysReadByParser(source); |
| 51 | + expect(FOCUS_MANIFEST_TOP_LEVEL_FIELDS.filter((key) => !read.has(key)).sort()).toEqual([]); |
| 52 | + }); |
| 53 | + |
| 54 | + it("guards against a vacuous scan: the parser really does read a known key", () => { |
| 55 | + // Without this, a refactor that renamed `record` would empty both sets and make the two assertions above |
| 56 | + // pass while checking nothing at all. |
| 57 | + const read = topLevelKeysReadByParser(source); |
| 58 | + expect(read.size).toBeGreaterThan(20); |
| 59 | + expect(read.has("wantedPaths")).toBe(true); |
| 60 | + expect(read.has("gate")).toBe(true); |
| 61 | + }); |
| 62 | +}); |
0 commit comments