Repository navigation
Commit e5a1732
committed
test(e2e): unreadable scenario roots fail loudly; pin the layering guide's refusal rows (#117)
Two small harness fixes, both test-only plus one docs table.
## 1. An unreadable scenario root fails loudly
**Before**
```text
$ chmod 000 src/sections/labels/scenarios && bun test/e2e/run.ts
no scenario .yml files found under test/e2e/scenarios, ...
$ echo $?
0
```
`collectYmlFiles` swallowed every readdir error and returned `[]`, so an
unreadable corpus was indistinguishable from an empty one.
**After**
```text
$ chmod 000 src/sections/labels/scenarios && bun test/e2e/run.ts
Error: cannot read the scenario directory .../src/sections/labels/scenarios: EACCES: permission denied, scandir '...'
$ echo $?
1
```
**How**
- The loader decides absence versus unreadability once, at the owner
(`test/e2e/schema.ts`): `ENOENT` still yields `[]` (a section may have
no `scenarios/` yet), any other readdir failure throws naming the
directory and the error, with `cause`.
- `loadScenarios` propagates it, so `run.ts` and the coverage tripwire
both fail instead of reporting an empty corpus. The `run.ts` comment on
the empty-set branch now says so.
- `scenarioRoots` no longer filters section paths with `existsSync`
(false for an absent AND an unreadable directory, so a mode-000 section
dropped its root silently). It returns the cross-cutting root plus
`<sectionsDir>/<key>/scenarios` for every key in `SECTION_KEYS`,
unfiltered; absence is decided exactly once, in `collectYmlFiles`.
`sectionsDir` is injectable so a test can stage a tree.
- No caller relied on the old swallow: every root today comes from
`scenarioRoots()` (already `existsSync`-filtered) or is
`test/e2e/scenarios`.
## 2. The layering guide's refusal rows are pinned to the engine
**Before**
The two tables under `## Refusals` in `docs/operate/layering.md` quoted
nine engine messages by hand, with no test. Only one had been verified.
**After**
Each row's first cell ends with the layer that triggers it, so the page
is the single source:
```text
| Two entries under one key in one list (`labels: [{name: bug}, {name: docs}, {name: Bug}]`) | `labels[0] and labels[2] both claim one name; ...` |
```
A guides test parses both tables, folds each row's layer through the
merge step's real gate order (`foldLayers`: per-layer validation, then
the fold) under the layer name the page quotes as the fold's prefix, and
asserts the whole produced error equals the wrapper around the quoted
message. Validation rows are also held equal to the standalone
validator's error (the page's "same messages a standalone file gets"
claim). The wrapper text around a validation problem (`<layer> has
malformed section entries: ... Fix these values in ...`) comes from
`src/engine/validate.ts`, and the test holds the whole error to that
wrapper around the one quoted problem.
**How**
- `refusalRows` in `test/docs/guides.test.ts` reads tables as GFM
renders them: a piped line opens a table only when the delimiter row
follows it, exactly that one line is skipped, every non-blank line after
it is a row (outer pipes optional, indentation allowed), and a blank
line closes the table. A row without its parenthesized input, a row
whose message is not one code span, a one- or three-cell row, an unknown
or malformed header, or a piped line with no delimiter after it fails by
name. A row appended without its leading pipe, or with a dash-run first
cell, is pinned rather than skipped (gate review findings).
- A mutation-check block proves each of those parser branches.
- One row's prose was tightened: "a rule with no `type`" was dropped
from the keyed-entry row because its message differs from the one the
row quotes; the label form is the one reproduced. The "no layering key"
row names both forms the page claims (wrapper and file-level), and both
are reproduced.
## Proof
- `bun run check`: green (2613 pass, 1 skip, 0 fail; lint, lint:arch,
typecheck, knip, build:check all green).
- Mutation checks run by hand, then reverted: swallowing all errno codes
in the loader fails the unreadable-root test; changing one word of a
quoted refusal on the page fails that row's test; indenting a row and
changing its message fails that row's test; appending an unpiped last
row with a wrong message fails that row's test.
- Codex rubber-duck, read-only: round 1 one BLOCKING (indented table
rows were skipped), fixed and covered; round 2 no blocking, no
non-blocking; round 3 (on the gate-review fixes) no blocking, no
non-blocking.
Tests added:
- `scenario corpus loader (collectYmlFiles)`: a root that does not exist
yields `[]`; a readable empty root yields `[]`; an unreadable root fails
naming it and the error (skipped as root); `scenarioRoots` names every
registered section's path, present or not, and an empty tree loads as
`[]`; an unreadable section `scenarios/` fails the whole corpus naming
it (skipped as root).
- `the layering guide's refusal tables quote the messages the merge step
emits`: both tables present; one test per row (3 validation, 6 fold).
- `refusal table parser (mutation checks)`: 13 cases.1 parent 3381d3f commit e5a1732
5 files changed
Lines changed: 397 additions & 31 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
254 | 254 | | |
255 | 255 | | |
256 | 256 | | |
257 | | - | |
| 257 | + | |
258 | 258 | | |
259 | 259 | | |
260 | 260 | | |
261 | 261 | | |
262 | 262 | | |
263 | 263 | | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | | - | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
270 | 270 | | |
271 | 271 | | |
272 | 272 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| |||
288 | 289 | | |
289 | 290 | | |
290 | 291 | | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
291 | 411 | | |
292 | 412 | | |
293 | 413 | | |
| |||
619 | 739 | | |
620 | 740 | | |
621 | 741 | | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
622 | 789 | | |
623 | 790 | | |
624 | 791 | | |
| |||
867 | 1034 | | |
868 | 1035 | | |
869 | 1036 | | |
| 1037 | + | |
| 1038 | + | |
| 1039 | + | |
| 1040 | + | |
| 1041 | + | |
| 1042 | + | |
| 1043 | + | |
| 1044 | + | |
| 1045 | + | |
| 1046 | + | |
| 1047 | + | |
| 1048 | + | |
| 1049 | + | |
| 1050 | + | |
| 1051 | + | |
| 1052 | + | |
| 1053 | + | |
| 1054 | + | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
| 1058 | + | |
| 1059 | + | |
| 1060 | + | |
| 1061 | + | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + | |
| 1065 | + | |
| 1066 | + | |
| 1067 | + | |
| 1068 | + | |
| 1069 | + | |
| 1070 | + | |
| 1071 | + | |
| 1072 | + | |
| 1073 | + | |
| 1074 | + | |
| 1075 | + | |
| 1076 | + | |
| 1077 | + | |
| 1078 | + | |
| 1079 | + | |
| 1080 | + | |
| 1081 | + | |
| 1082 | + | |
| 1083 | + | |
| 1084 | + | |
| 1085 | + | |
| 1086 | + | |
| 1087 | + | |
| 1088 | + | |
| 1089 | + | |
| 1090 | + | |
| 1091 | + | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
| 1105 | + | |
| 1106 | + | |
| 1107 | + | |
| 1108 | + | |
| 1109 | + | |
| 1110 | + | |
| 1111 | + | |
| 1112 | + | |
| 1113 | + | |
| 1114 | + | |
| 1115 | + | |
| 1116 | + | |
870 | 1117 | | |
871 | 1118 | | |
872 | 1119 | | |
| |||
0 commit comments