@boopkit/cap-testing 0.5.1 → 0.5.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boopkit/cap-testing",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Jest testing setup and testing skills for AI agents",
5
5
  "files": [
6
6
  "scaffold/",
@@ -58,6 +58,35 @@ A skip carrying neither a ticket reference nor a `SKIP-OK:` reason is a conventi
58
58
 
59
59
  ---
60
60
 
61
+ ## Integration tests never touch the shared dev database
62
+
63
+ A test or migration that needs a real database must not run against the checkout's inherited connection string.
64
+ In a worktree that is the operator's live dev database, and **file isolation is not data isolation**.
65
+ Measured cost: a loop's cutover migration ran against the shared dev database and destroyed the operator's live data.
66
+ An agent that applies migrations by hand, outside the test harness, must still create and point at a throwaway database first, and say so in its run record.
67
+
68
+ Make the harness handle this so nobody has to remember it.
69
+ Ask git whether the run is in a linked worktree, and in one, derive a per-worktree test database name from the worktree's absolute path — the same name on every run in that worktree, so the one-time migration cost is paid once.
70
+ The main checkout keeps the ordinary shared test database, and an explicit test-database override still wins over both, so it remains the escape hatch when you want a particular database.
71
+
72
+ Without that derivation, two concurrent runs both resolve to one test database and each suite's setup truncates the other's rows.
73
+ What that produces is not an obvious collision: a suite passes alone, fails in a full run, and fails on unmodified `HEAD` too, which reads as pre-existing flakiness in your own code and invites an agent to weaken real assertions until the phantom failures stop.
74
+ If you see that pattern, check which database the run resolved to before you touch an assertion.
75
+
76
+ ## A suite that passes alone and fails in a full run is a suspect, not a verdict
77
+
78
+ Inter-suite interference — leaked state, a shared fixture, a revision conflict under concurrency — produces exactly the shape of a real regression.
79
+ Rerun before you touch an assertion, and never make a red test green by weakening what it asserts.
80
+ The failure that invites that is exactly the failure that is lying to you.
81
+
82
+ If a suite is a known intermittent offender, say so in writing where the next reader will look, for the same reason the shared-database hazard is written down: an undocumented intermittent failure gets "fixed" by deleting the assertion that caught it.
83
+
84
+ ## A guard is only a guard if it can tell it is talking to your application
85
+
86
+ A test that gates its live cases on a generic liveness probe — a plain health endpoint returning ok — is trusting that nothing else could answer.
87
+ Measured: an unrelated process on the operator's machine answered that probe while returning 404 for every real route, so cases that should have skipped ran and failed.
88
+ Make the guard assert something only this application would say.
89
+
61
90
  ## Generic Testing Approach
62
91
 
63
92
  These principles apply across all subsystems.
@@ -7,7 +7,7 @@ metadata:
7
7
  # Testing: Express App (Web & APIs)
8
8
 
9
9
 
10
- Parent skill: [testing.skill.md](testing.skill.md) (philosophy, generic approach)
10
+ Parent skill: [`boop-testing-testing`](../boop-testing-testing/SKILL.md) (philosophy, generic approach)
11
11
 
12
12
  ---
13
13
 
@@ -20,63 +20,68 @@ 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
- **Prerequisites**: local Postgres, ClickHouse, and AWS credentials must be available. Integration tests use real services via `getServices()`.
23
+ **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
24
 
25
25
  ## Test File Layout
26
26
 
27
27
  ```
28
28
  tests/
29
- helpers.ts # getServices(), TestEntityManager, factories
29
+ helpers/ # shared setup, factories, harnesses (see below)
30
30
  services/
31
31
  foo.test.ts # Unit tests (mocked deps)
32
32
  foo.integration.test.ts # Integration tests (real services)
33
33
  controllers/
34
- walletAPI.test.ts # HTTP route tests
34
+ itemsAPI.test.ts # HTTP route tests
35
35
  middleware/
36
36
  utils/
37
37
  ```
38
38
 
39
- - `.test.ts` for Jest auto-discovery; `.integration.test.ts` when `jest.mock` conflicts with `getServices()` in the same file
39
+ - `.test.ts` for Jest auto-discovery. Keep the mocked tests and the real-service tests for the same subject in **separate files**: `jest.mock` is hoisted above the imports and applies to every test in the module, so one file cannot both stub a dependency and exercise the real one. `.integration.test.ts` is the conventional name for the half that uses the real services.
40
40
  - Group by domain: `services/`, `controllers/`, `middleware/`
41
41
 
42
- ## Test Helpers — [tests/helpers.ts](../tests/helpers.ts)
43
- - `getServices()` — real services for integration tests
44
- - `TestEntityManager` — tracks entities, cleans up in `afterEach`
45
- - Factory helpers (`createTestAccount`, `createTestDomain`, etc.) — use instead of raw Prisma
42
+ ## Test Helpers
43
+
44
+ Shared test setup belongs in one place under `tests/`, not re-derived in each spec file: booting the app, creating the entities a test needs and tearing them down again, and harnesses that make an awkward dependency — a broker, a queue, a rendered template — drivable from a test.
45
+
46
+ **Read what the project already has before you write one.** These helpers are not scaffolded and cannot be, because they follow whichever capabilities and domain the project has. Every project grows its own set, and the shape varies from one shared module to a directory of purpose-built ones. Reuse what fits and extend it, rather than starting a private copy inside your test file. A helper used by exactly one test is not a helper — leave it in the test.
47
+
48
+ When you do add one, keep two properties. A factory returns a real, persisted entity rather than a hand-built object literal, so the test exercises the same shape production does. And whatever creates data owns its teardown, so one failed run does not poison the next.
46
49
 
47
50
  ## Integration Test Data Isolation
48
- - Unique prefixes (`_test/${Date.now()}`) so parallel runs don't collide
49
- - Track created resources for cleanup in `afterAll`; wrap each cleanup in `try/catch`
50
- - For ClickHouse: use test-specific `account_id` / `hostname` (row deletes are expensive with RMT)
51
+
52
+ Integration tests share their backing services with every other integration test, and with whatever the last run left behind. Three habits keep them from interfering with each other:
53
+
54
+ - **Namespace whatever you create.** A per-run prefix (`_test/${Date.now()}`, a UUID) keeps parallel runs and repeat runs from colliding on unique constraints.
55
+ - **Track created records and remove them in `afterAll`**, each cleanup wrapped in its own `try/catch` so one failure doesn't strand the rest.
56
+ - **Scope instead of deleting where deletes are expensive.** Some stores — append-only logs, analytics columnstores, anything with asynchronous merge semantics — make row deletion slow or eventually-consistent. Isolate those by writing under a test-specific tenant or key and leave the rows in place.
51
57
 
52
58
  ## HTTP Route Tests
53
- Test Express routes via **real HTTP requests** (`fetch()`) against a running local server. This exercises the full middleware chain (auth → feature flags → controller → service → DB → response).
59
+ Test Express routes via **real HTTP requests** (`fetch()`) against a running local server. This exercises the full middleware chain (auth → controller → service → DB → response).
54
60
 
55
61
  Why real HTTP over supertest: supertest skips TCP/listen and can mask middleware ordering or config bugs. Auth middleware is a critical path worth testing through, not around.
56
62
 
57
63
  **Setup:**
58
64
  ```typescript
59
65
  const BASE_URL = process.env.TEST_BASE_URL || 'http://localhost:3000';
60
- const TEST_API_KEY = process.env.TEST_API_KEY; // npm run admin -- key-generate
66
+ const TEST_API_KEY = process.env.TEST_API_KEY; // however this project issues API keys
61
67
  const authHeaders = { 'Authorization': `Bearer ${TEST_API_KEY}`, 'Content-Type': 'application/json' };
62
68
  ```
63
69
 
64
- **What to cover:** auth rejection (401/403), feature-flag gates (404), request validation (400), happy-path response shape, cross-account access checks.
70
+ **What to cover:** auth rejection (401/403), request validation (400), the happy-path response shape, and the authorization boundary — that one account cannot reach another's data. If the project gates routes on anything further (feature flags, roles, plan tiers), cover the closed gate too, and assert the status code it actually returns rather than the one that seems right.
71
+
72
+ **Prereqs:** a local server running against a reachable database, a durable test account, and whatever credential the auth middleware expects (`TEST_API_KEY` by convention).
65
73
 
66
- **Prereqs:** local server running, durable test account, valid API key (`TEST_API_KEY` env var), relevant feature flags enabled.
74
+ ## Ad-Hoc Testing Tools
67
75
 
68
- ## CLI Tools for Ad-Hoc Testing
76
+ Jest is what runs in CI and before a commit. Alongside it, projects grow small CLIs for the things a test suite is the wrong shape for: generating fixture data, poking a live local server, checking what actually landed in the database.
69
77
 
70
- | Tool | Purpose |
71
- |---|---|
72
- | `npm run tester -- <cmd>` | Exploratory smoke tests, fixture data generation (`crawl`, `access-logs-generate`, `s3-raw-smoke`, `user-agent`, etc.) |
73
- | `npm run admin -- <cmd>` | Entity/data operations (accounts, agents, credentials, CH export/replicate) |
78
+ Boop scaffolds one — `npm run db:query` (database capability) — for reading the local database without opening Prisma Studio. Anything beyond that is project-grown; `package.json` scripts and `src/scripts/` are where to look for what yours has, and `--help` on each is faster than reading its source.
74
79
 
75
- Run `--help` on either for full command list. These are not automated or part of pre-commit — use Jest for that.
80
+ These are exploratory aids. They are not part of the automated suite, and none of them substitutes for a Jest test that can go red on its own.
76
81
 
77
82
  ## References
78
83
 
79
- - [tests/helpers.ts](../tests/helpers.ts) — shared test utilities and entity factories
80
- - [src/tester.ts](../src/tester.ts) — ad-hoc CLI (`npm run tester -- help`)
81
- - [src/admin.ts](../src/admin.ts) — entity management CLI (`npm run admin -- help`)
82
- - [jest.config.js](../jest.config.js) — Jest configuration
84
+ - [`boop-testing-testing`](../boop-testing-testing/SKILL.md) — testing philosophy and the generic approach
85
+ - [`boop-testing-tdd-light`](../boop-testing-tdd-light/SKILL.md) — the red/green loop for a single change
86
+ - The project's own shared test setup under `tests/` — read it before adding another helper
87
+ - `jest.config.js` at the project root — test discovery, transform, and match config