@boopkit/cap-testing 0.5.3 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,16 +12,19 @@ Agent skills plus a minimal Jest + ts-jest scaffold. The scaffold is intentional
12
12
 
13
13
  Copied into your project on install:
14
14
 
15
- - `jest.config.js` — ts-jest preset, Node test environment, test matcher at `tests/**/*.test.ts`
16
- - `tsconfig.test.json` — extends your project tsconfig and includes `tests/`
15
+ - `jest.config.js` — ts-jest preset, Node test environment, test matcher at `tests/**/*.test.ts`, and the worker settings the suite runs under (`maxWorkers: 4`, `workerIdleMemoryLimit: '500MB'`). Those two spread test files across a pool of worker processes Jest recycles as they grow, so a long suite's memory stays bounded instead of climbing until it dies. They live in the config rather than in the `test` script so that focused runs — `npx jest tests/foo.test.ts` — inherit them too. Because files now run concurrently, tests must namespace whatever they create; drop `maxWorkers` to 1 if a suite is not isolation-safe yet.
16
+ - `tsconfig.test.json` — extends your project tsconfig and includes `tests/`, widening `rootDir` to the project and naming its `@types` packages so that `src/` and `tests/` compile as one program on any TypeScript major. Its comments explain each setting.
17
17
  - `tests/app.test.ts` — a starter test asserting the Express app boots and has body parsing wired
18
18
 
19
19
  ### Scripts added to `package.json`
20
20
 
21
21
  | Script | Command |
22
22
  |--------|---------|
23
- | `test` | `jest --runInBand` |
23
+ | `test` | `jest` |
24
24
  | `test:watch` | `jest --watch` |
25
+ | `typecheck` | `tsc -p tsconfig.test.json` |
26
+
27
+ `typecheck` is the whole-program static check the testing and planning skills tell every task to run alongside its focused tests: it sees every caller of what changed, which is the breakage a focused run is blind to.
25
28
 
26
29
  ### Skills installed
27
30
 
@@ -37,9 +40,12 @@ Copied into `skills/` on install:
37
40
 
38
41
  **npm devDependencies added to your project:**
39
42
 
40
- - `jest` — test runner
41
- - `ts-jest` — TypeScript preset for Jest
42
- - `@types/jest` — TypeScript types for Jest
43
+ - `jest@^30` — test runner
44
+ - `ts-jest@^29` — TypeScript preset for Jest
45
+ - `@types/jest@^30` — TypeScript types for Jest
46
+ - `typescript@^5` — required by `ts-jest` and by the `typecheck` script
47
+
48
+ Each is pinned to a caret major so that the toolchain a new project receives is the one boop runs its own suite against, rather than whatever shipped most recently.
43
49
 
44
50
  **Environment variables:** none required for the scaffold. Individual tests may need service credentials (the Express testing skill documents the integration-test prerequisites — local Postgres, etc.) — those are project-specific and not enforced by this capability.
45
51
 
@@ -48,7 +54,7 @@ Copied into `skills/` on install:
48
54
  Run the suite:
49
55
 
50
56
  ```bash
51
- npm test # full suite, serialized
57
+ npm test # full suite
52
58
  npm run test:watch # watch mode
53
59
  ```
54
60
 
package/capability.json CHANGED
@@ -4,9 +4,10 @@
4
4
  "type": "development",
5
5
  "dependencies": [],
6
6
  "devDependencies": [
7
- "jest",
8
- "ts-jest",
9
- "@types/jest"
7
+ "jest@^30",
8
+ "ts-jest@^29",
9
+ "@types/jest@^30",
10
+ "typescript@^5"
10
11
  ],
11
12
  "files": [
12
13
  "jest.config.js",
@@ -20,12 +21,16 @@
20
21
  ],
21
22
  "scripts": {
22
23
  "test": {
23
- "cmd": "jest --runInBand",
24
+ "cmd": "jest",
24
25
  "description": "Run all tests"
25
26
  },
26
27
  "test:watch": {
27
28
  "cmd": "jest --watch",
28
29
  "description": "Run tests in watch mode"
30
+ },
31
+ "typecheck": {
32
+ "cmd": "tsc -p tsconfig.test.json",
33
+ "description": "Type-check src/ and tests/ as one program"
29
34
  }
30
35
  },
31
36
  "skill_migrations": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boopkit/cap-testing",
3
- "version": "0.5.3",
3
+ "version": "0.5.6",
4
4
  "description": "Jest testing setup and testing skills for AI agents",
