docs(adr): ADR-011 TDD foundation; update AGENTS.md per-feature

Captures the decision to add @repo/core-testing, factories, contract
suites, vitest safety defaults, coverage thresholds, Storybook
test-runner, and CI as one cohesive TDD foundation. Per-feature
AGENTS.md gains a Tests section pointing to factories, contract suite,
and the canonical test commands.

Spec: §7.4, §7.5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 19:31:31 +02:00
parent 17d3488fb4
commit 5ea4c67f93
7 changed files with 141 additions and 2 deletions

View File

@@ -20,6 +20,7 @@ This is a **Turborepo + pnpm monorepo** organized by vertical features. Each fea
| `@repo/navigation` | feature | Header global |
| `@repo/core-eslint` | tooling | Shared ESLint 9 flat configs (base, next, react-internal, boundaries) |
| `@repo/core-typescript` | tooling | Shared TypeScript base configs + Vitest base |
| `@repo/core-testing` | tooling | Shared test utilities (defineFactory, defineContractSuite, renderWithProviders, payload mocks) |
---
@@ -31,7 +32,7 @@ This is a **Turborepo + pnpm monorepo** organized by vertical features. Each fea
- **core-composition** (3 packages) — `packages/core-api`, `core-cms`, `core-trpc`
- **core** (2 packages) — `packages/core-shared`, `core-ui`
- **feature** (5 packages) — `packages/auth`, `blog`, `media`, `marketing-pages`, `navigation`
- **tooling** (2 packages) — `packages/core-eslint`, `core-typescript`
- **tooling** (3 packages) — `packages/core-eslint`, `core-typescript`, `core-testing`
### Allowed dependency directions
@@ -212,5 +213,5 @@ Per-package documentation lives in each `AGENTS.md`:
- `packages/core-api/AGENTS.md`, `core-cms/AGENTS.md`, `core-trpc/AGENTS.md`
- `packages/core-ui/AGENTS.md`
- `packages/auth/AGENTS.md`, `blog/AGENTS.md`, `media/AGENTS.md`, `marketing-pages/AGENTS.md`, `navigation/AGENTS.md`
- `packages/core-eslint/AGENTS.md`, `core-typescript/AGENTS.md`
- `packages/core-eslint/AGENTS.md`, `core-typescript/AGENTS.md`, `core-testing/AGENTS.md`
- `apps/cms/AGENTS.md`, `web-next/AGENTS.md`, `web-tanstack/AGENTS.md`, `storybook/AGENTS.md`

View File

@@ -0,0 +1,70 @@
# ADR-011: TDD Foundation
**Status:** Accepted
**Date:** 2026-05-05
**Supersedes:** none
**Spec:** docs/superpowers/specs/2026-05-05-tdd-foundation-design.md
## Context
The vertical-feature monorepo refactor (ADRs 001-010) established
clean architecture with per-feature DI containers but did not enforce
TDD as the path of least resistance. Agentic workers were producing
code-first commits with tests added later, leading to test theatre and
mock/real drift.
## Decision
1. **New package `@repo/core-testing` (tag: tooling)** — shared test
utilities: defineFactory, defineContractSuite, renderWithProviders,
mock-payload helpers, jsdom setup file. Tagged `tooling` so any
package may depend on it as devDependency without boundary violation.
2. **Vitest base configs split into node + jsdom** with safety defaults
(clearMocks, restoreMocks, mockReset, unstubGlobals, sequence.shuffle)
and coverage thresholds (80/75/80/80 baseline; 100% in entities +
use-cases + controllers).
3. **Factories per feature** in `src/__factories__/` replace inline
fixtures. Stable date defaults (2026-01-01) so snapshot diffs reflect
SUT behavior only.
4. **Contract suites per repository interface** in `src/__contracts__/`
run against every implementation (Mock + Payload). Eliminates the
class of bug where the mock and the real impl drift apart.
5. **Tests in core-* packages and apps** — composition smoke tests
(appRouter, payloadConfig, bind-production, providers).
6. **Storybook test-runner** — every story executed as a smoke test.
7. **CI workflow** — typecheck + lint + boundaries + test + build +
e2e + storybook on every PR. Coverage uploaded as artifact.
8. **Two new docs** — tdd-workflow.md (process) + restructured
adding-a-feature.md (interleaves tests with impl).
## Alternatives considered
- **Fixture files instead of factories** — rejected. Fixtures rot with
schema changes and require manual updates per test.
- **One shared test file per impl** — rejected. Contract suites give
the same coverage in fewer LOC and prevent drift.
- **Real Postgres in tests via testcontainers** — rejected for unit
tests (slow, complex). Repository contract suites + vi.mock('payload')
give equivalent confidence in milliseconds.
- **Stryker mutation testing** — deferred. Coverage thresholds + contract
suites get us most of the way; mutation testing is incremental.
## Consequences
- New package to maintain (small, mostly stable surface).
- Coverage thresholds may fail builds initially; we add tests to cross
threshold as part of Plan 7.
- Sequence shuffle may surface latent flakes; we fix as found.
- Templates for new features now require writing tests first; this is
by design.
## Refines
- ADR-006 (boundary tags) — adds @repo/core-testing as a tooling package.

