Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
- Add the TypeORM adapter and preserve errors across rollback and resource release.
- Verify nested fixtures, retries, timeouts, expected failures, and declaration isolation in child processes.
- Include a repository-only NestJS consumer verified outside the workspace against the packed package, covering the real database matrix and handwritten baselines.
- Extract the database test annotation and lifecycle manager into `@integration-testing/data`.
- Extract the database test annotation and lifecycle manager into `@integration-testing/data-isolation`.
- Include optional Prisma and node-postgres rollback adapters.
- Provide independent examples with the published Testcontainers package and without containers.
- Preserve original setup failures when cleanup also fails.
Expand Down
64 changes: 32 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# @integration-testing/data
# @integration-testing/data-isolation

**Write isolated database integration tests with a consistent API across supported test runners and database clients.**

Expand All @@ -21,7 +21,7 @@ Configure your database and test runner once. Then declare each transactional te
the shared context:

```ts
import { declareDataIntegrationTest } from '@integration-testing/data';
import { declareDataIntegrationTest } from '@integration-testing/data-isolation';
import { dataContext } from './data-context.js';

declareDataIntegrationTest();
Expand All @@ -38,14 +38,14 @@ This is the test-file API after setup, using pg and runner globals. The quick st
the context and runner configuration. Prefer annotations? `@DataIntegrationTest` on one marker
class has the same effect as `declareDataIntegrationTest()`; use one form per file.

## Recommended pairing: Testcontainers Integration + Data Integration
## Recommended pairing: Testcontainers Integration + Data Isolation

**Real infrastructure, isolated test data: two tools that work hand in hand.**

Use **[@integration-testing/testcontainers](https://www.npmjs.com/package/@integration-testing/testcontainers)**
to start a disposable PostgreSQL instance and provide its connection URL. Apply your application's
real migrations once, then use **@integration-testing/data** to roll back each test's writes.
Testcontainers Integration stops the infrastructure after the run; Data Integration manages the
real migrations once, then use **@integration-testing/data-isolation** to roll back each test's writes.
Testcontainers Integration stops the infrastructure after the run; Data Isolation manages the
test transactions and its configured clients.

The [Testcontainers Integration repository](https://github.com/RolandSall/testcontainers-integration)
Expand Down Expand Up @@ -94,7 +94,7 @@ not establish SQL Server transaction support.
**Release target: `0.1.0`.** Once this version is available on npm, install it with:

```sh
npm install --save-dev @integration-testing/data@0.1.0
npm install --save-dev @integration-testing/data-isolation@0.1.0
```

Before publication, use a local package archive instead. Build and pack this repository:
Expand All @@ -109,7 +109,7 @@ npm pack ./packages/data --pack-destination .
Copy the resulting archive to your application's `vendor` directory and install it:

```sh
npm install --save-dev ./vendor/integration-testing-data-0.1.0.tgz
npm install --save-dev ./vendor/integration-testing-data-isolation-0.1.0.tgz
```

For the pg quick start, install the driver and TypeScript tooling:
Expand Down Expand Up @@ -149,8 +149,8 @@ exist, merge the relevant options into them.
```ts
// test/data-context.ts
import { Pool } from 'pg';
import { createDataIntegrationTestContext } from '@integration-testing/data';
import { PgTransactionAdapter } from '@integration-testing/data/pg';
import { createDataIntegrationTestContext } from '@integration-testing/data-isolation';
import { PgTransactionAdapter } from '@integration-testing/data-isolation/pg';

