6.7 KiB
Why
@suaveplan/testing is the shared test infrastructure every other @suaveplan/* package imports from. As Layer 0 in the genesis dependency graph (zero @suaveplan/* workspace deps — rule #9 of openspec/project.md), every package downstream depends on it. Enterprise-quality testing unblocks enterprise quality everywhere else.
The enterprise specification at /mnt/s/web/suaveplan/enterprise/testing.md sets the bar; this change translates that spec into genesis form, preserving the full public surface established in the archived 2026-04-08-testing-libraries change (RequestBuilder, ResponseBuilder, FakeClock, Factory, FixtureManager, MemoryStore, helpers, dual-runner conditional exports, createTestConfig — full inventory in design.md) while upgrading internals to enterprise grade.
This is the first implementation change on the genesis branch. There is no prior genesis code to audit — the "current state" is empty. Design intent comes from the enterprise doc; surface intent comes from the existing implementation on main at /mnt/s/web/suaveplan/packages/universal/testing/src/.
What Changes
All requirements from enterprise/testing.md §3 are carried forward, with four explicit substitutions forced by genesis rule #9 (testing packages may not depend on any @suaveplan/* workspace package). Each substitution is called out inline below.
Carried forward verbatim
- Universal purity — zero browser/Node/React deps in
src/. Runs in Bun, Node, and modern browsers. Enforced by a tree-shake test that inspects the built bundle. - Zod schemas for all public function inputs and outputs, exported from sibling
*.schemas.tsfiles alongside each implementation file. Zod is an external catalog dep (catalog:forms), not routed through@suaveplan/schema-registry. - Property-based tests (fast-check) for pure functions (clock arithmetic, builder composition, factory deterministic output given fixed RNG, memory-store TTL math, semver-ish helpers).
- Deterministic behaviour — no
Math.random()/Date.now()/performance.now()at module scope. Every function that reads time or randomness accepts an optional{ clock?, rng?, signal? }options parameter. Zero-config defaults preserved. - Backpressure & cancellation —
AbortSignalon every iterative / async helper (waitFor,FixtureManager.setupAll/teardownAll,Factory.buildin batch mode,MemoryStoreeviction sweeps), checked at bounded intervals (≤ 256 items or ≤ 1 KB). - Idempotency & retry primitives wired for any helper with network-ish retry semantics (e.g.
waitForpolling). - Feature flags — any risky new behaviour gated behind an options flag with safe defaults.
- Structured errors with stable codes of the form
TESTING.<DOMAIN>.<REASON>(e.g.TESTING.CLOCK.MAX_TIMERS_EXCEEDED,TESTING.FIXTURES.SETUP_FAILED). Every thrown error carries{ code, cause?, context? }. - Benchmarks via
mitata(external catalog dep) undersrc/__benchmarks__/<domain>.bench.ts, baseline inbenchmarks/baselines.json, CI gate at 10% regression floor. - Plugin / extension interface —
FixtureManageraccepts plugins implementingbeforeEach/afterEach/onErrorhooks;Factoryacceptstraitsthat downstream packages can contribute without forking. - Observability surface — counters, histograms, spans for every public entrypoint. Gated: no-op when no OTEL SDK is registered; live when one is. Useful for diagnosing flaky test environments in CI without touching test source.
- Security — input validation at the boundary via Zod, no prototype-pollution vectors in
MemoryStore, dependency audit clean. - Migration story — N/A; genesis starts fresh at version
0.1.0. - Example / story —
examples/directory with runnable usage of each public entrypoint.
Substitutions (enterprise-doc → genesis form)
| Enterprise doc requires | Genesis substitution | Why |
|---|---|---|
Errors extend @suaveplan/error |
Local TestingError extends Error with the same { code, cause, context } shape, stable TESTING.<DOMAIN>.<REASON> codes |
Rule #9 — can't take @suaveplan/* deps. Shape-compatible so a future @suaveplan/error adapter can bridge without API change. |
OTEL via @suaveplan/opentelemetry |
@opentelemetry/api (external, ~2 KB, no transitive Node deps). Spans/counters/histograms created via the standard API, no-op without a registered SDK |
Rule #9. @opentelemetry/api is the canonical public API spec; the internal package will wrap this at a later layer. |
Schemas via @suaveplan/schema-registry |
zod direct (catalog:forms). Schemas exported from *.schemas.ts under stable names (e.g. BuilderSchemas.request.input) so a future schema-registry adapter can enumerate them |
Rule #9. |
Clock via @suaveplan/datetime |
Local Clock interface in src/context/context.ts: { now(): number; hrtime(): bigint }. defaultClock reads Date.now() / performance.now() directly. FakeClock implements the same interface. |
Rule #9. |
i18n via @suaveplan/i18n or hard-coded message keys |
English-only message fields. code is the machine contract; message is developer-facing. Matches the utils D10 precedent. |
Rule #9. Test helpers emit messages to developers in logs, not to end users. |
Public surface preserved
Every export enumerated in design.md §Surface Inventory is retained unchanged in shape. Internals are rewritten; signatures gain the optional { clock?, rng?, signal? } options parameter where appropriate (purely additive — zero-config callers are unaffected).
Capabilities
New Capabilities
testing: shared test infrastructure for the genesis monorepo — test primitives (vitest + bun-test conditional export), mock API, request/response builders, FakeClock, Factory, FixtureManager, MemoryStore, async helpers, vitest config factory, polyfills. Layer 0 leaf.
Modified Capabilities
(none — genesis has no prior spec)
Impact
- Affected code:
/mnt/s/web/genesis/packages/universal/testing/**(fresh implementation). - Affected consumers: none on genesis yet; every subsequent genesis package depends on this one.
- Version:
0.1.0(genesis baseline; no bump until graduation cut). - Catalogs added / extended in root
package.json:zodtocatalogs.forms,@opentelemetry/apito a newcatalogs.observability,fast-checkandmitatatocatalogs.testing. - Root
package.jsondevDependenciesaddition:@types/bunalready present; tooling catalogs unchanged. - New capability spec:
openspec/specs/testing/spec.md(created on archive). - Pre-publish gate: full turbo
lint typecheck teston@suaveplan/testing+ istanbul coverage ≥ 95% beforebun scripts/publish.ts --package @suaveplan/testing.