@fragno-dev/test 0.1.11 → 0.1.13
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/.turbo/turbo-build.log +15 -11
- package/CHANGELOG.md +35 -0
- package/dist/adapters.d.ts +3 -11
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +65 -50
- package/dist/adapters.js.map +1 -1
- package/dist/db-test.d.ts +129 -0
- package/dist/db-test.d.ts.map +1 -0
- package/dist/db-test.js +214 -0
- package/dist/db-test.js.map +1 -0
- package/dist/index.d.ts +23 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -91
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
- package/src/adapters.ts +137 -118
- package/src/db-test.test.ts +352 -0
- package/src/db-test.ts +574 -0
- package/src/index.test.ts +287 -546
- package/src/index.ts +39 -241
package/src/adapters.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { createRequire } from "node:module";
|
|
|
12
12
|
import { mkdir, writeFile, rm } from "node:fs/promises";
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { existsSync } from "node:fs";
|
|
15
|
+
import type { BaseTestContext } from ".";
|
|
16
|
+
import { createCommonTestContextMethods } from ".";
|
|
15
17
|
|
|
16
18
|
// Adapter configuration types
|
|
17
19
|
export interface KyselySqliteAdapter {
|
|
@@ -30,44 +32,47 @@ export interface DrizzlePgliteAdapter {
|
|
|
30
32
|
|
|
31
33
|
export type SupportedAdapter = KyselySqliteAdapter | KyselyPgliteAdapter | DrizzlePgliteAdapter;
|
|
32
34
|
|
|
33
|
-
//
|
|
34
|
-
export
|
|
35
|
+
// Schema configuration for multi-schema adapters
|
|
36
|
+
export interface SchemaConfig {
|
|
37
|
+
schema: AnySchema;
|
|
38
|
+
namespace: string;
|
|
39
|
+
migrateToVersion?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Internal test context extends BaseTestContext with getOrm (not exposed publicly)
|
|
43
|
+
interface InternalTestContext extends BaseTestContext {
|
|
44
|
+
getOrm: <TSchema extends AnySchema>(namespace: string) => AbstractQuery<TSchema>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Conditional return types based on adapter (adapter-specific properties only)
|
|
48
|
+
export type AdapterContext<T extends SupportedAdapter> = T extends
|
|
35
49
|
| KyselySqliteAdapter
|
|
36
50
|
| KyselyPgliteAdapter
|
|
37
51
|
? {
|
|
38
|
-
readonly db: AbstractQuery<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
39
52
|
readonly kysely: Kysely<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
40
|
-
readonly adapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
41
|
-
resetDatabase: () => Promise<void>;
|
|
42
|
-
cleanup: () => Promise<void>;
|
|
43
53
|
}
|
|
44
54
|
: T extends DrizzlePgliteAdapter
|
|
45
55
|
? {
|
|
46
|
-
readonly db: AbstractQuery<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
47
56
|
readonly drizzle: ReturnType<typeof drizzle<any>>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
48
|
-
readonly adapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
49
|
-
resetDatabase: () => Promise<void>;
|
|
50
|
-
cleanup: () => Promise<void>;
|
|
51
57
|
}
|
|
52
58
|
: never;
|
|
53
59
|
|
|
54
60
|
// Factory function return type
|
|
55
61
|
interface AdapterFactoryResult<T extends SupportedAdapter> {
|
|
56
|
-
testContext:
|
|
62
|
+
testContext: InternalTestContext & AdapterContext<T>;
|
|
57
63
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
64
|
adapter: DatabaseAdapter<any>;
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
/**
|
|
62
68
|
* Create Kysely + SQLite adapter using SQLocalKysely (always in-memory)
|
|
69
|
+
* Supports multiple schemas with separate namespaces
|
|
63
70
|
*/
|
|
64
71
|
export async function createKyselySqliteAdapter(
|
|
65
72
|
_config: KyselySqliteAdapter,
|
|
66
|
-
|
|
67
|
-
namespace: string,
|
|
68
|
-
migrateToVersion?: number,
|
|
73
|
+
schemas: SchemaConfig[],
|
|
69
74
|
): Promise<AdapterFactoryResult<KyselySqliteAdapter>> {
|
|
70
|
-
// Helper to create a new database instance and run migrations
|
|
75
|
+
// Helper to create a new database instance and run migrations for all schemas
|
|
71
76
|
const createDatabase = async () => {
|
|
72
77
|
// Create SQLocalKysely instance (always in-memory for tests)
|
|
73
78
|
const { dialect } = new SQLocalKysely(":memory:");
|
|
@@ -82,37 +87,43 @@ export async function createKyselySqliteAdapter(
|
|
|
82
87
|
provider: "sqlite",
|
|
83
88
|
});
|
|
84
89
|
|
|
85
|
-
// Run migrations
|
|
86
|
-
const migrator = adapter.createMigrationEngine(schema, namespace);
|
|
87
|
-
const preparedMigration = migrateToVersion
|
|
88
|
-
? await migrator.prepareMigrationTo(migrateToVersion, {
|
|
89
|
-
updateSettings: false,
|
|
90
|
-
})
|
|
91
|
-
: await migrator.prepareMigration({
|
|
92
|
-
updateSettings: false,
|
|
93
|
-
});
|
|
94
|
-
await preparedMigration.execute();
|
|
95
|
-
|
|
96
|
-
// Create ORM instance
|
|
90
|
+
// Run migrations for all schemas in order
|
|
97
91
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
-
const
|
|
92
|
+
const ormMap = new Map<string, AbstractQuery<any, any>>();
|
|
93
|
+
|
|
94
|
+
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
95
|
+
// Run migrations
|
|
96
|
+
const migrator = adapter.createMigrationEngine(schema, namespace);
|
|
97
|
+
const preparedMigration = migrateToVersion
|
|
98
|
+
? await migrator.prepareMigrationTo(migrateToVersion, {
|
|
99
|
+
updateSettings: false,
|
|
100
|
+
})
|
|
101
|
+
: await migrator.prepareMigration({
|
|
102
|
+
updateSettings: false,
|
|
103
|
+
});
|
|
104
|
+
await preparedMigration.execute();
|
|
105
|
+
|
|
106
|
+
// Create ORM instance and store in map
|
|
107
|
+
const orm = adapter.createQueryEngine(schema, namespace);
|
|
108
|
+
ormMap.set(namespace, orm);
|
|
109
|
+
}
|
|
99
110
|
|
|
100
|
-
return { kysely, adapter,
|
|
111
|
+
return { kysely, adapter, ormMap };
|
|
101
112
|
};
|
|
102
113
|
|
|
103
114
|
// Create initial database
|
|
104
|
-
let { kysely, adapter,
|
|
115
|
+
let { kysely, adapter, ormMap } = await createDatabase();
|
|
105
116
|
|
|
106
|
-
// Reset database function -
|
|
117
|
+
// Reset database function - truncates all tables (only supported for in-memory databases)
|
|
107
118
|
const resetDatabase = async () => {
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
// For SQLite, truncate all tables by deleting rows
|
|
120
|
+
for (const { schema, namespace } of schemas) {
|
|
121
|
+
const mapper = adapter.createTableNameMapper(namespace);
|
|
122
|
+
for (const tableName of Object.keys(schema.tables)) {
|
|
123
|
+
const physicalTableName = mapper.toPhysical(tableName);
|
|
124
|
+
await kysely.deleteFrom(physicalTableName).execute();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
116
127
|
};
|
|
117
128
|
|
|
118
129
|
// Cleanup function - closes connections (no files to delete for in-memory)
|
|
@@ -120,17 +131,17 @@ export async function createKyselySqliteAdapter(
|
|
|
120
131
|
await kysely.destroy();
|
|
121
132
|
};
|
|
122
133
|
|
|
134
|
+
const commonMethods = createCommonTestContextMethods(ormMap);
|
|
135
|
+
|
|
123
136
|
return {
|
|
124
137
|
testContext: {
|
|
125
|
-
get db() {
|
|
126
|
-
return orm;
|
|
127
|
-
},
|
|
128
138
|
get kysely() {
|
|
129
139
|
return kysely;
|
|
130
140
|
},
|
|
131
141
|
get adapter() {
|
|
132
142
|
return adapter;
|
|
133
143
|
},
|
|
144
|
+
...commonMethods,
|
|
134
145
|
resetDatabase,
|
|
135
146
|
cleanup,
|
|
136
147
|
},
|
|
@@ -142,16 +153,15 @@ export async function createKyselySqliteAdapter(
|
|
|
142
153
|
|
|
143
154
|
/**
|
|
144
155
|
* Create Kysely + PGLite adapter using kysely-pglite
|
|
156
|
+
* Supports multiple schemas with separate namespaces
|
|
145
157
|
*/
|
|
146
158
|
export async function createKyselyPgliteAdapter(
|
|
147
159
|
config: KyselyPgliteAdapter,
|
|
148
|
-
|
|
149
|
-
namespace: string,
|
|
150
|
-
migrateToVersion?: number,
|
|
160
|
+
schemas: SchemaConfig[],
|
|
151
161
|
): Promise<AdapterFactoryResult<KyselyPgliteAdapter>> {
|
|
152
162
|
const databasePath = config.databasePath;
|
|
153
163
|
|
|
154
|
-
// Helper to create a new database instance and run migrations
|
|
164
|
+
// Helper to create a new database instance and run migrations for all schemas
|
|
155
165
|
const createDatabase = async () => {
|
|
156
166
|
// Create KyselyPGlite instance
|
|
157
167
|
const kyselyPglite = await KyselyPGlite.create(databasePath);
|
|
@@ -168,44 +178,47 @@ export async function createKyselyPgliteAdapter(
|
|
|
168
178
|
provider: "postgresql",
|
|
169
179
|
});
|
|
170
180
|
|
|
171
|
-
// Run migrations
|
|
172
|
-
const migrator = adapter.createMigrationEngine(schema, namespace);
|
|
173
|
-
const preparedMigration = migrateToVersion
|
|
174
|
-
? await migrator.prepareMigrationTo(migrateToVersion, {
|
|
175
|
-
updateSettings: false,
|
|
176
|
-
})
|
|
177
|
-
: await migrator.prepareMigration({
|
|
178
|
-
updateSettings: false,
|
|
179
|
-
});
|
|
180
|
-
await preparedMigration.execute();
|
|
181
|
-
|
|
182
|
-
// Create ORM instance
|
|
181
|
+
// Run migrations for all schemas in order
|
|
183
182
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
184
|
-
const
|
|
183
|
+
const ormMap = new Map<string, AbstractQuery<any, any>>();
|
|
184
|
+
|
|
185
|
+
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
186
|
+
// Run migrations
|
|
187
|
+
const migrator = adapter.createMigrationEngine(schema, namespace);
|
|
188
|
+
const preparedMigration = migrateToVersion
|
|
189
|
+
? await migrator.prepareMigrationTo(migrateToVersion, {
|
|
190
|
+
updateSettings: false,
|
|
191
|
+
})
|
|
192
|
+
: await migrator.prepareMigration({
|
|
193
|
+
updateSettings: false,
|
|
194
|
+
});
|
|
195
|
+
await preparedMigration.execute();
|
|
196
|
+
|
|
197
|
+
// Create ORM instance and store in map
|
|
198
|
+
const orm = adapter.createQueryEngine(schema, namespace);
|
|
199
|
+
ormMap.set(namespace, orm);
|
|
200
|
+
}
|
|
185
201
|
|
|
186
|
-
return { kysely, adapter, kyselyPglite,
|
|
202
|
+
return { kysely, adapter, kyselyPglite, ormMap };
|
|
187
203
|
};
|
|
188
204
|
|
|
189
205
|
// Create initial database
|
|
190
|
-
|
|
206
|
+
const { kysely, adapter, kyselyPglite, ormMap } = await createDatabase();
|
|
191
207
|
|
|
192
|
-
// Reset database function -
|
|
208
|
+
// Reset database function - truncates all tables (only supported for in-memory databases)
|
|
193
209
|
const resetDatabase = async () => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
await kyselyPglite.client.close();
|
|
199
|
-
} catch {
|
|
200
|
-
// Ignore if already closed
|
|
210
|
+
if (databasePath && databasePath !== ":memory:") {
|
|
211
|
+
throw new Error("resetDatabase is only supported for in-memory databases");
|
|
201
212
|
}
|
|
202
213
|
|
|
203
|
-
//
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
214
|
+
// Truncate all tables
|
|
215
|
+
for (const { schema, namespace } of schemas) {
|
|
216
|
+
const mapper = adapter.createTableNameMapper(namespace);
|
|
217
|
+
for (const tableName of Object.keys(schema.tables)) {
|
|
218
|
+
const physicalTableName = mapper.toPhysical(tableName);
|
|
219
|
+
await kysely.deleteFrom(physicalTableName).execute();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
209
222
|
};
|
|
210
223
|
|
|
211
224
|
// Cleanup function - closes connections and deletes database directory
|
|
@@ -224,17 +237,17 @@ export async function createKyselyPgliteAdapter(
|
|
|
224
237
|
}
|
|
225
238
|
};
|
|
226
239
|
|
|
240
|
+
const commonMethods = createCommonTestContextMethods(ormMap);
|
|
241
|
+
|
|
227
242
|
return {
|
|
228
243
|
testContext: {
|
|
229
|
-
get db() {
|
|
230
|
-
return orm;
|
|
231
|
-
},
|
|
232
244
|
get kysely() {
|
|
233
245
|
return kysely;
|
|
234
246
|
},
|
|
235
247
|
get adapter() {
|
|
236
248
|
return adapter;
|
|
237
249
|
},
|
|
250
|
+
...commonMethods,
|
|
238
251
|
resetDatabase,
|
|
239
252
|
cleanup,
|
|
240
253
|
},
|
|
@@ -246,12 +259,11 @@ export async function createKyselyPgliteAdapter(
|
|
|
246
259
|
|
|
247
260
|
/**
|
|
248
261
|
* Create Drizzle + PGLite adapter using drizzle-orm/pglite
|
|
262
|
+
* Supports multiple schemas with separate namespaces
|
|
249
263
|
*/
|
|
250
264
|
export async function createDrizzlePgliteAdapter(
|
|
251
265
|
config: DrizzlePgliteAdapter,
|
|
252
|
-
|
|
253
|
-
namespace: string,
|
|
254
|
-
_migrateToVersion?: number,
|
|
266
|
+
schemas: SchemaConfig[],
|
|
255
267
|
): Promise<AdapterFactoryResult<DrizzlePgliteAdapter>> {
|
|
256
268
|
const databasePath = config.databasePath;
|
|
257
269
|
|
|
@@ -275,8 +287,13 @@ export async function createDrizzlePgliteAdapter(
|
|
|
275
287
|
`test-schema-${Date.now()}-${Math.random().toString(36).slice(2, 9)}.ts`,
|
|
276
288
|
);
|
|
277
289
|
|
|
278
|
-
//
|
|
279
|
-
const
|
|
290
|
+
// Combine all schemas into a single Drizzle schema with namespaced tables
|
|
291
|
+
const schemaConfigs = schemas.map(({ schema, namespace }) => ({
|
|
292
|
+
namespace: namespace ?? "",
|
|
293
|
+
schema,
|
|
294
|
+
}));
|
|
295
|
+
|
|
296
|
+
const drizzleSchemaTs = generateSchema(schemaConfigs, "postgresql");
|
|
280
297
|
await writeFile(schemaFilePath, drizzleSchemaTs, "utf-8");
|
|
281
298
|
|
|
282
299
|
// Dynamically import the generated schema (with cache busting)
|
|
@@ -289,7 +306,7 @@ export async function createDrizzlePgliteAdapter(
|
|
|
289
306
|
return { schemaModule, cleanup };
|
|
290
307
|
};
|
|
291
308
|
|
|
292
|
-
// Helper to create a new database instance and run migrations
|
|
309
|
+
// Helper to create a new database instance and run migrations for all schemas
|
|
293
310
|
const createDatabase = async () => {
|
|
294
311
|
// Write schema to file and load it
|
|
295
312
|
const { schemaModule, cleanup } = await writeAndLoadSchema();
|
|
@@ -320,29 +337,41 @@ export async function createDrizzlePgliteAdapter(
|
|
|
320
337
|
provider: "postgresql",
|
|
321
338
|
});
|
|
322
339
|
|
|
323
|
-
// Create ORM
|
|
340
|
+
// Create ORM instances for each schema and store in map
|
|
324
341
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
325
|
-
const
|
|
342
|
+
const ormMap = new Map<string, AbstractQuery<any, any>>();
|
|
343
|
+
|
|
344
|
+
for (const { schema, namespace } of schemas) {
|
|
345
|
+
const orm = adapter.createQueryEngine(schema, namespace);
|
|
346
|
+
ormMap.set(namespace, orm);
|
|
347
|
+
}
|
|
326
348
|
|
|
327
|
-
return { drizzle: db, adapter, pglite, cleanup,
|
|
349
|
+
return { drizzle: db, adapter, pglite, cleanup, ormMap };
|
|
328
350
|
};
|
|
329
351
|
|
|
330
352
|
// Create initial database
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
353
|
+
const {
|
|
354
|
+
drizzle: drizzleDb,
|
|
355
|
+
adapter,
|
|
356
|
+
pglite,
|
|
357
|
+
cleanup: schemaCleanup,
|
|
358
|
+
ormMap,
|
|
359
|
+
} = await createDatabase();
|
|
360
|
+
|
|
361
|
+
// Reset database function - truncates all tables (only supported for in-memory databases)
|
|
334
362
|
const resetDatabase = async () => {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
363
|
+
if (databasePath && databasePath !== ":memory:") {
|
|
364
|
+
throw new Error("resetDatabase is only supported for in-memory databases");
|
|
365
|
+
}
|
|
338
366
|
|
|
339
|
-
//
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
367
|
+
// Truncate all tables by deleting rows
|
|
368
|
+
for (const { schema, namespace } of schemas) {
|
|
369
|
+
const mapper = adapter.createTableNameMapper(namespace);
|
|
370
|
+
for (const tableName of Object.keys(schema.tables)) {
|
|
371
|
+
const physicalTableName = mapper.toPhysical(tableName);
|
|
372
|
+
await drizzleDb.execute(`DELETE FROM "${physicalTableName}"`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
346
375
|
};
|
|
347
376
|
|
|
348
377
|
// Cleanup function - closes connections and deletes generated files and database directory
|
|
@@ -356,17 +385,17 @@ export async function createDrizzlePgliteAdapter(
|
|
|
356
385
|
}
|
|
357
386
|
};
|
|
358
387
|
|
|
388
|
+
const commonMethods = createCommonTestContextMethods(ormMap);
|
|
389
|
+
|
|
359
390
|
return {
|
|
360
391
|
testContext: {
|
|
361
|
-
get db() {
|
|
362
|
-
return orm;
|
|
363
|
-
},
|
|
364
392
|
get drizzle() {
|
|
365
393
|
return drizzleDb;
|
|
366
394
|
},
|
|
367
395
|
get adapter() {
|
|
368
396
|
return adapter;
|
|
369
397
|
},
|
|
398
|
+
...commonMethods,
|
|
370
399
|
resetDatabase,
|
|
371
400
|
cleanup,
|
|
372
401
|
},
|
|
@@ -378,28 +407,18 @@ export async function createDrizzlePgliteAdapter(
|
|
|
378
407
|
|
|
379
408
|
/**
|
|
380
409
|
* Create adapter based on configuration
|
|
410
|
+
* Supports multiple schemas with separate namespaces
|
|
381
411
|
*/
|
|
382
412
|
export async function createAdapter<T extends SupportedAdapter>(
|
|
383
413
|
adapterConfig: T,
|
|
384
|
-
|
|
385
|
-
namespace: string,
|
|
386
|
-
migrateToVersion?: number,
|
|
414
|
+
schemas: SchemaConfig[],
|
|
387
415
|
): Promise<AdapterFactoryResult<T>> {
|
|
388
416
|
if (adapterConfig.type === "kysely-sqlite") {
|
|
389
|
-
return createKyselySqliteAdapter(adapterConfig,
|
|
390
|
-
AdapterFactoryResult<T>
|
|
391
|
-
>;
|
|
417
|
+
return createKyselySqliteAdapter(adapterConfig, schemas) as Promise<AdapterFactoryResult<T>>;
|
|
392
418
|
} else if (adapterConfig.type === "kysely-pglite") {
|
|
393
|
-
return createKyselyPgliteAdapter(adapterConfig,
|
|
394
|
-
AdapterFactoryResult<T>
|
|
395
|
-
>;
|
|
419
|
+
return createKyselyPgliteAdapter(adapterConfig, schemas) as Promise<AdapterFactoryResult<T>>;
|
|
396
420
|
} else if (adapterConfig.type === "drizzle-pglite") {
|
|
397
|
-
return createDrizzlePgliteAdapter(
|
|
398
|
-
adapterConfig,
|
|
399
|
-
schema,
|
|
400
|
-
namespace,
|
|
401
|
-
migrateToVersion,
|
|
402
|
-
) as Promise<AdapterFactoryResult<T>>;
|
|
421
|
+
return createDrizzlePgliteAdapter(adapterConfig, schemas) as Promise<AdapterFactoryResult<T>>;
|
|
403
422
|
}
|
|
404
423
|
|
|
405
424
|
throw new Error(`Unsupported adapter type: ${(adapterConfig as SupportedAdapter).type}`);
|