5.0 KiB
testing-integration: spec authoring + generics hardening
Package: @suaveplan/testing-integration and the six variant fixtures
(@suaveplan/testing-integration-{surrealdb,redis,postgres,mailpit,typesense,garage-s3})
Status: PROPOSED
Type: spec-conformance + refactor
Why
The seven testing-integration* packages shipped without folded specs and with
several latent issues uncovered by the Slice-3 audit:
- Rule 8 violation — every variant listed
@suaveplan/testing-integrationindependencies, notpeerDependencies. The core's API is meant to be brought by the consumer; treating it as a hard dep duplicates copies in usernode_modulesand makes theinstanceof TestEnvironmentErrorchecks fail across realms. vitestruntime import in core, devDep only —create-integration-setup.tsimportedinjectfromvitest. Withvitestonly indevDependencies, an installed-from-registry consumer would load a broken module.- Generic erasure —
TestEnvironment.setup(),HealthPoller.poll(), andskipIfUnavailable()typed their fixture surface asRecord<string, unknown>, throwing away precise per-fixture config types. assertMail()cast-based parsing — the Mailpit response shape wasas { messages?: ... }-cast; a server payload regression silently surfaced as a runtimeTypeErrordeep inm.To.some(...).GarageS3Fixture.waitForHealthreimplementedHealthPoller— divergent timeout semantics + bespoke error type._fixtureRegistryand_createIntegrationSetupWithInjectexported from the public barrel — leading-underscore naming is convention only; nothing prevented downstream from importing them and pinning the package shape.
This change folds the seven packages' contracts into seven openspec/specs/*
files with Verified by: lines on every requirement, fixes the plumbing, and
threads precise generics end-to-end.
What changes
- ADDED:
openspec/specs/testing-integration/spec.md— folded spec for the core. - ADDED:
openspec/specs/testing-integration-{surrealdb,redis,postgres,mailpit,typesense,garage-s3}/spec.md— six folded specs for the variants. - ADDED:
src/internal/subdirectory (NOT inpackage.jsonexports) housingfixtureRegistry,createIntegrationSetupWithInject, andcreateSkipIfUnavailableWithInject. Public barrel no longer re-exports them. - ADDED:
src/environment/test-environment.spec-conformance.test.ts— pins generic threading onTestEnvironment.setup(),HealthPoller.poll(),createIntegrationSetup'sinject(), andskipIfUnavailable(). - MODIFIED:
TestEnvironmentis now generic overTFixtures extends DefaultFixtureMap;setup()returns{ configs: { [K in keyof TFixtures]?: InferConfig<TFixtures[K]> }; failed: string[] }. Defaults preserve compat. - MODIFIED:
HealthPoller.poll()is now generic overTConfigand acceptsPick<ServiceFixture<TConfig>, "name" | "healthCheck">instead of widening toServiceFixture<Record<string, unknown>>. - MODIFIED:
skipIfUnavailable<TName extends string = string>()is now generic over the fixture-name keyset for typed-registry pairing. - MODIFIED:
assertMail()validates Mailpit responses with a Zod schema and throwsTestEnvironmentErroron shape mismatch, non-2xx, or timeout. - MODIFIED:
GarageS3Fixture.waitForHealth()delegates toHealthPoller.poll(this). - MODIFIED: every variant
package.jsonmoves@suaveplan/testing-integrationandtestcontainersfromdependenciestopeerDependencies(and adds them todevDependenciesso workspace symlinks resolve in the variant's own tests). - MODIFIED: core
package.jsonaddsvitesttopeerDependencies. - REMOVED:
_fixtureRegistry,_createIntegrationSetupWithInject, and_createSkipIfUnavailableWithInjectfrom the public barrel. Replaced by the package-privateinternal/modules.
Out of scope
- Adding new fixture variants (Kafka, ClickHouse, etc.) — deferred.
- Changing the Vitest global-setup contract — deferred until Vitest 5 lands.
- Migrating
assertMailto a generic mail-API client — deferred.
Verification approach
Each requirement in every spec carries a **Verified by:** line naming an
existing it(...) title. Every claim about a generic is pinned by an
expectTypeOf assertion in
src/environment/test-environment.spec-conformance.test.ts. Coverage stays at
100/100/100/100 across all seven packages.
Migration impact
- Variant packages (six fixtures) — consumers who imported
_fixtureRegistry/_createIntegrationSetupWithInject/_createSkipIfUnavailableWithInjectfrom@suaveplan/testing-integrationmust migrate to the publiccreateIntegrationSetup/skipIfUnavailableAPI. The leading-underscore symbols were never part of the supported surface (their JSDoc explicitly said "for testing only"); no in-repo consumer imports them. TestEnvironmentandHealthPollercallers — non-breaking; the new generics default to the prior erased contract.