export function databaseUrl(): string {
const url = process.env.DATABASE_URL;
Expand Down Expand Up @@ -180,7 +180,7 @@ Export it once and import that same instance from your test files.

```ts
// test/vitest.setup.ts
import { installVitestDataIntegrationTestSupport } from '@integration-testing/data/vitest';
import { installVitestDataIntegrationTestSupport } from '@integration-testing/data-isolation/vitest';
import { dataContext } from './data-context.js';

installVitestDataIntegrationTestSupport(dataContext);
Expand Down Expand Up @@ -241,7 +241,7 @@ runs unchanged under Jest:

```ts
// test/products.integration.test.ts
import { declareDataIntegrationTest } from '@integration-testing/data';
import { declareDataIntegrationTest } from '@integration-testing/data-isolation';
import { dataContext } from './data-context.js';

declareDataIntegrationTest();
Expand Down Expand Up @@ -283,7 +283,7 @@ a test-class instance or register test methods. Continue using your runner's ord
If you prefer annotations, replace the `declareDataIntegrationTest` import and call with:

```ts
import { DataIntegrationTest } from '@integration-testing/data';
import { DataIntegrationTest } from '@integration-testing/data-isolation';

@DataIntegrationTest
export class ProductRepositoryTest {}
Expand Down Expand Up @@ -328,15 +328,15 @@ that client, for example through constructor injection; see [NestJS wiring](#inj

## Configure Jest once

Jest needs **both** the installer and the Data Integration Node environment. The installer
Jest needs **both** the installer and the Data Isolation Node environment. The installer
registers the context inside Jest's test sandbox; the environment connects it to test execution.
The annotation by itself cannot activate this behavior. Keep quick start steps 1 and 3, and use
this section in place of its Vitest setup. The root TypeScript options are shown in the quick
start; select Jest types as described below.

```ts
// test/jest.setup.ts
import { installJestDataIntegrationTestSupport } from '@integration-testing/data/jest';
import { installJestDataIntegrationTestSupport } from '@integration-testing/data-isolation/jest';
import { dataContext } from './data-context.js';

installJestDataIntegrationTestSupport(dataContext);
Expand All @@ -345,7 +345,7 @@ installJestDataIntegrationTestSupport(dataContext);
```js
// jest.integration.config.cjs
module.exports = {
testEnvironment: '@integration-testing/data/jest/environment',
testEnvironment: '@integration-testing/data-isolation/jest/environment',
setupFilesAfterEnv: ['<rootDir>/test/jest.setup.ts'],
testMatch: ['**/test/**/*.integration.test.ts'],
maxWorkers: 2,
Expand Down Expand Up @@ -539,7 +539,7 @@ Replace `test/vitest.setup.ts` with:
import { Container, ContainerResources } from '@integration-testing/testcontainers';
import { CONTAINER_RESOURCES_CONTEXT_KEY } from '@integration-testing/testcontainers/vitest';
import { inject } from 'vitest';
import { installVitestDataIntegrationTestSupport } from '@integration-testing/data/vitest';
import { installVitestDataIntegrationTestSupport } from '@integration-testing/data-isolation/vitest';
import { dataContext } from './data-context.js';

const resources = ContainerResources.fromSerializable(inject(CONTAINER_RESOURCES_CONTEXT_KEY));
Expand Down Expand Up @@ -597,7 +597,7 @@ module.exports = require('./containers.lifecycle.cjs').setup;
module.exports = require('./containers.lifecycle.cjs').teardown;
```

Add these entries to `jest.integration.config.cjs`, retaining the Data Integration test environment:
Add these entries to `jest.integration.config.cjs`, retaining the Data Isolation test environment:

```js
globalSetup: '<rootDir>/test/containers.global-setup.cjs',
Expand All @@ -611,7 +611,7 @@ import { readFileSync } from 'node:fs';
import { Container, ContainerResources } from '@integration-testing/testcontainers';
import type { SerializableContainerResources } from '@integration-testing/testcontainers';
import { JEST_CONTAINER_RESOURCES_PATH_ENV } from '@integration-testing/testcontainers/jest';
import { installJestDataIntegrationTestSupport } from '@integration-testing/data/jest';
import { installJestDataIntegrationTestSupport } from '@integration-testing/data-isolation/jest';
import { dataContext } from './data-context.js';

const resourcePath = process.env[JEST_CONTAINER_RESOURCES_PATH_ENV];
Expand Down Expand Up @@ -668,8 +668,8 @@ npx prisma generate
```ts
// test/data-context.ts
import { PrismaClient, type Prisma } from '@prisma/client';
import { createDataIntegrationTestContext } from '@integration-testing/data';
import { PrismaTransactionAdapter } from '@integration-testing/data/prisma';
import { createDataIntegrationTestContext } from '@integration-testing/data-isolation';
import { PrismaTransactionAdapter } from '@integration-testing/data-isolation/prisma';

export const dataContext = createDataIntegrationTestContext({
getResources: () => {
Expand Down Expand Up @@ -718,8 +718,8 @@ npm install typeorm@~0.3.28 pg@8 reflect-metadata
// test/data-context.ts
import 'reflect-metadata';
import { DataSource } from 'typeorm';
import { createDataIntegrationTestContext } from '@integration-testing/data';
import { TypeOrmTransactionAdapter } from '@integration-testing/data/typeorm';
import { createDataIntegrationTestContext } from '@integration-testing/data-isolation';
import { TypeOrmTransactionAdapter } from '@integration-testing/data-isolation/typeorm';
import { ProductEntity, ReservationEntity } from '../src/entities.js';

export const dataContext = createDataIntegrationTestContext({
Expand Down Expand Up @@ -795,15 +795,15 @@ are for applications whose SQLite migrations are maintained by Prisma.

The testing library does not patch production clients. Override the database provider in a NestJS
`TestingModule` with the scoped client. Application services and repositories should not import
`@integration-testing/data`.
`@integration-testing/data-isolation`.

For an application exposing `DATABASE_CLIENT`, `InventoryRepository`, and `InventoryService`,
the pg implementation can be tested like this:

```ts
import 'reflect-metadata';
import { Test, type TestingModule } from '@nestjs/testing';
import { DataIntegrationTest } from '@integration-testing/data';
import { DataIntegrationTest } from '@integration-testing/data-isolation';
import { dataContext } from './data-context.js';
import { DATABASE_CLIENT, InventoryRepository, InventoryService } from '../src/inventory.js';
import { PgInventoryRepository } from '../src/pg-repository.js';
Expand Down Expand Up @@ -884,25 +884,25 @@ default setup and not permission for tests to invent tables that differ from pro

The examples pin **`@integration-testing/testcontainers@0.1.0`**. The connection URL is the
boundary between the libraries: Testcontainers Integration supplies a real database endpoint;
Data Integration creates the configured driver and controls transactions through its adapter.
Neither library needs to import the other's source, and Data Integration does not require
Data Isolation creates the configured driver and controls transactions through its adapter.
Neither library needs to import the other's source, and Data Isolation does not require
Testcontainers as a dependency.

| Setup with Testcontainers Integration 0.1.0 | Data Integration compatibility |
| Setup with Testcontainers Integration 0.1.0 | Data Isolation compatibility |
| --- | --- |
| Shared `ContainerRuntime` launcher | Verified with PostgreSQL + pg, Prisma 6.19, and TypeORM 0.3 under Jest and Vitest, using an installed data package archive |
| Shared native `createVitestContainerGlobalSetup` | Verified with pg, global migrations, and transaction-scoped tests |
| Shared native `createJestContainerGlobalSetup` | Verified with pg while retaining `@integration-testing/data/jest/environment` |
| Shared native `createJestContainerGlobalSetup` | Verified with pg while retaining `@integration-testing/data-isolation/jest/environment` |
| Named `@RequiredContainer` with `isolation: 'shared'` and manual global setup | Demonstrated by the small Vitest example; both files select the same named database |
| Generated annotation/project file setup, including `isolation: 'dedicated'` | Not a supported drop-in pairing in this release; file lifecycle coordination is still needed |
| RabbitMQ, MongoDB, or SQL Server containers | Available infrastructure in Testcontainers Integration; this does not add transactional adapter coverage to Data Integration |
| RabbitMQ, MongoDB, or SQL Server containers | Available infrastructure in Testcontainers Integration; this does not add transactional adapter coverage to Data Isolation |

**Upgrading from the older Testcontainers beta:** replace the positional
`@RequiredContainer(Container.PostgreSql)` declaration with its named form:

```ts
import { Container, RequiredContainer } from '@integration-testing/testcontainers';
import { DataIntegrationTest } from '@integration-testing/data';
import { DataIntegrationTest } from '@integration-testing/data-isolation';

@RequiredContainer({
database: { kind: Container.PostgreSql, isolation: 'shared' },
Expand All @@ -921,12 +921,12 @@ the data declaration. `@ApplicationIntegrationTest` belongs to Testcontainers In
application lifecycle and is not required for these direct database/repository setups.

**Resource handoff in 0.1.0:** `injectedContainerResources()` now reads the active file lifecycle,
so it cannot supply resources during Data Integration's earlier initialization. The manual
so it cannot supply resources during Data Isolation's earlier initialization. The manual
global-setup examples below restore the shared resources through the exported Vitest context key
or Jest resource-file environment key. They do not install Testcontainers' generated file setup.

**Why dedicated containers need more work:** Testcontainers Integration's generated file setup
starts them in `beforeAll`. Data Integration initializes its file context earlier, in Vitest's
starts them in `beforeAll`. Data Isolation initializes its file context earlier, in Vitest's
`aroundAll` or Jest's `run_start`, so the dedicated resource is not yet available at that point.
Simply stacking annotations or generated setup files does not establish the necessary startup
and teardown ordering. Use the shared launcher or manual global setup documented here.
Expand Down
12 changes: 6 additions & 6 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 14 additions & 8 deletions docs/releasing.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Publishing @integration-testing/data
# Publishing @integration-testing/data-isolation

The first release target is **0.1.0**, published with npm's **latest** tag. Preparing the version,
passing CI, and opening a release PR do not publish the package.
Expand Down Expand Up @@ -42,22 +42,28 @@ publishing. The package does not inherit authentication or trusted-publisher set
4. When ready to publish, run:

```sh
npm publish ./.artifacts/integration-testing-data-0.1.0.tgz --access public --tag latest
npm publish ./.artifacts/integration-testing-data-isolation-0.1.0.tgz --access public --tag latest
```

This publishes the archive produced by the package checks. The publication is permanent for
this version, and npm may require account 2FA. Do not run this command merely to test access.
5. Check the registry rather than relying only on the command's exit status:

```sh
npm view @integration-testing/data@0.1.0 version dist.integrity repository --json
npm view @integration-testing/data dist-tags --json
npm view @integration-testing/data-isolation@0.1.0 version dist.integrity repository --json
npm view @integration-testing/data-isolation dist-tags --json
```

6. In a clean directory outside this repository, install `@integration-testing/data@0.1.0` from
npm and run the documented setup against a migrated test database. Confirm that `latest`
resolves to `0.1.0` before announcing availability. A locally packed consumer does not prove
registry installation.
6. Run the registry consumer check from the repository root:

```sh
bun run test:consumer:published
```

It copies the application outside the workspace, installs the manifest's exact package version
from npm (without a local tarball or source alias), and runs the same database matrix. Confirm
that `latest` resolves to `0.1.0` before announcing availability. A locally packed consumer
does not prove registry installation.

## Configure trusted publishing for subsequent releases

Expand Down
2 changes: 1 addition & 1 deletion examples/nestjs-inventory/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Transactional inventory integration tests

A small NestJS inventory application exercising `@integration-testing/data` from this repository. The same repositories, transaction configuration, and test scenarios run under
A small NestJS inventory application exercising `@integration-testing/data-isolation` from this repository. The same repositories, transaction configuration, and test scenarios run under
Jest 30 and Vitest 4.1. The example is excluded from the published npm package.

## Run
Expand Down
2 changes: 1 addition & 1 deletion examples/nestjs-inventory/jest.config.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
testMatch: [process.env.FAILURE_MODE ? '**/test/failure.test.ts' : process.env.BASELINE ? '**/test/baseline.test.ts' : '**/test/*.integration.test.ts'],
testEnvironment: process.env.BASELINE ? 'node' : '@integration-testing/data/jest/environment',
testEnvironment: process.env.BASELINE ? 'node' : '@integration-testing/data-isolation/jest/environment',
setupFilesAfterEnv: process.env.BASELINE ? [] : ['<rootDir>/test/jest.setup.ts'],
maxWorkers: process.env.DATA_CLIENT === 'sqlite' ? 1 : 2,
testTimeout: 30000,
Expand Down
2 changes: 1 addition & 1 deletion examples/nestjs-inventory/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"typeorm": "~0.3.28"
},
"devDependencies": {
"@integration-testing/data": "workspace:*",
"@integration-testing/data-isolation": "workspace:*",
"@integration-testing/testcontainers": "0.1.0",
"@nestjs/testing": "^11.1.0",
"@types/node": "^22.18.0",
Expand Down
Loading