Skip to content

Commit 960dc1e

Browse files
committed
docs(rest): the mirrored-fixture census states a property, not a list of three
The census in `src/http-response-test-builder.ts` named three files. Measured over this package's 187 test files, the population that matches its own stated property -- builds an `any`-typed mirror and asserts on `res.statusCode` / `res.body` -- is 76. The list was also wrong about one of the three it did name: `analytics-dataset-dimension-gate` contains no `makeRes`, because its fixture is spelled `mockRes`. Both errors have one cause: the population was enumerated by HELPER NAME, and the helper name is the one thing about this shape that varies -- only 30 of the 76 spell it `makeRes`. A hand-list keyed on it can only go stale silently, so replace it with the property, the command that derives it, and a non-zero control (18 files that DO define a `makeRes` and are correctly excluded, their double being spy-only or closure-capture with no mirror to read). The same three-name list was mirrored in `test-typecheck-debt.json`'s authored `_note`, which regeneration preserves verbatim and so would never have corrected; fix it there too. `src/rest.test.ts` holds one fixture of each kind and gets a pointer at the mirrored one, which is the fixture a reader of that file is likeliest to assume is already covered because the rest of the file now is. Comment-only; no behaviour change, no assertion converted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
1 parent a5eccf9 commit 960dc1e

3 files changed

Lines changed: 50 additions & 8 deletions

File tree

packages/rest/src/http-response-test-builder.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,45 @@
7676
* a mirror keeps only the last status where `mock.calls` keeps every one, and
7777
* `mockClear()` empties one and not the other. One record, not two.
7878
*
79-
* ⚠️ Three other files in this package — `analytics-dataset-dimension-gate`,
80-
* `meta-public-book-grant`, `rest-batch-size-cap` — assert on a mirrored
81-
* `res.statusCode` / `res.body` built by a local `makeRes()` typed `any`. They
82-
* are green for the forbidden reason rather than conforming, but they cannot
83-
* adopt this builder by substitution: converting those reads into spy reads
84-
* CHANGES the assertion, so it is a decision of its own rather than a mechanical
85-
* edit. Recorded here so the omission is not read as an oversight.
79+
* ⚠️ Most of this package's test files — not the three this paragraph used
80+
* to name — assert on a mirrored `res.statusCode` / `res.body` built by a local
81+
* `any`-typed fixture instead of reaching for this builder. They are green for
82+
* the forbidden reason rather than conforming, but they cannot adopt it by
83+
* substitution: converting those reads into spy reads CHANGES the assertion, so
84+
* it is a decision of its own rather than a mechanical edit. Recorded here so
85+
* the omission is not read as an oversight.
86+
*
87+
* ⛔ **Do not put the file list back.** The three names this paragraph carried
88+
* were wrong in both directions at once. The count was wrong — the real
89+
* population is 76, not 3 — and the stated property was wrong for one of the
90+
* three it did name: `analytics-dataset-dimension-gate` contains no `makeRes`
91+
* at all, because its fixture is spelled `mockRes`. Both errors have one cause,
92+
* which is that the population was enumerated by HELPER NAME and the helper name
93+
* is the one thing about this shape that varies. So what replaces the list is
94+
* the property and the command that derives it, and a reader re-derives instead
95+
* of trusting this sentence:
96+
*
97+
* ```
98+
* # builds a mirror AND asserts on it — 76 of 187 test files at a5eccf925
99+
* grep -lE 'expect\([A-Za-z_$][A-Za-z0-9_$]*\.(statusCode|body)\b' \
100+
* packages/rest/src/*.test.ts | xargs grep -l 'statusCode = ' | wc -l
101+
* ```
102+
*
103+
* Only **30** of those 76 spell the fixture `makeRes`; the rest spell it
104+
* `mockRes` or inline it, which is precisely how a name-keyed hand-list came to
105+
* miss 73 of them. The control that shows the filter discriminates rather than
106+
* matching everything it sees: **18** further files DO define a `makeRes` and
107+
* are correctly excluded, because theirs is a spy-only or closure-capture double
108+
* with no mirror to read.
109+
*
110+
* `src/rest.test.ts` holds one fixture of each kind, which is the reason to
111+
* state a property here rather than a filename: its `makeRes` under
112+
* `describe('RestServer — object API exposure')` IS a mirror and IS in the 76,
113+
* while its `makeRes` under `describe('export handler')` captures status in a
114+
* closure and is correctly outside. The mirrored one also feeds that file's only
115+
* `route.handler` call whose enclosing binding is untyped, so neither argument
116+
* is checked at it — typing it would red the response for the reason above, and
117+
* so belongs to the conversion decision rather than to this paragraph.
86118
*
87119
* **It takes no parameters.** `httpRequestForRoute` needs them because a
88120
* request carries fixture DATA that differs per test. A response double carries

