Files
2026-05-27 23:29:53 +01:00

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 }).globalSetup is 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 }).globalSetup is 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] in vitest.config.ts and runs vitest run
  • THEN the runner reaches the global-setup phase without ERR_LOAD_URL or any Failed to load url error

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 globalSetup and inject are 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 via package.json exports.".") and not from ../src/

Scenario: dist-asserts fails fast when globalSetup is broken

  • WHEN globalSetup is a data: 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.ts is read after this change ships
  • THEN it imports createIntegrationSetup from @suaveplan/testing-integration and does not construct a TestEnvironment directly

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.