Initial Commit
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# 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-integration` in
|
||||
`dependencies`, not `peerDependencies`. The core's API is meant to be brought
|
||||
by the consumer; treating it as a hard dep duplicates copies in user
|
||||
`node_modules` and makes the `instanceof TestEnvironmentError` checks fail
|
||||
across realms.
|
||||
- **`vitest` runtime import in core, devDep only** — `create-integration-setup.ts`
|
||||
imported `inject` from `vitest`. With `vitest` only in `devDependencies`, an
|
||||
installed-from-registry consumer would load a broken module.
|
||||
- **Generic erasure** — `TestEnvironment.setup()`, `HealthPoller.poll()`, and
|
||||
`skipIfUnavailable()` typed their fixture surface as
|
||||
`Record<string, unknown>`, throwing away precise per-fixture config types.
|
||||
- **`assertMail()` cast-based parsing** — the Mailpit response shape was
|
||||
`as { messages?: ... }`-cast; a server payload regression silently surfaced
|
||||
as a runtime `TypeError` deep in `m.To.some(...)`.
|
||||
- **`GarageS3Fixture.waitForHealth` reimplemented `HealthPoller`** — divergent
|
||||
timeout semantics + bespoke error type.
|
||||
- **`_fixtureRegistry` and `_createIntegrationSetupWithInject` exported 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 in `package.json` `exports`) housing
|
||||
`fixtureRegistry`, `createIntegrationSetupWithInject`, and
|
||||
`createSkipIfUnavailableWithInject`. Public barrel no longer re-exports them.
|
||||
- ADDED: `src/environment/test-environment.spec-conformance.test.ts` — pins
|
||||
generic threading on `TestEnvironment.setup()`, `HealthPoller.poll()`,
|
||||
`createIntegrationSetup`'s `inject()`, and `skipIfUnavailable()`.
|
||||
- MODIFIED: `TestEnvironment` is now generic over `TFixtures extends DefaultFixtureMap`;
|
||||
`setup()` returns `{ configs: { [K in keyof TFixtures]?: InferConfig<TFixtures[K]> }; failed: string[] }`. Defaults preserve compat.
|
||||
- MODIFIED: `HealthPoller.poll()` is now generic over `TConfig` and accepts
|
||||
`Pick<ServiceFixture<TConfig>, "name" | "healthCheck">` instead of widening to
|
||||
`ServiceFixture<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
|
||||
throws `TestEnvironmentError` on shape mismatch, non-2xx, or timeout.
|
||||
- MODIFIED: `GarageS3Fixture.waitForHealth()` delegates to `HealthPoller.poll(this)`.
|
||||
- MODIFIED: every variant `package.json` moves `@suaveplan/testing-integration` and
|
||||
`testcontainers` from `dependencies` to `peerDependencies` (and adds them to
|
||||
`devDependencies` so workspace symlinks resolve in the variant's own tests).
|
||||
- MODIFIED: core `package.json` adds `vitest` to `peerDependencies`.
|
||||
- REMOVED: `_fixtureRegistry`, `_createIntegrationSetupWithInject`, and
|
||||
`_createSkipIfUnavailableWithInject` from the public barrel. Replaced by the
|
||||
package-private `internal/` modules.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Adding new fixture variants (Kafka, ClickHouse, etc.) — deferred.
|
||||
- Changing the Vitest global-setup contract — deferred until Vitest 5 lands.
|
||||
- Migrating `assertMail` to 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` /
|
||||
`_createSkipIfUnavailableWithInject` from `@suaveplan/testing-integration`
|
||||
must migrate to the public `createIntegrationSetup` / `skipIfUnavailable`
|
||||
API. The leading-underscore symbols were never part of the supported surface
|
||||
(their JSDoc explicitly said "for testing only"); no in-repo consumer
|
||||
imports them.
|
||||
- `TestEnvironment` and `HealthPoller` callers — non-breaking; the new generics
|
||||
default to the prior erased contract.
|
||||
Reference in New Issue
Block a user