@boopkit/cap-testing 0.1.0
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/capability.json +13 -0
- package/package.json +20 -0
- package/scaffold/jest.config.js +9 -0
- package/scaffold/tests/app.test.ts +17 -0
- package/scaffold/tsconfig.test.json +5 -0
- package/skills/test.tdd-light.skill.md +10 -0
- package/skills/test.testing-express-web.skill.md +77 -0
- package/skills/test.testing.skill.md +79 -0
package/capability.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "testing",
|
|
3
|
+
"description": "Jest testing setup",
|
|
4
|
+
"type": "development",
|
|
5
|
+
"dependencies": [],
|
|
6
|
+
"devDependencies": ["jest", "ts-jest", "@types/jest"],
|
|
7
|
+
"files": ["jest.config.js", "tsconfig.test.json", "tests/"],
|
|
8
|
+
"skills": ["test.tdd-light.skill.md", "test.testing-express-web.skill.md", "test.testing.skill.md"],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": { "cmd": "jest --runInBand", "description": "Run all tests" },
|
|
11
|
+
"test:watch": { "cmd": "jest --watch", "description": "Run tests in watch mode" }
|
|
12
|
+
}
|
|
13
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@boopkit/cap-testing",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Jest testing setup and testing skills for AI agents",
|
|
5
|
+
"files": [
|
|
6
|
+
"scaffold/",
|
|
7
|
+
"skills/",
|
|
8
|
+
"capability.json"
|
|
9
|
+
],
|
|
10
|
+
"boopkit": {
|
|
11
|
+
"capability": true,
|
|
12
|
+
"scaffoldDir": "scaffold",
|
|
13
|
+
"skillsDir": "skills",
|
|
14
|
+
"manifest": "capability.json"
|
|
15
|
+
},
|
|
16
|
+
"license": "ISC",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import app from '../src/app';
|
|
2
|
+
|
|
3
|
+
describe('app', () => {
|
|
4
|
+
it('exports a valid Express application', () => {
|
|
5
|
+
expect(app).toBeDefined();
|
|
6
|
+
expect(typeof app.get).toBe('function');
|
|
7
|
+
expect(typeof app.post).toBe('function');
|
|
8
|
+
expect(typeof app.use).toBe('function');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('has JSON and URL-encoded body parsing enabled', () => {
|
|
12
|
+
const stack = (app as any).router?.stack ?? (app as any)._router?.stack ?? [];
|
|
13
|
+
const layerNames = stack.map((layer: any) => layer.name);
|
|
14
|
+
expect(layerNames).toContain('jsonParser');
|
|
15
|
+
expect(layerNames).toContain('urlencodedParser');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Light TDD Flow
|
|
2
|
+
When you need to build features or implement changes, follow the Light TDD Flow:
|
|
3
|
+
|
|
4
|
+
Red → Green → Refactor. Document "before" and "after" states.
|
|
5
|
+
|
|
6
|
+
1. **Red**: Run/write tests that expose the gap (failing test for new feature, reproduced bug, baseline metric). Verify that issues is *observed* and that tests fail for the expected reason. Document the "before" state.
|
|
7
|
+
2. **Green**: Implement the minimum change. Add any additional tests.
|
|
8
|
+
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.
|
|
9
|
+
|
|
10
|
+
Test tools: `npm run test` (Jest), `npm run tester` (CLI — see `npm run tester -- help`). For UI changes, coordinate manual browser testing.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Testing: Express App (Web & APIs)
|
|
2
|
+
|
|
3
|
+
When you need to write or run tests for the Express server — services, controllers, middleware, and HTTP routes.
|
|
4
|
+
|
|
5
|
+
Parent skill: [testing.skill.md](testing.skill.md) (philosophy, generic approach)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Running Tests
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm test # Full suite
|
|
13
|
+
npx jest tests/services/foo.test.ts --no-coverage # Single file
|
|
14
|
+
npx jest tests/services/foo --no-coverage # Pattern match
|
|
15
|
+
npx jest tests/services/foo.test.ts -t "handles replay" # By test name
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Prerequisites**: local Postgres, ClickHouse, and AWS credentials must be available. Integration tests use real services via `getServices()`.
|
|
19
|
+
|
|
20
|
+
## Test File Layout
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
tests/
|
|
24
|
+
helpers.ts # getServices(), TestEntityManager, factories
|
|
25
|
+
services/
|
|
26
|
+
foo.test.ts # Unit tests (mocked deps)
|
|
27
|
+
foo.integration.test.ts # Integration tests (real services)
|
|
28
|
+
controllers/
|
|
29
|
+
walletAPI.test.ts # HTTP route tests
|
|
30
|
+
middleware/
|
|
31
|
+
utils/
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- `.test.ts` for Jest auto-discovery; `.integration.test.ts` when `jest.mock` conflicts with `getServices()` in the same file
|
|
35
|
+
- Group by domain: `services/`, `controllers/`, `middleware/`
|
|
36
|
+
|
|
37
|
+
## Test Helpers — [tests/helpers.ts](../tests/helpers.ts)
|
|
38
|
+
- `getServices()` — real services for integration tests
|
|
39
|
+
- `TestEntityManager` — tracks entities, cleans up in `afterEach`
|
|
40
|
+
- Factory helpers (`createTestAccount`, `createTestDomain`, etc.) — use instead of raw Prisma
|
|
41
|
+
|
|
42
|
+
## Integration Test Data Isolation
|
|
43
|
+
- Unique prefixes (`_test/${Date.now()}`) so parallel runs don't collide
|
|
44
|
+
- Track created resources for cleanup in `afterAll`; wrap each cleanup in `try/catch`
|
|
45
|
+
- For ClickHouse: use test-specific `account_id` / `hostname` (row deletes are expensive with RMT)
|
|
46
|
+
|
|
47
|
+
## HTTP Route Tests
|
|
48
|
+
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).
|
|
49
|
+
|
|
50
|
+
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.
|
|
51
|
+
|
|
52
|
+
**Setup:**
|
|
53
|
+
```typescript
|
|
54
|
+
const BASE_URL = process.env.TEST_BASE_URL || 'http://localhost:3000';
|
|
55
|
+
const TEST_API_KEY = process.env.TEST_API_KEY; // npm run admin -- key-generate
|
|
56
|
+
const authHeaders = { 'Authorization': `Bearer ${TEST_API_KEY}`, 'Content-Type': 'application/json' };
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**What to cover:** auth rejection (401/403), feature-flag gates (404), request validation (400), happy-path response shape, cross-account access checks.
|
|
60
|
+
|
|
61
|
+
**Prereqs:** local server running, durable test account, valid API key (`TEST_API_KEY` env var), relevant feature flags enabled.
|
|
62
|
+
|
|
63
|
+
## CLI Tools for Ad-Hoc Testing
|
|
64
|
+
|
|
65
|
+
| Tool | Purpose |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `npm run tester -- <cmd>` | Exploratory smoke tests, fixture data generation (`crawl`, `access-logs-generate`, `s3-raw-smoke`, `user-agent`, etc.) |
|
|
68
|
+
| `npm run admin -- <cmd>` | Entity/data operations (accounts, agents, credentials, CH export/replicate) |
|
|
69
|
+
|
|
70
|
+
Run `--help` on either for full command list. These are not automated or part of pre-commit — use Jest for that.
|
|
71
|
+
|
|
72
|
+
## References
|
|
73
|
+
|
|
74
|
+
- [tests/helpers.ts](../tests/helpers.ts) — shared test utilities and entity factories
|
|
75
|
+
- [src/tester.ts](../src/tester.ts) — ad-hoc CLI (`npm run tester -- help`)
|
|
76
|
+
- [src/admin.ts](../src/admin.ts) — entity management CLI (`npm run admin -- help`)
|
|
77
|
+
- [jest.config.js](../jest.config.js) — Jest configuration
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
When you need to write, run, or reason about tests in this project.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
**Functional testing, not ceremony.** Tests prove code works — not that mocks were wired correctly. Every test should exercise real logic. Type-checking or compilation alone is never sufficient. Be pessimistic about your assumptions: if you think "this should never fail," write a test that verifies it doesn't.
|
|
8
|
+
|
|
9
|
+
- **Observe the failure first.** A plausible explanation for repeated failures is still a hypothesis until you see success. Always verify the expected *positive* outcome actually occurs (e.g., `cache_hit: true`, not just "no errors").
|
|
10
|
+
- **Mocks are a last resort**, acceptable only for genuinely external dependencies (third-party HTTP, email providers) or to isolate complex decision logic (error classification, retry semantics). If it doesn't hit a real store, call it a unit test — not an "integration test with mocks."
|
|
11
|
+
- **Data-driven coverage for config tables.** Code backed by configuration (pattern tables, route maps, enum mappings) must have tests that exercise *every entry*, not a hand-picked subset. Include a count sanity check (`expect(records.length).toBeGreaterThan(N)`) so an empty response doesn't silently pass.
|
|
12
|
+
- **Undocumented assumptions are spec gaps, not test targets.** When the spec is silent on a behavior (e.g., can a user have multiple sessions? does logout delete or expire state?), that's a design decision to resolve — not something to test around. **Resolve the ambiguity first** (update the spec or get a decision), **then** write the test encoding the decided behavior. If you're writing tests before code (TDD), a spec gap blocks you — don't guess the intent and test your guess. If you discover the gap while reviewing existing code, file a spec issue and name the test to surface the question (e.g., `'login creates additional session token — multi-device support'` not `'login works'`).
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Generic Testing Approach
|
|
17
|
+
|
|
18
|
+
These principles apply across all subsystems.
|
|
19
|
+
|
|
20
|
+
### Unit tests
|
|
21
|
+
Test **decision logic** independent of real stores: pure functions, orchestration branching, error classification, algorithm correctness.
|
|
22
|
+
|
|
23
|
+
Mock minimally — just enough to drive the code path under test.
|
|
24
|
+
|
|
25
|
+
### Refactor for testability
|
|
26
|
+
When a function mixes I/O with logic (reading a file *and* parsing its contents, fetching data *and* transforming it), split it into:
|
|
27
|
+
- A **pure function** that accepts pre-parsed data and returns a result — directly testable with plain objects, no mocks.
|
|
28
|
+
- A **thin I/O wrapper** that reads/writes and delegates to the pure function.
|
|
29
|
+
|
|
30
|
+
Signs you need this split:
|
|
31
|
+
- Tests require mocking `fs`, `fetch`, or other I/O just to reach the logic you actually want to verify.
|
|
32
|
+
- The mock setup is longer than the assertions.
|
|
33
|
+
- Small changes to serialization format require rewriting mock plumbing.
|
|
34
|
+
|
|
35
|
+
The pure function tests are fast, deterministic, and mock-free. The I/O wrapper is trivial enough to cover via integration tests or manual verification.
|
|
36
|
+
|
|
37
|
+
### Integration tests
|
|
38
|
+
Test that **data survives real boundaries** — between components, across serialization, through schema constraints and multi-step lifecycles.
|
|
39
|
+
|
|
40
|
+
**Principle: test pairwise producer/consumer contracts.** When component A produces output that component B consumes, write a test that runs A then feeds its real output into B. Don't test them only in isolation with hand-crafted fixtures — the fixture may not match what A actually produces.
|
|
41
|
+
|
|
42
|
+
Two common shapes:
|
|
43
|
+
|
|
44
|
+
- **Round-trip fidelity.** Write data through one path, read it back through another. *Example:* insert a row via the ORM, query it via the API, assert the response matches. Key question: *could this break because of how the database/service handles the data?* If yes, hit the real dependency.
|
|
45
|
+
- **Writer/reader handoff.** One command writes artifacts (files, config, state) that another command interprets. *Example:* `init` writes a project manifest, `status` reads it to determine setup state — test that status correctly interprets init's actual output, not a synthetic fixture. Key question: *could these disagree about the shape or meaning of the shared artifact?* If yes, wire them together in a test.
|
|
46
|
+
|
|
47
|
+
When the shared boundary is the filesystem or an in-memory data structure (CLI tools, config pipelines), these tests are cheap and deterministic — no mocks, no external services. Prefer them over testing each side in isolation whenever feasible.
|
|
48
|
+
|
|
49
|
+
### Mock boundaries
|
|
50
|
+
|
|
51
|
+
| Mock this | Don't mock this |
|
|
52
|
+
|---|---|
|
|
53
|
+
| Third-party HTTP (email, OAuth) | Adjacent services you own (use real `getServices()`) |
|
|
54
|
+
| `Date.now()` for deterministic assertions | Database queries (use real Postgres/CH) |
|
|
55
|
+
| Complex branching inputs | The service under test's own methods |
|
|
56
|
+
|
|
57
|
+
### Data-driven config coverage
|
|
58
|
+
When code is backed by a config table (patterns, routes, flags, enums):
|
|
59
|
+
- Load the full config at test time from the real source
|
|
60
|
+
- Iterate every entry and assert expected output
|
|
61
|
+
- Collect all failures (don't fail-fast on first broken entry)
|
|
62
|
+
- Sanity-check the count: `expect(records.length).toBeGreaterThan(N)`
|
|
63
|
+
- Skip only when each entry triggers expensive metered side effects; document why
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Subsystem Testing Skills
|
|
68
|
+
|
|
69
|
+
| Subsystem | Skill |
|
|
70
|
+
|---|---|
|
|
71
|
+
| Express app (web & APIs) | [testing-express-web.skill.md](testing-express-web.skill.md) — Jest, HTTP route tests, CLI tools, test helpers |
|
|
72
|
+
| Cloudflare edge workers | [testing-cf-workers.skill.md](testing-cf-workers.skill.md) — Worker unit/integration tests, wrangler dev setup |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## References
|
|
77
|
+
|
|
78
|
+
- [development.skill.md](development.skill.md) — Light TDD Flow, completion criteria
|
|
79
|
+
- [jest.config.js](../jest.config.js) — Jest configuration
|