5.5 KiB
Capability Spec Delta: testing-integration (testing-integration-build-fix)
Package: @suaveplan/testing-integration
Status: PROPOSED
ADDED Requirements
Requirement: globalSetup MUST resolve to a real on-disk file
createIntegrationSetup() returns an IntegrationSetupResult.globalSetup: string value. After bun run build, this string MUST be an absolute filesystem path that exists on disk and that vitest can load as a module. It MUST NOT contain a data: URL, an import.meta.url-derived expression that survives bundling, or any other URL scheme that Vitest cannot pass to import().
The bug this requirement guards against: Vite library mode treats new URL("../setup/global-setup.js", import.meta.url).pathname as an asset reference and inlines the source file as a data:video/mp2t;base64,<…> URL during vite build. The published artifact at dist/internal/create-integration-setup-internal.js then literally returns "data:video/mp2t;base64,…" as the globalSetup field. Vitest crashes at startup with ERR_LOAD_URL: Failed to load url <consumer-cwd>/video/mp2t;base64,… and no consumer can use the public API.
The fix path declared by testing-integration-build-fix is to add src/setup/global-setup.ts as a second lib.entry in vite.config.ts (with output.entryFileNames: "[name].js" so dist/setup/global-setup.js is actually emitted) and to replace the asset-intercepted new URL(...).pathname expression in src/internal/create-integration-setup-internal.ts with a Vite-safe alternative — path.join(fileURLToPath(import.meta.url), "..", "..", "setup", "global-setup.js") — that Vite does not transform.
Scenario: globalSetup is an existing file path after build
- WHEN the package is built and
createIntegrationSetup({ x: stubFixture }).globalSetupis read - THEN the returned string is an absolute path AND
existsSync(globalSetup) === true
Scenario: globalSetup is not a data: URL
- WHEN the package is built and
createIntegrationSetup({ x: stubFixture }).globalSetupis read - THEN the returned string does not start with
data:,blob:, or any other non-file://URL scheme
Scenario: vitest can load the returned globalSetup
- WHEN a consumer wires
test.globalSetup = [createIntegrationSetup({...}).globalSetup]invitest.config.tsand runsvitest run - THEN the runner reaches the global-setup phase without
ERR_LOAD_URLor anyFailed to load urlerror
Verified by: tests/dist-asserts.test.ts (added by this change) — loads dist/index.js, calls createIntegrationSetup({ x: stubFixture }), asserts existsSync(globalSetup) === true and that the path does not start with data:.
Requirement: createIntegrationSetup public API is preserved
The shape of createIntegrationSetup<TFixtures>(fixtures: TFixtures): IntegrationSetupResult<TFixtures> MUST NOT change as part of this fix. Consumers continue to receive { globalSetup: string; inject: <K extends keyof TFixtures>(key: K) => InferConfig<TFixtures[K]> }. The fix is a build-config and internal-resolution change only; no consumer call site needs to be modified.
Scenario: existing callers compile unchanged
- WHEN an existing consumer's
createIntegrationSetup({ surrealdb: new SurrealDBFixture() })call is type-checked against the post-fix package - THEN the call compiles without modification and
globalSetupandinjectare typed as before
Verified by: src/setup/create-integration-setup.test.ts (existing) AND tests/dist-asserts.test.ts (added by this change).
Requirement: dist-asserts regression test
The package SHALL ship a tests/dist-asserts.test.ts that runs against the BUILT dist/index.js (not the src/ source) and asserts the globalSetup resolution contract above. This test guards against any future refactor that re-introduces an asset-intercepted URL expression in the build output. The test MUST run after build so it exercises the published artifact, not the unbundled source.
Scenario: dist-asserts runs against the built artifact
- WHEN the test suite executes
- THEN the test imports from
../dist/index.js(or the published entry resolved viapackage.jsonexports.".") and not from../src/
Scenario: dist-asserts fails fast when globalSetup is broken
- WHEN
globalSetupis adata:URL or a non-existent path - THEN the test fails with a message naming the actual offending value
Verified by: tests/dist-asserts.test.ts (added by this change).
Requirement: Storage-s3 consumer migrates back to the public API
After this change ships, packages/storage/storage-s3/tests/integration/setup.ts and packages/storage/storage-s3/tests/integration/global-setup.ts MUST be migrated from their current hand-rolled TestEnvironment driver pattern (the rebel pattern that bypasses the broken createIntegrationSetup API) back to the public createIntegrationSetup({ garageS3: new GarageS3Fixture() }) factory. No subsequent testing-integration-* consumer is permitted to ship the rebel pattern.
Scenario: storage-s3 setup uses the public factory
- WHEN
packages/storage/storage-s3/tests/integration/setup.tsis read after this change ships - THEN it imports
createIntegrationSetupfrom@suaveplan/testing-integrationand does not construct aTestEnvironmentdirectly
Verified by: code-review against the migrated setup.ts and global-setup.ts files; the set-partial.integration.test.ts suite continues to pass via bun run test:integration --filter=@suaveplan/storage-s3.