packages/rest/src/rest.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4109,6 +4109,16 @@ describe('filterDashboardForUser — ADR-0057 D10 widget requiresService gate',
41094109
// ---------------------------------------------------------------------------
41104110

41114111
describe('RestServer — object API exposure (apiEnabled / apiMethods)', () => {
4112+
// ⚠️ A MIRROR fixture, NOT this package's response double: it keeps
4113+
// `statusCode` / `body` off `status` / `json`, and the assertions below read
4114+
// that mirror rather than the spies. It is in the census stated in
4115+
// `src/http-response-test-builder.ts` — read that header for why it cannot
4116+
// adopt the builder by substitution. ⛔ Do not assume this file is done with
4117+
// mirrors because most of it is: `invoke()` below carries the only
4118+
// `route.handler` call here whose enclosing binding is untyped, so neither
4119+
// argument is checked at it. (The `makeRes` under `describe('export
4120+
// handler')` above is a different shape again — it captures status in a
4121+
// closure and is outside the census.)
41124122
function makeRes() {
41134123
const res: any = { statusCode: 200, body: undefined };
41144124
res.status = vi.fn((c: number) => { res.statusCode = c; return res; });
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"_comment": "Per-file tsc error debt of the @objectstack/rest TEST layer (#5286). `tsconfig.test.json` compiles `src/**/*.test.ts` — which `tsconfig.json` excludes and therefore no gate ever read — and every file below still carries errors from before that gate existed. THIS FIELD IS GENERATED: every regeneration rewrites it from scripts/check-test-typecheck.mts, and the EXACT ratchet below requires a regeneration on every repair — so an edit made here is gone by the next one. Anything true of THIS package goes in the sibling `_note` field, which is authored, is preserved verbatim, and is never written by the generator (#12624). This comment states NO cause for the errors, deliberately: the classes differ per package and per file, they move as the debt is paid down, and a cause written here is rewritten verbatim into every ledger by every regeneration — so it outlives its own repair and cannot be corrected in the file where it is read. Measure instead, before repairing anything: `tsc --noEmit --pretty false -p tsconfig.test.json` in the package prints the real classes with their TS codes. Each entry maps a file to its per-SIGNATURE error counts, never to a bare total (#13470): a signature is the TS code plus the diagnostic message with structural type blobs collapsed, and it carries NO line or column — so the pin survives edits that move code around, and only stops matching when the error itself becomes a different error. EXACT ratchet, judged by re-running tsc: a file that gains errors is red, a file that loses them is red until its number is re-recorded, a file that reaches zero is red until its entry is deleted, a signature that ARRIVES or VANISHES is red even when the file total is unchanged, and a file NOT listed here may have no errors at all. Regenerate with: pnpm --filter @objectstack/rest gen:test-typecheck-debt",
3-
"_note": "This package's test layer is at ZERO: `entries` is empty, and that is the end state the EXACT ratchet was built for rather than a gap in it. Nothing in this layer is exempt any more, so an error arriving in any src/**/*.test.ts of this package is red on the PR that introduces it, with no entry to widen and nothing to re-record. #13454 closed the last one. The history below is kept because the mechanism is not rest-specific and has not gone away. tsc reports at most ONE argument-assignability error per call expression, so a repair can uncover a different error at the very same site, and a per-file COUNT can hold while the errors underneath it are replaced wholesale: this file was recorded at 2 before #13377 and measured 2 after, and neither of the two was the same error - the request literals that card removed had been masking the response literals beside them. That blindness is closed (#13470): an entry is a map of normalized error SIGNATURE to count, so such a substitution reds with the signature that ARRIVED and the one that VANISHED both named. The count was never the pin; the signature keys were. None of the three classes recorded here was paid down by an annotation, and each ended as one statement a reader can find: the five members IHttpRequest requires are stated in src/http-request-test-builder.ts (#13377), where a request takes its method and its path from the route under test so the two cannot disagree; the four members IHttpResponse requires are stated in src/http-response-test-builder.ts (#13454), whose required-member set is computed from the contract so a new required member fails there rather than in a fixture; and exceljs's module-local Buffer, which shadows Node's inside every exceljs signature, is asserted around once in src/xlsx-test-loader.ts (#13378). What an empty ledger here does NOT say: that every fixture in this package is well-typed. Most `.handler(` call sites still cast their arguments `as any`, and three files (analytics-dataset-dimension-gate, meta-public-book-grant, rest-batch-size-cap) build their response through a local makeRes() typed `any` and assert on a mirrored res.statusCode/res.body. Those are green because nothing is checked there, not because they conform. This ledger can only ever speak for the CHECKED layer.",
3+
"_note": "This package's test layer is at ZERO: `entries` is empty, and that is the end state the EXACT ratchet was built for rather than a gap in it. Nothing in this layer is exempt any more, so an error arriving in any src/**/*.test.ts of this package is red on the PR that introduces it, with no entry to widen and nothing to re-record. #13454 closed the last one. The history below is kept because the mechanism is not rest-specific and has not gone away. tsc reports at most ONE argument-assignability error per call expression, so a repair can uncover a different error at the very same site, and a per-file COUNT can hold while the errors underneath it are replaced wholesale: this file was recorded at 2 before #13377 and measured 2 after, and neither of the two was the same error - the request literals that card removed had been masking the response literals beside them. That blindness is closed (#13470): an entry is a map of normalized error SIGNATURE to count, so such a substitution reds with the signature that ARRIVED and the one that VANISHED both named. The count was never the pin; the signature keys were. None of the three classes recorded here was paid down by an annotation, and each ended as one statement a reader can find: the five members IHttpRequest requires are stated in src/http-request-test-builder.ts (#13377), where a request takes its method and its path from the route under test so the two cannot disagree; the four members IHttpResponse requires are stated in src/http-response-test-builder.ts (#13454), whose required-member set is computed from the contract so a new required member fails there rather than in a fixture; and exceljs's module-local Buffer, which shadows Node's inside every exceljs signature, is asserted around once in src/xlsx-test-loader.ts (#13378). What an empty ledger here does NOT say: that every fixture in this package is well-typed. Most `.handler(` call sites still cast their arguments `as any`, and 76 of this package's 187 test files build their response through a local `any`-typed fixture and assert on a mirrored res.statusCode/res.body. That number replaces a hand-list of three names, which sat here and in src/http-response-test-builder.ts until #15584 and was wrong in both directions: short by 73, and stating a property (`a local makeRes()`) that one of its own three names did not satisfy, since analytics-dataset-dimension-gate spells its fixture mockRes. The cause of both errors is that the population was enumerated by HELPER NAME, which is the one thing about this shape that varies - only 30 of the 76 spell it makeRes. Re-derive it rather than re-listing it: grep -lE 'expect\\([A-Za-z_$][A-Za-z0-9_$]*\\.(statusCode|body)\\b' packages/rest/src/*.test.ts | xargs grep -l 'statusCode = '. Those are green because nothing is checked there, not because they conform. This ledger can only ever speak for the CHECKED layer.",
44
"entries": {}
55
}

0 commit comments

Comments
 (0)