@ultimat3/testing 27.10.4 → 27.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/package.json +16 -16
- package/src/index.ts +6 -1
- package/src/template-db.ts +17 -5
package/README.md
CHANGED
|
@@ -305,9 +305,15 @@ its own, so one shared store is enough.
|
|
|
305
305
|
## Parallel databases
|
|
306
306
|
|
|
307
307
|
```ts
|
|
308
|
-
|
|
308
|
+
import { acquireWorkerDatabase } from '@ultimat3/testing';
|
|
309
|
+
|
|
310
|
+
// `migrate: (url) => …` applies the app's schema into the template once; omitted here.
|
|
311
|
+
await using db = await acquireWorkerDatabase({ adminUrl: Bun.env['TEST_DATABASE_URL'] });
|
|
309
312
|
```
|
|
310
313
|
|
|
314
|
+
The handle is async-disposable (`As of 2026-10-18`): `await using` drops the worker's database when
|
|
315
|
+
the scope ends, a failed test included; `db.drop()` is the same call spelled by hand.
|
|
316
|
+
|
|
311
317
|
The first worker creates the template under a Postgres advisory lock and migrates it; every
|
|
312
318
|
worker then clones it copy-on-write, under the same lock — a clone waits only 5 s for a session on
|
|
313
319
|
the template to leave, and the next worker's migration holds one. With no Postgres configured it falls back to PGlite, so
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/testing",
|
|
3
|
-
"version": "27.
|
|
3
|
+
"version": "27.12.0",
|
|
4
4
|
"description": "Test harness: cloned template DBs per worker, frozen clock, sealed network, 6 test types",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,20 +36,20 @@
|
|
|
36
36
|
"test": "bun test"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@ultimat3/action": "27.
|
|
40
|
-
"@ultimat3/cache": "27.
|
|
41
|
-
"@ultimat3/core": "27.
|
|
42
|
-
"@ultimat3/db": "27.
|
|
43
|
-
"@ultimat3/entity": "27.
|
|
44
|
-
"@ultimat3/http": "27.
|
|
45
|
-
"@ultimat3/i18n": "27.
|
|
46
|
-
"@ultimat3/jobs": "27.
|
|
47
|
-
"@ultimat3/mail": "27.
|
|
48
|
-
"@ultimat3/policy": "27.
|
|
49
|
-
"@ultimat3/pwa": "27.
|
|
50
|
-
"@ultimat3/query": "27.
|
|
51
|
-
"@ultimat3/realtime": "27.
|
|
52
|
-
"@ultimat3/render": "27.
|
|
53
|
-
"@ultimat3/time": "27.
|
|
39
|
+
"@ultimat3/action": "27.12.0",
|
|
40
|
+
"@ultimat3/cache": "27.12.0",
|
|
41
|
+
"@ultimat3/core": "27.12.0",
|
|
42
|
+
"@ultimat3/db": "27.12.0",
|
|
43
|
+
"@ultimat3/entity": "27.12.0",
|
|
44
|
+
"@ultimat3/http": "27.12.0",
|
|
45
|
+
"@ultimat3/i18n": "27.12.0",
|
|
46
|
+
"@ultimat3/jobs": "27.12.0",
|
|
47
|
+
"@ultimat3/mail": "27.12.0",
|
|
48
|
+
"@ultimat3/policy": "27.12.0",
|
|
49
|
+
"@ultimat3/pwa": "27.12.0",
|
|
50
|
+
"@ultimat3/query": "27.12.0",
|
|
51
|
+
"@ultimat3/realtime": "27.12.0",
|
|
52
|
+
"@ultimat3/render": "27.12.0",
|
|
53
|
+
"@ultimat3/time": "27.12.0"
|
|
54
54
|
}
|
|
55
55
|
}
|
package/src/index.ts
CHANGED
|
@@ -340,7 +340,12 @@ export {
|
|
|
340
340
|
} from './sealed-network';
|
|
341
341
|
export type { SharedExamples } from './shared-examples';
|
|
342
342
|
export { behavesLike, sharedExamples } from './shared-examples';
|
|
343
|
-
export type {
|
|
343
|
+
export type {
|
|
344
|
+
DisposableWorkerDatabase,
|
|
345
|
+
SqlRunner,
|
|
346
|
+
TemplateDbConfig,
|
|
347
|
+
WorkerDatabase,
|
|
348
|
+
} from './template-db';
|
|
344
349
|
export {
|
|
345
350
|
acquireWorkerDatabase,
|
|
346
351
|
cloneSql,
|
package/src/template-db.ts
CHANGED
|
@@ -30,6 +30,13 @@ export interface WorkerDatabase {
|
|
|
30
30
|
drop(): Promise<void>;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* What `acquireWorkerDatabase` answers: the handle plus `drop()` as its async disposer, so
|
|
35
|
+
* `await using db = await acquireWorkerDatabase()` releases it however the scope exits. A separate
|
|
36
|
+
* type so a hand-built `WorkerDatabase` (a fake `acquire`) owes no disposer.
|
|
37
|
+
*/
|
|
38
|
+
export type DisposableWorkerDatabase = WorkerDatabase & AsyncDisposable;
|
|
39
|
+
|
|
33
40
|
export const DEFAULT_TEMPLATE = 'ultimate_test_template';
|
|
34
41
|
|
|
35
42
|
/**
|
|
@@ -109,7 +116,7 @@ const defaultConnect = (url: string): SqlRunner => {
|
|
|
109
116
|
export async function acquireWorkerDatabase(
|
|
110
117
|
config: TemplateDbConfig = {},
|
|
111
118
|
deps: Partial<TemplateDbDeps> = {},
|
|
112
|
-
): Promise<
|
|
119
|
+
): Promise<DisposableWorkerDatabase> {
|
|
113
120
|
const env = deps.env ?? (Bun.env as Readonly<Record<string, string | undefined>>);
|
|
114
121
|
const connect = deps.connect ?? defaultConnect;
|
|
115
122
|
const template = config.templateName ?? DEFAULT_TEMPLATE;
|
|
@@ -118,13 +125,13 @@ export async function acquireWorkerDatabase(
|
|
|
118
125
|
const adminUrl = config.adminUrl ?? env['TEST_DATABASE_URL'] ?? env['DATABASE_URL'];
|
|
119
126
|
|
|
120
127
|
if (adminUrl === undefined || adminUrl.length === 0) {
|
|
121
|
-
return {
|
|
128
|
+
return disposable({
|
|
122
129
|
kind: 'pglite',
|
|
123
130
|
worker,
|
|
124
131
|
database,
|
|
125
132
|
url: `pglite://memory/${database}`,
|
|
126
133
|
drop: async () => undefined,
|
|
127
|
-
};
|
|
134
|
+
});
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
const admin = connect(adminUrl);
|
|
@@ -160,7 +167,7 @@ export async function acquireWorkerDatabase(
|
|
|
160
167
|
}
|
|
161
168
|
await admin.close();
|
|
162
169
|
|
|
163
|
-
return {
|
|
170
|
+
return disposable({
|
|
164
171
|
kind: 'postgres',
|
|
165
172
|
worker,
|
|
166
173
|
database,
|
|
@@ -170,7 +177,12 @@ export async function acquireWorkerDatabase(
|
|
|
170
177
|
await cleanup.exec(dropSql(database));
|
|
171
178
|
await cleanup.close();
|
|
172
179
|
},
|
|
173
|
-
};
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** The handle with its `drop` as the async disposer, so `await using` is the one release spelling. */
|
|
184
|
+
function disposable(db: WorkerDatabase): DisposableWorkerDatabase {
|
|
185
|
+
return { ...db, [Symbol.asyncDispose]: () => db.drop() };
|
|
174
186
|
}
|
|
175
187
|
|
|
176
188
|
/**
|