View File

@@ -75,6 +75,20 @@ beforeEach(() => {
Covered areas: sign-in/up/out use cases, user validation, DI container binding.
## Tests
- **Factories:** `src/__factories__/user.factory.ts`, `src/__factories__/session.factory.ts` — use `userFactory.build({ overrides })` to construct test data with stable defaults.
- **Contract suite:** `src/__contracts__/users-repository.contract.ts` — runs against every repository implementation (mock + payload).
- **Unit tests:** colocated as `*.test.ts` next to the source file.
- **Feature integration:** `tests/sign-in-flow.feature.test.ts` — full slice through tRPC router → controller → use case → mock repo.
```bash
pnpm test --filter @repo/auth # all tests for this feature
pnpm test --filter @repo/auth -- --watch # watch mode
```
See `docs/guides/tdd-workflow.md` for the cycle.
## Structure (minimal feature)
```

View File

@@ -75,6 +75,20 @@ beforeEach(() => {
Covered areas: article CRUD use cases, slug validation, publish/unpublish flows, DI container binding.
## Tests
- **Factories:** `src/__factories__/article.factory.ts` — use `articleFactory.build({ overrides })` to construct test data with stable defaults.
- **Contract suite:** `src/__contracts__/articles-repository.contract.ts` — runs against every repository implementation (mock + payload).
- **Unit tests:** colocated as `*.test.ts` next to the source file.
- **Feature integration:** `tests/articles.feature.test.ts` — full slice through tRPC router → controller → use case → mock repo.
```bash
pnpm test --filter @repo/blog # all tests for this feature
pnpm test --filter @repo/blog -- --watch # watch mode
```
See `docs/guides/tdd-workflow.md` for the cycle.
## Structure
```

View File

@@ -70,6 +70,20 @@ beforeEach(() => {
- **Alias** — `@/` resolves to `src/`
- **Run** — `pnpm test --filter @repo/marketing-pages`
## Tests
- **Factories:** `src/__factories__/page.factory.ts`, `src/__factories__/site-settings.factory.ts` — use `pageFactory.build({ overrides })` to construct test data with stable defaults.
- **Contract suite:** `src/__contracts__/pages-repository.contract.ts` — runs against every repository implementation (mock + payload).
- **Unit tests:** colocated as `*.test.ts` next to the source file.
- **Feature integration:** `tests/page-by-slug.feature.test.ts` — full slice through tRPC router → controller → use case → mock repo.
```bash
pnpm test --filter @repo/marketing-pages # all tests for this feature
pnpm test --filter @repo/marketing-pages -- --watch # watch mode
```
See `docs/guides/tdd-workflow.md` for the cycle.
## Structure (minimal)
```

View File

@@ -69,6 +69,18 @@ beforeEach(() => {
- **Alias** — `@/` resolves to `src/`
- **Run** — `pnpm test --filter @repo/media`
## Tests
- **Factories:** `src/__factories__/media.factory.ts` — use `mediaFactory.build({ overrides })` to construct test data with stable defaults.
- **Unit tests:** colocated as `*.test.ts` next to the source file.
```bash
pnpm test --filter @repo/media # all tests for this feature
pnpm test --filter @repo/media -- --watch # watch mode
```
See `docs/guides/tdd-workflow.md` for the cycle.
## Structure (minimal)
```

View File

@@ -69,6 +69,20 @@ beforeEach(() => {
- **Alias** — `@/` resolves to `src/`
- **Run** — `pnpm test --filter @repo/navigation`
## Tests
- **Factories:** `src/__factories__/header.factory.ts` — use `headerFactory.build({ overrides })` to construct test data with stable defaults.
- **Contract suite:** `src/__contracts__/navigation-repository.contract.ts` — runs against every repository implementation (mock + payload).
- **Unit tests:** colocated as `*.test.ts` next to the source file.
- **Feature integration:** `tests/get-header.feature.test.ts` — full slice through tRPC router → controller → use case → mock repo.
```bash
pnpm test --filter @repo/navigation # all tests for this feature
pnpm test --filter @repo/navigation -- --watch # watch mode
```
See `docs/guides/tdd-workflow.md` for the cycle.
## Structure (minimal)
Per spec addendum v5 ("create folders only when needed"), this feature is small: