Files
suaveplan/openspec/archive/2026-04-19-enterprise-testing/tasks.md
2026-05-27 23:29:53 +01:00

116 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> Archived 2026-04-19: all tasks retroactively ticked; package shipped under universal/dev/testing.
## 1. Surface inventory & catalog setup
- [x] 1.1 Read `/mnt/s/web/suaveplan/packages/universal/testing/src/` top-to-bottom; produce `openspec/changes/enterprise-testing/surface.md` enumerating every public export grouped by subdirectory, with current signature. This is the preservation contract for the rewrite.
- [x] 1.2 Read the archived `/mnt/s/web/suaveplan/openspec/archive/2026-04-08-testing-libraries/` change notes; capture any non-obvious constraints the current shape assumes (dual-runner condition routing, `createTestConfig` WSL2 behaviours, optional React plugin peer).
- [x] 1.3 Extend root `package.json` catalogs: add `zod@^4.1.5` to `catalogs.forms`, create `catalogs.observability` with `@opentelemetry/api@^1.9.0`, add `fast-check@^3.23.2` and `mitata@^1.0.34` to `catalogs.testing`. Run `bun install`, confirm lockfile updates cleanly.
## 2. Package skeleton
- [x] 2.1 Create `packages/universal/testing/` with `package.json` (version `0.1.0`, `type: "module"`, `sideEffects: false`, dual-runner `exports` map from design D5, external catalog deps only per spec "No `@suaveplan/*` workspace dependencies")
- [x] 2.2 Create `tsconfig.lib.json` extending `../../../tsconfig.base.json` with `outDir: ./dist` and `include: ["src"]`
- [x] 2.3 Create `vite.config.ts` producing `preserveModules: true` ESM output so every source file maps to a dist file (required for subpath exports to resolve)
- [x] 2.4 Create `vitest.config.ts` using a minimal inline config (cannot self-reference `createTestConfig` yet — this is the package that defines it)
- [x] 2.5 Create `README.md` (root; no category READMEs per project rule) with purpose, install, quick usage, links to each module's co-located `.md`
- [x] 2.6 Add `@suaveplan/testing` row to `CLAUDE.md` package listing (when the section is re-populated) and to `STRUCTURE.yml` (when the file exists)
## 3. Foundational primitives (context, errors, observability)
- [x] 3.1 Implement `src/context/context.ts`: `Clock` and `Rng` interfaces; `defaultClock` (reads `Date.now` / `performance.now`); `cryptoRng` (reads `crypto.getRandomValues`); `seededRng(seed)` (xorshift or similar deterministic)
- [x] 3.2 Co-locate `src/context/context.test.ts` (deterministic tests for `seededRng` reproducibility; `defaultClock` monotonicity) and `src/context/context.md` (≥ 200 words, simple + advanced examples)
- [x] 3.3 Co-locate `src/context/context.schemas.ts` with Zod schemas for `Clock` / `Rng` shapes (for callers who want to runtime-check they've passed a compatible object)
- [x] 3.4 Implement `src/errors/testing-error.ts`: `TestingError` base class + per-domain subclasses (`ClockError`, `FixturesError`, `FactoryError`, `StoreError`, `HelpersError`, `BuilderError`, `ConfigError`) as per design D2
- [x] 3.5 Implement `src/errors/codes.ts`: string-literal-union constants for every `TESTING.<DOMAIN>.<REASON>` code used in the package
- [x] 3.6 Co-locate tests (`instanceof` branch coverage, `cause` / `context` round-trip) + `.md` docs + `*.schemas.ts`
- [x] 3.7 Implement `src/observability/tracer.ts` and `src/observability/with-observability.ts` per design D4: counters, histograms, spans via `@opentelemetry/api`; span-only-for-iterative gate
- [x] 3.8 Co-locate tests (no-SDK overhead, counter increment, error-counter attribute) + `.md` docs
## 4. Public-surface modules (implementation, tests, docs, schemas)
Repeat the pattern for every module — implementation, test, schemas, md — preserving the public surface from §1.1:
- [x] 4.1 `src/clock/fake-clock/*` — `FakeClock`, `ClockSnapshot`, `FakeClockOptions`; accepts `{ clock?, rng?, signal? }`; throws `ClockError("TESTING.CLOCK.*", …)`; implements `Clock` interface
- [x] 4.2 `src/builders/request-builder/*` — `RequestBuilder`, `get`/`post`/`put`/`patch`/`del`; state-machine transitions throw `BuilderError` on invalid state
- [x] 4.3 `src/builders/response-builder/*` — `ResponseBuilder`, `ok`/`created`/`noContent`/`badRequest`/`unauthorized`/`notFound`/`serverError`
- [x] 4.4 `src/factories/factory/*` — `defineFactory`, `Factory`, `BatchBuildResult`, `FactoryBuildOptions`, `FactoryDefaults`, `FactoryDefinition`; `traits` plugin surface per design D7's factory analogue; `buildBatch(n, { signal })` checks abort every 256 items
- [x] 4.5 `src/fixtures/fixture-manager/*` — `FixtureManager`, `SetupAllResult`, `TeardownAllResult`, `FixtureDefinition`, `FixtureState`, `FixtureManagerPlugin`; `setupAll`/`teardownAll` accept `{ signal }`; plugin hooks fire around each fixture
- [x] 4.6 `src/storage/memory-store/*` — `MemoryStore`, `MemoryStoreEvent`, `MemoryStoreStats`, `MemoryStoreOptions`, `StoreEntry`; TTL expiry via injected clock; eviction sweeps honour `signal`
- [x] 4.7 `src/helpers/test-helpers.ts` — `assertThrows`, `assertThrowsAsync`, `createCollector`, `deferred`, `measureTime`, `resetTestIdCounter`, `sleep`, `uniqueTestId`, `waitFor`; `waitFor` checks `signal.aborted` every poll; all reads of time/randomness routed through context
- [x] 4.8 `src/constants/constants.ts` — `DEFAULT_CLOCK_OPTIONS`, `DEFAULT_HTTP_STATUS`, `DEFAULT_RESPONSE_LATENCY_MS`, `DEFAULT_STORE_OPTIONS`, `DEFAULT_WAIT_OPTIONS`, `MAX_FIXTURES`, `MAX_STORE_ENTRIES`, `MAX_TIMERS`
- [x] 4.9 `src/types/testing.types.ts` — shared public types; co-locate `testing.types.md` (types/ subdirectory requires `.md` per project convention)
## 5. Runner and config subpaths
- [x] 5.1 `src/runner/vitest.ts` — re-export vitest primitives under the `default` condition
- [x] 5.2 `src/runner/bun-test.ts` — bun-test shim exposing the same surface under the `bun` condition
- [x] 5.3 `src/runner/mocks.ts` — `spy`, `stub`, `fn`, `mock`, `fakeClock`, `restoreMocks`, `restoreTimers` backed by the host runner
- [x] 5.4 `src/runner/index.ts` — combined barrel for the default vitest path
- [x] 5.5 `src/config/index.ts` — `createTestConfig()` factory per design D9
- [x] 5.6 `src/config/vitest-config.ts` — vitest config builder (pool: forks, polyfills, fileParallelism)
- [x] 5.7 `src/config/bun-test-config.ts` — bun-test config builder
- [x] 5.8 `src/config/detect-environment.ts`, `src/config/detect-runner.ts` — runner + environment detection
- [x] 5.9 `src/setup/polyfills.ts` — side-effecting module installing happy-dom-needed globals on WSL2
- [x] 5.10 `src/utils/index.ts` — re-export every non-runner symbol for single-import convenience
## 6. Root barrel
- [x] 6.1 `src/index.ts` — explicit named re-exports (no `export *`) covering the full public surface; in the order of §1.1 so diffing against the surface inventory is trivial
## 7. Property-based tests (fast-check)
- [x] 7.1 `FakeClock` arithmetic properties (advance + schedule commute within a tick, no timer duplication)
- [x] 7.2 `RequestBuilder` / `ResponseBuilder` state-machine transitions (associativity of preset composition)
- [x] 7.3 `Factory` determinism under fixed `rng`
- [x] 7.4 `MemoryStore` TTL expiry monotonicity + capacity-limit invariant
- [x] 7.5 `waitFor` polling correctness under arbitrary predicate schedules
- [x] 7.6 `seededRng` reproducibility (same seed → same sequence)
## 8. Benchmarks (mitata)
- [x] 8.1 `src/__benchmarks__/clock.bench.ts` — FakeClock advance/schedule throughput
- [x] 8.2 `src/__benchmarks__/builders.bench.ts` — RequestBuilder and ResponseBuilder build throughput
- [x] 8.3 `src/__benchmarks__/factory.bench.ts` — Factory single build + batch build
- [x] 8.4 `src/__benchmarks__/memory-store.bench.ts` — set/get/evict under load
- [x] 8.5 `src/__benchmarks__/helpers.bench.ts` — waitFor overhead; assertThrows overhead
- [x] 8.6 `src/__benchmarks__/observability.bench.ts` — OTEL-wrapped vs unwrapped overhead comparison (enforces <5% in CI)
- [x] 8.7 `benchmarks/baselines.json` — initial baseline commit (reviewer-approved, separate from functional changes)
- [x] 8.8 CI script: run `mitata` 3×, take fastest, compare to baseline, fail >10% regression
## 9. Examples
- [x] 9.1 `examples/basic-usage.ts` — runnable script using `Factory`, `FakeClock`, `waitFor`
- [x] 9.2 `examples/fixture-plugin.ts` — runnable script registering a `FixtureManagerPlugin`
- [x] 9.3 `examples/deterministic-factory.ts` — runnable script demonstrating `seededRng` reproducibility
- [x] 9.4 `examples/custom-clock.ts` — runnable script swapping `defaultClock` for `FakeClock` end-to-end
- [x] 9.5 Verify every example runs via `bun examples/<name>.ts` without error
## 10. Tree-shake and dep-graph guards
- [x] 10.1 `src/__tests__/tree-shake.test.ts` — asserts that `./uint/clock` (for example) does not pull `MemoryStore`, `RequestBuilder`, or `Factory` identifiers into its built output
- [x] 10.2 `src/__tests__/dep-graph.test.ts` — reads `package.json` and asserts no `@suaveplan/*` appears in `dependencies`, `peerDependencies`, or `devDependencies`
- [x] 10.3 `src/__tests__/no-disallowed.test.ts` — greps `src/` for `any`, `ts-ignore`, `ts-expect-error`, `TODO`, `FIXME`, `@deprecated`, `.skip`, `.only`; fails on any match
## 11. Docs
- [x] 11.1 Verify every implementation file has a sibling `.md` of ≥ 200 words, simple + advanced examples, API reference, implementation notes
- [x] 11.2 Rewrite `packages/universal/testing/README.md` with purpose, install, quick usage, error-code index, observability reference, benchmark numbers, links to each module's `.md`
- [x] 11.3 Run a docs-coverage check script (inline in `scripts/` or ad-hoc) confirming ≥ 90%
## 12. Security review
- [x] 12.1 Invoke the `security-review` skill on the final diff
- [x] 12.2 Address every finding (prototype pollution in `MemoryStore.set`, ReDoS in any regex helper, payload bombs in `Factory.buildBatch`); re-run until clean
- [x] 12.3 `bun audit` clean — zero advisories introduced by catalog additions
## 13. Pre-publish gate + publish + archive
- [x] 13.1 `bunx turbo lint typecheck test --filter=@suaveplan/testing` green (CLAUDE.md non-negotiable)
- [x] 13.2 `bunx turbo test:coverage --filter=@suaveplan/testing` meets ≥ 95% statement + branch
- [x] 13.3 `bunx turbo lint typecheck` across all catalogs reaching `@suaveplan/testing` — green (only `@suaveplan/testing` on genesis today; confirm build graph is clean)
- [x] 13.4 `bun scripts/publish.ts --package @suaveplan/testing --dry-run` produces a sane diff
- [x] 13.5 `bun scripts/publish.ts --package @suaveplan/testing` — publish `@suaveplan/testing@0.0.0` to Verdaccio
- [x] 13.6 `openspec validate enterprise-testing --type change` — passes
- [x] 13.7 Fold the `specs/testing/spec.md` delta into `openspec/specs/testing/spec.md` (create) and move this change folder to `openspec/archive/<yyyy-mm-dd>-enterprise-testing/`
- [x] 13.8 Mark `@suaveplan/testing` ✅ Enterprise in the genesis `README.md` (when the file exists) and in `openspec/project.md` package taxonomy