5
5
  "files": [
6
6
  "scaffold/",
@@ -6,4 +6,28 @@ module.exports = {
6
6
  transform: {
7
7
  '^.+\\.ts$': ['ts-jest', { tsconfig: 'tsconfig.test.json' }],
8
8
  },
9
+
10
+ // Run test files across a small pool of worker processes that Jest recycles.
11
+ //
12
+ // `workerIdleMemoryLimit` is the load-bearing setting and it is easy to drop
13
+ // by accident. Without it Jest runs every file in its own main process — never
14
+ // recycling it — whenever `maxWorkers <= 1`, and that process's heap grows for
15
+ // the whole run until a long suite dies of it. Setting a memory limit is what
16
+ // overrides that decision: it forces real workers and gives Jest a threshold at
17
+ // which to replace one. `maxWorkers` on its own does not do this, and neither
18
+ // did the `--runInBand` this replaced — that flag asks for exactly the
19
+ // un-recycled main process described above.
20
+ //
21
+ // The limit is compared against V8 `heapUsed`, not RSS, and is checked between
22
+ // test files. Budget for real memory use somewhat above `maxWorkers` times this
23
+ // number, and note that a single file exceeding it is not caught mid-flight.
24
+ //
25
+ // Four workers is a deliberate middle setting; six is comfortable on a developer
26
+ // machine with memory to spare. The constraint on raising it is not the machine,
27
+ // it is your tests: files now run CONCURRENTLY, so anything sharing a database,
28
+ // a fixture directory, or a well-known key with another test file will collide.
29
+ // Namespace what each test creates. Drop to `maxWorkers: 1` if a suite is not
30
+ // isolation-safe yet — that still gets you the recycling, just without overlap.
31
+ maxWorkers: 4,
32
+ workerIdleMemoryLimit: '500MB',
9
33
  };
@@ -1,5 +1,26 @@
1
1
  {
2
+ // The config ts-jest type-checks against, and the one to point a static
3
+ // check at: `npx tsc -p tsconfig.test.json`.
2
4
  "extends": "./tsconfig.json",
5
+ "compilerOptions": {
6
+ // The base config sets "rootDir": "src" so a build emits dist/ cleanly.
7
+ // Tests live outside src/, so inheriting that root makes every one of them
8
+ // a TS6059 "not under rootDir" error before a single assertion is read.
9
+ // Widening the root to the project is what lets tests and sources share a
10
+ // program; nothing is emitted from here, so the narrower build root is
11
+ // unaffected.
12
+ "rootDir": ".",
13
+ // Type-check only. Without this a stray `tsc -p tsconfig.test.json` would
14
+ // emit compiled tests into dist/ alongside the real build output.
15
+ "noEmit": true,
16
+ // Name the type packages explicitly instead of relying on TypeScript
17
+ // pulling in everything under node_modules/@types. That automatic
18
+ // inclusion is not dependable across TypeScript majors — under 6.x it
19
+ // stops finding @types/jest here, and every `describe`/`it`/`expect` in
20
+ // the suite becomes TS2593/TS2304 while the package sits installed and
21
+ // correct. Listing them costs nothing and does not change on upgrade.
22
+ "types": ["jest", "node"]
23
+ },
3
24
  "include": ["src/**/*", "tests/**/*"],
4
25
  "exclude": ["node_modules", "dist"]
5
26
  }
@@ -12,7 +12,7 @@ Red → Green → Refactor. Document "before" and "after" states.
12
12
  2. **Green**: Implement the minimum change. Add any additional tests.
13
13
  3. **Verify**: Re-run all tests from steps 1-2. Type-checking alone is **not sufficient** — functional tests must pass. Check for regressions in related functionality. Verify from the user's perspective — don't just test internal state. For example, if the feature writes files the user interacts with, verify those files exist and are correct.
14
14
 
15
- Test tools: `npm run test` (Jest), `npm run tester` (CLI — see `npm run tester -- help`). For UI changes, coordinate manual browser testing.
15
+ Test tools: `npm test` for the whole suite, `npx jest <path>` for a focused run. Go through `npm test` rather than the runner binary when you want the suite — the script and `jest.config.js` together carry the settings the suite is tuned for, including how many files run at once and how much memory a worker may hold before Jest replaces it, and a bare runner invocation drops whatever lives only in the script. The general testing skill explains why those settings matter and which of them is load-bearing. If the project defines its own test commands beyond these, its `package.json` is the authority. For UI changes, coordinate manual browser testing.
16
16
 
17
17
  ## Picking the right test shape
18
18
 
@@ -58,6 +58,34 @@ A skip carrying neither a ticket reference nor a `SKIP-OK:` reason is a conventi
58
58
 
59
59
  ---
60
60
 
61
+ ## Which tests to run, and when
62
+
63
+ **The rule.** A change runs the focused tests for what it touched, plus the project's whole-program static check. The full suite runs once per branch, on the work that completes it. The `boop-project-mgmt-beads-planning` skill carries the canonical wording to put on issues; this section is the reasoning behind it, and the two have to keep agreeing.
64
+
65
+ **The command is `npm run typecheck`** — `tsc -p tsconfig.test.json`, which compiles `src/` and `tests/` as one program and emits nothing. `boop add testing` scaffolds the script and the config together, so in any project with this capability the static check has a name rather than an invocation each agent has to derive.
66
+
67
+ If a project does not have it, add it before following the rule, and check the config before trusting it. A `tsconfig.test.json` that extends a build config and then includes `tests/**/*` inherits `"rootDir": "src"`, and every test file is then a TS6059 "not under rootDir" before a single assertion is read — a static check that fails on every file teaches an agent to skip it. The scaffolded config sets `"rootDir": "."`, `"noEmit": true` and an explicit `"types"` list for exactly that reason; its comments explain each one.
68
+
69
+ **Why the whole suite is the wrong tool mid-change.** The problem is localization, first and foremost. Break a shared dependency and the suite reports it as a cascade of failures in tests that never mention the thing you changed, and you are then reading a wall of red to reconstruct a fact the type-checker would have told you outright, naming a file and a line. A static check plus a search for callers answers "does everything that uses this still hold?" both faster and more precisely than the suite does.
70
+
71
+ Second, it is slower than the focused run, though see the caution below about how much weight that carries.
72
+
73
+ Third, a suite already red for an unrelated reason gives you nothing to compare against, so mid-change runs on a red tree get retried and re-read rather than acted on.
74
+
75
+ **Do not rest the rule on the suite being slow.** A suite's wall-clock time is a property of how the runner is invoked as much as of the tests themselves, and it can fall by an order of magnitude on a single configuration change — running the files in a recyclable worker pool rather than in one long-lived process is the common case. A rule argued from "the suite takes tens of minutes" quietly expires the day somebody fixes the runner, and then it reads as superstition. Argue it from instrument fit, which does not expire.
76
+
77
+ **What we have not measured.** Nobody has measured how often a focused-tests-only run misses a break that the full suite would have caught, weighed against the time the narrow runs save. Until somebody does, this rule rests on the shippable-unit argument and on instrument fit, and on nothing else. That is worth stating plainly rather than dressing up.
78
+
79
+ ### Go through the project's test script, not a bare runner invocation
80
+
81
+ Run the suite the way the project defines it — `npm test` — rather than reaching past it to the runner binary. The script and the runner's config file together carry the settings the suite is tuned for: how many files run at once, and how much memory a worker may hold before the runner replaces it. A bare `npx jest` picks up the config file but drops anything that lives only in the script.
82
+
83
+ Those settings are not decoration. With Jest specifically, `--maxWorkers=1` **does nothing on its own** — Jest runs every file in its own main process, and never recycles it, whenever `maxWorkers <= 1`. Setting `workerIdleMemoryLimit` is what forces a real worker process and gives Jest a threshold at which to replace it; it is the load-bearing setting, and a long suite that grows its heap until it dies is what its absence looks like. Anyone "simplifying" the two settings down to the worker cap has restored the bug.
84
+
85
+ Keep those settings in the runner's config file rather than in the `test` script, so that the focused runs this section asks for inherit them too. `boop add testing` scaffolds them that way, at four workers and a 500MB per-worker ceiling.
86
+
87
+ **A worker pool makes test isolation a requirement, not a nicety.** More than one worker means test *files* run at the same time, in separate processes, against the same backing services. Anything two files share and do not namespace — a table one of them truncates in setup, a fixture directory, a well-known key or account id — is now a race, and it surfaces as a suite that passes alone and fails in a full run. That is the same symptom the shared-dev-database section below describes, reached by a different route, and it invites the same wrong fix of weakening the assertion that caught it. Namespace what each test creates. Where a suite is not isolation-safe yet, set `maxWorkers: 1`: that keeps the recycling, which is the part that fixes the memory, and gives up only the overlap.
88
+
61
89
  ## Integration tests never touch the shared dev database
62
90
 
63
91
  A test or migration that needs a real database must not run against the checkout's inherited connection string.
@@ -197,4 +225,4 @@ When code produces a schema or structure that is validated by an external system
197
225
  ## References
198
226
 
199
227
  - [development.skill.md](development.skill.md) — Light TDD Flow, completion criteria
200
- - [jest.config.js](../jest.config.js) — Jest configuration
228
+ - [jest.config.js](../../jest.config.js) — Jest configuration, at the project root this capability installs it to
@@ -20,6 +20,8 @@ npx jest tests/services/foo --no-coverage # Pattern match
20
20
  npx jest tests/services/foo.test.ts -t "handles replay" # By test name
21
21
  ```
22
22
 
23
+ The focused forms are safe to reach for: the concurrency and per-worker memory settings live in `jest.config.js`, not in the `test` script, so `npx jest <path>` runs under the same settings the full suite does. Which of these you should be running on a given piece of work is a separate question — see "Which tests to run, and when" in the parent skill.
24
+
23
25
  **Prerequisites**: integration tests run against the project's real backing services rather than mocks, so whatever the app talks to at runtime — the database first of all — has to be reachable from the machine running the tests. Which services those are depends on the capabilities the project has installed; its README and environment template are the authority.
24
26
 
25
27
  ## Test File Layout