@fragno-dev/test 0.1.8 → 0.1.10
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 +11 -11
- package/CHANGELOG.md +52 -0
- package/dist/adapters.d.ts +3 -0
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +26 -10
- package/dist/adapters.js.map +1 -1
- package/dist/index.d.ts +11 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -8
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/src/adapters.ts +33 -6
- package/src/index.test.ts +217 -113
- package/src/index.ts +46 -21
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @fragno-dev/test@0.1.
|
|
2
|
+
> @fragno-dev/test@0.1.10 build /home/runner/work/fragno/fragno/packages/fragno-test
|
|
3
3
|
> tsdown
|
|
4
4
|
|
|
5
5
|
[34mℹ[39m tsdown [2mv0.15.12[22m powered by rolldown [2mv1.0.0-beta.45[22m
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
[34mℹ[39m entry: [34msrc/index.ts[39m
|
|
8
8
|
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
9
9
|
[34mℹ[39m Build start
|
|
10
|
-
[34mℹ[39m [2mdist/[22m[1mindex.js[22m [2m 2.
|
|
11
|
-
[34mℹ[39m [2mdist/[22madapters.js.map [
|
|
12
|
-
[34mℹ[39m [2mdist/[22mindex.js.map [2m
|
|
13
|
-
[34mℹ[39m [2mdist/[22madapters.js [2m 6.
|
|
14
|
-
[34mℹ[39m [2mdist/[22mindex.d.ts.map [2m 1.
|
|
15
|
-
[34mℹ[39m [2mdist/[22madapters.d.ts.map [2m 0.
|
|
16
|
-
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.ts[22m[39m [2m
|
|
17
|
-
[34mℹ[39m [2mdist/[22m[32madapters.d.ts[39m [2m 1.
|
|
18
|
-
[34mℹ[39m 8 files, total:
|
|
19
|
-
[32m✔[39m Build complete in [
|
|
10
|
+
[34mℹ[39m [2mdist/[22m[1mindex.js[22m [2m 2.64 kB[22m [2m│ gzip: 0.81 kB[22m
|
|
11
|
+
[34mℹ[39m [2mdist/[22madapters.js.map [2m16.62 kB[22m [2m│ gzip: 3.63 kB[22m
|
|
12
|
+
[34mℹ[39m [2mdist/[22mindex.js.map [2m 9.54 kB[22m [2m│ gzip: 2.63 kB[22m
|
|
13
|
+
[34mℹ[39m [2mdist/[22madapters.js [2m 6.74 kB[22m [2m│ gzip: 1.54 kB[22m
|
|
14
|
+
[34mℹ[39m [2mdist/[22mindex.d.ts.map [2m 1.72 kB[22m [2m│ gzip: 0.78 kB[22m
|
|
15
|
+
[34mℹ[39m [2mdist/[22madapters.d.ts.map [2m 0.93 kB[22m [2m│ gzip: 0.42 kB[22m
|
|
16
|
+
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.ts[22m[39m [2m 3.34 kB[22m [2m│ gzip: 0.93 kB[22m
|
|
17
|
+
[34mℹ[39m [2mdist/[22m[32madapters.d.ts[39m [2m 1.28 kB[22m [2m│ gzip: 0.42 kB[22m
|
|
18
|
+
[34mℹ[39m 8 files, total: 42.80 kB
|
|
19
|
+
[32m✔[39m Build complete in [32m13034ms[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# @fragno-dev/test
|
|
2
2
|
|
|
3
|
+
## 0.1.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [b54ff8b]
|
|
8
|
+
- @fragno-dev/db@0.1.13
|
|
9
|
+
|
|
10
|
+
## 0.1.9
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- be1a630: **BREAKING**: `callRoute` now returns type-safe `FragnoResponse<T>` instead of raw
|
|
15
|
+
`Response`
|
|
16
|
+
|
|
17
|
+
The `callRoute` method on fragment instances now returns a parsed `FragnoResponse<T>`
|
|
18
|
+
discriminated union instead of a raw `Response`. This provides type-safe access to response data
|
|
19
|
+
without manual JSON parsing.
|
|
20
|
+
|
|
21
|
+
**Migration:**
|
|
22
|
+
|
|
23
|
+
Preferably use the new type-safe response:
|
|
24
|
+
|
|
25
|
+
```diff
|
|
26
|
+
- const response = await fragment.callRoute("GET", "/users");
|
|
27
|
+
- const data = await response.json();
|
|
28
|
+
+ const response = await fragment.callRoute("GET", "/users");
|
|
29
|
+
+ if (response.type === "json") {
|
|
30
|
+
+ const data = response.data; // fully typed!
|
|
31
|
+
+ }
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- or -
|
|
35
|
+
|
|
36
|
+
Switch to `callRouteRaw` if you need the raw response:
|
|
37
|
+
|
|
38
|
+
```diff
|
|
39
|
+
- const response = await fragment.callRoute("GET", "/users");
|
|
40
|
+
+ const response = await fragment.callRouteRaw("GET", "/users");
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- e99ef47: feat: expose `db` object to run queries directly in tests
|
|
44
|
+
- Updated dependencies [be1a630]
|
|
45
|
+
- Updated dependencies [b2a88aa]
|
|
46
|
+
- Updated dependencies [2900bfa]
|
|
47
|
+
- Updated dependencies [059a249]
|
|
48
|
+
- Updated dependencies [f3f7bc2]
|
|
49
|
+
- Updated dependencies [a9f8159]
|
|
50
|
+
- Updated dependencies [9d4cd3a]
|
|
51
|
+
- Updated dependencies [fdb5aaf]
|
|
52
|
+
- @fragno-dev/core@0.1.6
|
|
53
|
+
- @fragno-dev/db@0.1.12
|
|
54
|
+
|
|
3
55
|
## 0.1.8
|
|
4
56
|
|
|
5
57
|
### Patch Changes
|
package/dist/adapters.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Kysely } from "kysely";
|
|
|
2
2
|
import { drizzle } from "drizzle-orm/pglite";
|
|
3
3
|
import { AnySchema } from "@fragno-dev/db/schema";
|
|
4
4
|
import { DatabaseAdapter } from "@fragno-dev/db/adapters";
|
|
5
|
+
import { AbstractQuery } from "@fragno-dev/db/query";
|
|
5
6
|
|
|
6
7
|
//#region src/adapters.d.ts
|
|
7
8
|
interface KyselySqliteAdapter {
|
|
@@ -17,11 +18,13 @@ interface DrizzlePgliteAdapter {
|
|
|
17
18
|
}
|
|
18
19
|
type SupportedAdapter = KyselySqliteAdapter | KyselyPgliteAdapter | DrizzlePgliteAdapter;
|
|
19
20
|
type TestContext<T extends SupportedAdapter> = T extends KyselySqliteAdapter | KyselyPgliteAdapter ? {
|
|
21
|
+
readonly db: AbstractQuery<any>;
|
|
20
22
|
readonly kysely: Kysely<any>;
|
|
21
23
|
readonly adapter: DatabaseAdapter<any>;
|
|
22
24
|
resetDatabase: () => Promise<void>;
|
|
23
25
|
cleanup: () => Promise<void>;
|
|
24
26
|
} : T extends DrizzlePgliteAdapter ? {
|
|
27
|
+
readonly db: AbstractQuery<any>;
|
|
25
28
|
readonly drizzle: ReturnType<typeof drizzle<any>>;
|
|
26
29
|
readonly adapter: DatabaseAdapter<any>;
|
|
27
30
|
resetDatabase: () => Promise<void>;
|
package/dist/adapters.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapters.d.ts","names":[],"sources":["../src/adapters.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","names":[],"sources":["../src/adapters.ts"],"sourcesContent":[],"mappings":";;;;;;;UAgBiB,mBAAA;;AAAjB;AAIiB,UAAA,mBAAA,CAAmB;EAKnB,IAAA,EAAA,eAAA;EAKL,YAAA,CAAA,EAAA,MAAgB;;AAAyB,UALpC,oBAAA,CAKoC;EAAsB,IAAA,EAAA,gBAAA;EAAoB,YAAA,CAAA,EAAA,MAAA;AAG/F;AAAkC,KAHtB,gBAAA,GAAmB,mBAGG,GAHmB,mBAGnB,GAHyC,oBAGzC;AAAoB,KAA1C,WAA0C,CAAA,UAApB,gBAAoB,CAAA,GAAA,CAAA,SAClD,mBADkD,GAElD,mBAFkD,GAAA;EAClD,SAAA,EAAA,EAGe,aAHf,CAAA,GAAA,CAAA;EACA,SAAA,MAAA,EAGmB,MAHnB,CAAA,GAAA,CAAA;EAEe,SAAA,OAAA,EAEK,eAFL,CAAA,GAAA,CAAA;EACI,aAAA,EAAA,GAAA,GAEI,OAFJ,CAAA,IAAA,CAAA;EACC,OAAA,EAAA,GAAA,GAEH,OAFG,CAAA,IAAA,CAAA;CACG,GAGvB,CAHuB,SAGb,oBAHa,GAAA;EACN,SAAA,EAAA,EAIA,aAJA,CAAA,GAAA,CAAA;EAEjB,SAAA,OAAA,EAGsB,UAHtB,CAAA,OAGwC,OAHxC,CAAA,GAAA,CAAA,CAAA;EAAU,SAAA,OAAA,EAIY,eAJZ,CAAA,GAAA,CAAA;EAEO,aAAA,EAAA,GAAA,GAGQ,OAHR,CAAA,IAAA,CAAA;EACuB,OAAA,EAAA,GAAA,GAGrB,OAHqB,CAAA,IAAA,CAAA;CAAlB,GAAA,KAAA"}
|
package/dist/adapters.js
CHANGED
|
@@ -26,21 +26,26 @@ async function createKyselySqliteAdapter(_config, schema, namespace, migrateToVe
|
|
|
26
26
|
await (migrateToVersion ? await migrator.prepareMigrationTo(migrateToVersion, { updateSettings: false }) : await migrator.prepareMigration({ updateSettings: false })).execute();
|
|
27
27
|
return {
|
|
28
28
|
kysely: kysely$1,
|
|
29
|
-
adapter: adapter$1
|
|
29
|
+
adapter: adapter$1,
|
|
30
|
+
orm: adapter$1.createQueryEngine(schema, namespace)
|
|
30
31
|
};
|
|
31
32
|
};
|
|
32
|
-
let { kysely, adapter } = await createDatabase();
|
|
33
|
+
let { kysely, adapter, orm } = await createDatabase();
|
|
33
34
|
const resetDatabase = async () => {
|
|
34
35
|
await kysely.destroy();
|
|
35
36
|
const newDb = await createDatabase();
|
|
36
37
|
kysely = newDb.kysely;
|
|
37
38
|
adapter = newDb.adapter;
|
|
39
|
+
orm = newDb.orm;
|
|
38
40
|
};
|
|
39
41
|
const cleanup = async () => {
|
|
40
42
|
await kysely.destroy();
|
|
41
43
|
};
|
|
42
44
|
return {
|
|
43
45
|
testContext: {
|
|
46
|
+
get db() {
|
|
47
|
+
return orm;
|
|
48
|
+
},
|
|
44
49
|
get kysely() {
|
|
45
50
|
return kysely;
|
|
46
51
|
},
|
|
@@ -72,10 +77,11 @@ async function createKyselyPgliteAdapter(config, schema, namespace, migrateToVer
|
|
|
72
77
|
return {
|
|
73
78
|
kysely: kysely$1,
|
|
74
79
|
adapter: adapter$1,
|
|
75
|
-
kyselyPglite: kyselyPglite$1
|
|
80
|
+
kyselyPglite: kyselyPglite$1,
|
|
81
|
+
orm: adapter$1.createQueryEngine(schema, namespace)
|
|
76
82
|
};
|
|
77
83
|
};
|
|
78
|
-
let { kysely, adapter, kyselyPglite } = await createDatabase();
|
|
84
|
+
let { kysely, adapter, kyselyPglite, orm } = await createDatabase();
|
|
79
85
|
const resetDatabase = async () => {
|
|
80
86
|
await kysely.destroy();
|
|
81
87
|
try {
|
|
@@ -85,6 +91,7 @@ async function createKyselyPgliteAdapter(config, schema, namespace, migrateToVer
|
|
|
85
91
|
kysely = newDb.kysely;
|
|
86
92
|
adapter = newDb.adapter;
|
|
87
93
|
kyselyPglite = newDb.kyselyPglite;
|
|
94
|
+
orm = newDb.orm;
|
|
88
95
|
};
|
|
89
96
|
const cleanup = async () => {
|
|
90
97
|
await kysely.destroy();
|
|
@@ -98,6 +105,9 @@ async function createKyselyPgliteAdapter(config, schema, namespace, migrateToVer
|
|
|
98
105
|
};
|
|
99
106
|
return {
|
|
100
107
|
testContext: {
|
|
108
|
+
get db() {
|
|
109
|
+
return orm;
|
|
110
|
+
},
|
|
101
111
|
get kysely() {
|
|
102
112
|
return kysely;
|
|
103
113
|
},
|
|
@@ -145,17 +155,19 @@ async function createDrizzlePgliteAdapter(config, schema, namespace, _migrateToV
|
|
|
145
155
|
const db = drizzle(pglite$1, { schema: schemaModule });
|
|
146
156
|
const migrationStatements = await generateMigration(generateDrizzleJson({}), generateDrizzleJson(schemaModule));
|
|
147
157
|
for (const statement of migrationStatements) await db.execute(statement);
|
|
158
|
+
const adapter$1 = new DrizzleAdapter({
|
|
159
|
+
db: () => db,
|
|
160
|
+
provider: "postgresql"
|
|
161
|
+
});
|
|
148
162
|
return {
|
|
149
163
|
drizzle: db,
|
|
150
|
-
adapter:
|
|
151
|
-
db: () => db,
|
|
152
|
-
provider: "postgresql"
|
|
153
|
-
}),
|
|
164
|
+
adapter: adapter$1,
|
|
154
165
|
pglite: pglite$1,
|
|
155
|
-
cleanup: cleanup$1
|
|
166
|
+
cleanup: cleanup$1,
|
|
167
|
+
orm: adapter$1.createQueryEngine(schema, namespace)
|
|
156
168
|
};
|
|
157
169
|
};
|
|
158
|
-
let { drizzle: drizzleDb, adapter, pglite, cleanup: schemaCleanup } = await createDatabase();
|
|
170
|
+
let { drizzle: drizzleDb, adapter, pglite, cleanup: schemaCleanup, orm } = await createDatabase();
|
|
159
171
|
const resetDatabase = async () => {
|
|
160
172
|
await pglite.close();
|
|
161
173
|
await schemaCleanup();
|
|
@@ -164,6 +176,7 @@ async function createDrizzlePgliteAdapter(config, schema, namespace, _migrateToV
|
|
|
164
176
|
adapter = newDb.adapter;
|
|
165
177
|
pglite = newDb.pglite;
|
|
166
178
|
schemaCleanup = newDb.cleanup;
|
|
179
|
+
orm = newDb.orm;
|
|
167
180
|
};
|
|
168
181
|
const cleanup = async () => {
|
|
169
182
|
await pglite.close();
|
|
@@ -175,6 +188,9 @@ async function createDrizzlePgliteAdapter(config, schema, namespace, _migrateToV
|
|
|
175
188
|
};
|
|
176
189
|
return {
|
|
177
190
|
testContext: {
|
|
191
|
+
get db() {
|
|
192
|
+
return orm;
|
|
193
|
+
},
|
|
178
194
|
get drizzle() {
|
|
179
195
|
return drizzleDb;
|
|
180
196
|
},
|
package/dist/adapters.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapters.js","names":["kysely","adapter","kyselyPglite","cleanup","pglite"],"sources":["../src/adapters.ts"],"sourcesContent":["import { Kysely } from \"kysely\";\nimport { SQLocalKysely } from \"sqlocal/kysely\";\nimport { KyselyPGlite } from \"kysely-pglite\";\nimport { drizzle } from \"drizzle-orm/pglite\";\nimport { PGlite } from \"@electric-sql/pglite\";\nimport { KyselyAdapter } from \"@fragno-dev/db/adapters/kysely\";\nimport { DrizzleAdapter } from \"@fragno-dev/db/adapters/drizzle\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db/adapters\";\nimport { createRequire } from \"node:module\";\nimport { mkdir, writeFile, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\n// Adapter configuration types\nexport interface KyselySqliteAdapter {\n type: \"kysely-sqlite\";\n}\n\nexport interface KyselyPgliteAdapter {\n type: \"kysely-pglite\";\n databasePath?: string;\n}\n\nexport interface DrizzlePgliteAdapter {\n type: \"drizzle-pglite\";\n databasePath?: string;\n}\n\nexport type SupportedAdapter = KyselySqliteAdapter | KyselyPgliteAdapter | DrizzlePgliteAdapter;\n\n// Conditional return types based on adapter\nexport type TestContext<T extends SupportedAdapter> = T extends\n | KyselySqliteAdapter\n | KyselyPgliteAdapter\n ? {\n readonly kysely: Kysely<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n readonly adapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n resetDatabase: () => Promise<void>;\n cleanup: () => Promise<void>;\n }\n : T extends DrizzlePgliteAdapter\n ? {\n readonly drizzle: ReturnType<typeof drizzle<any>>; // eslint-disable-line @typescript-eslint/no-explicit-any\n readonly adapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n resetDatabase: () => Promise<void>;\n cleanup: () => Promise<void>;\n }\n : never;\n\n// Factory function return type\ninterface AdapterFactoryResult<T extends SupportedAdapter> {\n testContext: TestContext<T>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n adapter: DatabaseAdapter<any>;\n}\n\n/**\n * Create Kysely + SQLite adapter using SQLocalKysely (always in-memory)\n */\nexport async function createKyselySqliteAdapter(\n _config: KyselySqliteAdapter,\n schema: AnySchema,\n namespace: string,\n migrateToVersion?: number,\n): Promise<AdapterFactoryResult<KyselySqliteAdapter>> {\n // Helper to create a new database instance and run migrations\n const createDatabase = async () => {\n // Create SQLocalKysely instance (always in-memory for tests)\n const { dialect } = new SQLocalKysely(\":memory:\");\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const kysely = new Kysely<any>({\n dialect,\n });\n\n // Create KyselyAdapter\n const adapter = new KyselyAdapter({\n db: kysely,\n provider: \"sqlite\",\n });\n\n // Run migrations\n const migrator = adapter.createMigrationEngine(schema, namespace);\n const preparedMigration = migrateToVersion\n ? await migrator.prepareMigrationTo(migrateToVersion, {\n updateSettings: false,\n })\n : await migrator.prepareMigration({\n updateSettings: false,\n });\n await preparedMigration.execute();\n\n return { kysely, adapter };\n };\n\n // Create initial database\n let { kysely, adapter } = await createDatabase();\n\n // Reset database function - creates a fresh in-memory database and re-runs migrations\n const resetDatabase = async () => {\n // Destroy the old Kysely instance\n await kysely.destroy();\n\n // Create a new database instance\n const newDb = await createDatabase();\n kysely = newDb.kysely;\n adapter = newDb.adapter;\n };\n\n // Cleanup function - closes connections (no files to delete for in-memory)\n const cleanup = async () => {\n await kysely.destroy();\n };\n\n return {\n testContext: {\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create Kysely + PGLite adapter using kysely-pglite\n */\nexport async function createKyselyPgliteAdapter(\n config: KyselyPgliteAdapter,\n schema: AnySchema,\n namespace: string,\n migrateToVersion?: number,\n): Promise<AdapterFactoryResult<KyselyPgliteAdapter>> {\n const databasePath = config.databasePath;\n\n // Helper to create a new database instance and run migrations\n const createDatabase = async () => {\n // Create KyselyPGlite instance\n const kyselyPglite = await KyselyPGlite.create(databasePath);\n\n // Create Kysely instance with PGlite dialect\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const kysely = new Kysely<any>({\n dialect: kyselyPglite.dialect,\n });\n\n // Create KyselyAdapter\n const adapter = new KyselyAdapter({\n db: kysely,\n provider: \"postgresql\",\n });\n\n // Run migrations\n const migrator = adapter.createMigrationEngine(schema, namespace);\n const preparedMigration = migrateToVersion\n ? await migrator.prepareMigrationTo(migrateToVersion, {\n updateSettings: false,\n })\n : await migrator.prepareMigration({\n updateSettings: false,\n });\n await preparedMigration.execute();\n\n return { kysely, adapter, kyselyPglite };\n };\n\n // Create initial database\n let { kysely, adapter, kyselyPglite } = await createDatabase();\n\n // Reset database function - creates a fresh database and re-runs migrations\n const resetDatabase = async () => {\n // Close the old instances\n await kysely.destroy();\n\n try {\n await kyselyPglite.client.close();\n } catch {\n // Ignore if already closed\n }\n\n // Create a new database instance\n const newDb = await createDatabase();\n kysely = newDb.kysely;\n adapter = newDb.adapter;\n kyselyPglite = newDb.kyselyPglite;\n };\n\n // Cleanup function - closes connections and deletes database directory\n const cleanup = async () => {\n await kysely.destroy();\n\n try {\n await kyselyPglite.client.close();\n } catch {\n // Ignore if already closed\n }\n\n // Delete the database directory if it exists and is a file path\n if (databasePath && databasePath !== \":memory:\" && existsSync(databasePath)) {\n await rm(databasePath, { recursive: true, force: true });\n }\n };\n\n return {\n testContext: {\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create Drizzle + PGLite adapter using drizzle-orm/pglite\n */\nexport async function createDrizzlePgliteAdapter(\n config: DrizzlePgliteAdapter,\n schema: AnySchema,\n namespace: string,\n _migrateToVersion?: number,\n): Promise<AdapterFactoryResult<DrizzlePgliteAdapter>> {\n const databasePath = config.databasePath;\n\n // Import drizzle-kit for migrations\n const require = createRequire(import.meta.url);\n const { generateDrizzleJson, generateMigration } =\n require(\"drizzle-kit/api\") as typeof import(\"drizzle-kit/api\");\n\n // Import generateSchema from the properly exported module\n const { generateSchema } = await import(\"@fragno-dev/db/adapters/drizzle/generate\");\n\n // Helper to write schema to file and dynamically import it\n const writeAndLoadSchema = async () => {\n const testDir = join(import.meta.dirname, \"_generated\", \"drizzle-test\");\n await mkdir(testDir, { recursive: true }).catch(() => {\n // Ignore error if directory already exists\n });\n\n const schemaFilePath = join(\n testDir,\n `test-schema-${Date.now()}-${Math.random().toString(36).slice(2, 9)}.ts`,\n );\n\n // Generate and write the Drizzle schema to file\n const drizzleSchemaTs = generateSchema([{ namespace: namespace ?? \"\", schema }], \"postgresql\");\n await writeFile(schemaFilePath, drizzleSchemaTs, \"utf-8\");\n\n // Dynamically import the generated schema (with cache busting)\n const schemaModule = await import(`${schemaFilePath}?t=${Date.now()}`);\n\n const cleanup = async () => {\n await rm(testDir, { recursive: true, force: true });\n };\n\n return { schemaModule, cleanup };\n };\n\n // Helper to create a new database instance and run migrations\n const createDatabase = async () => {\n // Write schema to file and load it\n const { schemaModule, cleanup } = await writeAndLoadSchema();\n\n // Create PGlite instance\n const pglite = new PGlite(databasePath);\n\n // Create Drizzle instance with PGlite\n const db = drizzle(pglite, {\n schema: schemaModule,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n }) as any;\n\n // Generate and run migrations\n const migrationStatements = await generateMigration(\n generateDrizzleJson({}), // Empty schema (starting state)\n generateDrizzleJson(schemaModule), // Target schema\n );\n\n // Execute migration SQL\n for (const statement of migrationStatements) {\n await db.execute(statement);\n }\n\n // Create DrizzleAdapter\n const adapter = new DrizzleAdapter({\n db: () => db,\n provider: \"postgresql\",\n });\n\n return { drizzle: db, adapter, pglite, cleanup };\n };\n\n // Create initial database\n let { drizzle: drizzleDb, adapter, pglite, cleanup: schemaCleanup } = await createDatabase();\n\n // Reset database function - creates a fresh database and re-runs migrations\n const resetDatabase = async () => {\n // Close the old instances and cleanup\n await pglite.close();\n await schemaCleanup();\n\n // Create a new database instance\n const newDb = await createDatabase();\n drizzleDb = newDb.drizzle;\n adapter = newDb.adapter;\n pglite = newDb.pglite;\n schemaCleanup = newDb.cleanup;\n };\n\n // Cleanup function - closes connections and deletes generated files and database directory\n const cleanup = async () => {\n await pglite.close();\n await schemaCleanup();\n\n // Delete the database directory if it exists and is a file path\n if (databasePath && databasePath !== \":memory:\" && existsSync(databasePath)) {\n await rm(databasePath, { recursive: true, force: true });\n }\n };\n\n return {\n testContext: {\n get drizzle() {\n return drizzleDb;\n },\n get adapter() {\n return adapter;\n },\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create adapter based on configuration\n */\nexport async function createAdapter<T extends SupportedAdapter>(\n adapterConfig: T,\n schema: AnySchema,\n namespace: string,\n migrateToVersion?: number,\n): Promise<AdapterFactoryResult<T>> {\n if (adapterConfig.type === \"kysely-sqlite\") {\n return createKyselySqliteAdapter(adapterConfig, schema, namespace, migrateToVersion) as Promise<\n AdapterFactoryResult<T>\n >;\n } else if (adapterConfig.type === \"kysely-pglite\") {\n return createKyselyPgliteAdapter(adapterConfig, schema, namespace, migrateToVersion) as Promise<\n AdapterFactoryResult<T>\n >;\n } else if (adapterConfig.type === \"drizzle-pglite\") {\n return createDrizzlePgliteAdapter(\n adapterConfig,\n schema,\n namespace,\n migrateToVersion,\n ) as Promise<AdapterFactoryResult<T>>;\n }\n\n throw new Error(`Unsupported adapter type: ${(adapterConfig as SupportedAdapter).type}`);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"adapters.js","names":["kysely","adapter","kyselyPglite","cleanup","pglite"],"sources":["../src/adapters.ts"],"sourcesContent":["import { Kysely } from \"kysely\";\nimport { SQLocalKysely } from \"sqlocal/kysely\";\nimport { KyselyPGlite } from \"kysely-pglite\";\nimport { drizzle } from \"drizzle-orm/pglite\";\nimport { PGlite } from \"@electric-sql/pglite\";\nimport { KyselyAdapter } from \"@fragno-dev/db/adapters/kysely\";\nimport { DrizzleAdapter } from \"@fragno-dev/db/adapters/drizzle\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db/adapters\";\nimport type { AbstractQuery } from \"@fragno-dev/db/query\";\nimport { createRequire } from \"node:module\";\nimport { mkdir, writeFile, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\n// Adapter configuration types\nexport interface KyselySqliteAdapter {\n type: \"kysely-sqlite\";\n}\n\nexport interface KyselyPgliteAdapter {\n type: \"kysely-pglite\";\n databasePath?: string;\n}\n\nexport interface DrizzlePgliteAdapter {\n type: \"drizzle-pglite\";\n databasePath?: string;\n}\n\nexport type SupportedAdapter = KyselySqliteAdapter | KyselyPgliteAdapter | DrizzlePgliteAdapter;\n\n// Conditional return types based on adapter\nexport type TestContext<T extends SupportedAdapter> = T extends\n | KyselySqliteAdapter\n | KyselyPgliteAdapter\n ? {\n readonly db: AbstractQuery<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n readonly kysely: Kysely<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n readonly adapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n resetDatabase: () => Promise<void>;\n cleanup: () => Promise<void>;\n }\n : T extends DrizzlePgliteAdapter\n ? {\n readonly db: AbstractQuery<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n readonly drizzle: ReturnType<typeof drizzle<any>>; // eslint-disable-line @typescript-eslint/no-explicit-any\n readonly adapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n resetDatabase: () => Promise<void>;\n cleanup: () => Promise<void>;\n }\n : never;\n\n// Factory function return type\ninterface AdapterFactoryResult<T extends SupportedAdapter> {\n testContext: TestContext<T>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n adapter: DatabaseAdapter<any>;\n}\n\n/**\n * Create Kysely + SQLite adapter using SQLocalKysely (always in-memory)\n */\nexport async function createKyselySqliteAdapter(\n _config: KyselySqliteAdapter,\n schema: AnySchema,\n namespace: string,\n migrateToVersion?: number,\n): Promise<AdapterFactoryResult<KyselySqliteAdapter>> {\n // Helper to create a new database instance and run migrations\n const createDatabase = async () => {\n // Create SQLocalKysely instance (always in-memory for tests)\n const { dialect } = new SQLocalKysely(\":memory:\");\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const kysely = new Kysely<any>({\n dialect,\n });\n\n // Create KyselyAdapter\n const adapter = new KyselyAdapter({\n db: kysely,\n provider: \"sqlite\",\n });\n\n // Run migrations\n const migrator = adapter.createMigrationEngine(schema, namespace);\n const preparedMigration = migrateToVersion\n ? await migrator.prepareMigrationTo(migrateToVersion, {\n updateSettings: false,\n })\n : await migrator.prepareMigration({\n updateSettings: false,\n });\n await preparedMigration.execute();\n\n // Create ORM instance\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const orm = adapter.createQueryEngine(schema, namespace) as AbstractQuery<any>;\n\n return { kysely, adapter, orm };\n };\n\n // Create initial database\n let { kysely, adapter, orm } = await createDatabase();\n\n // Reset database function - creates a fresh in-memory database and re-runs migrations\n const resetDatabase = async () => {\n // Destroy the old Kysely instance\n await kysely.destroy();\n\n // Create a new database instance\n const newDb = await createDatabase();\n kysely = newDb.kysely;\n adapter = newDb.adapter;\n orm = newDb.orm;\n };\n\n // Cleanup function - closes connections (no files to delete for in-memory)\n const cleanup = async () => {\n await kysely.destroy();\n };\n\n return {\n testContext: {\n get db() {\n return orm;\n },\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create Kysely + PGLite adapter using kysely-pglite\n */\nexport async function createKyselyPgliteAdapter(\n config: KyselyPgliteAdapter,\n schema: AnySchema,\n namespace: string,\n migrateToVersion?: number,\n): Promise<AdapterFactoryResult<KyselyPgliteAdapter>> {\n const databasePath = config.databasePath;\n\n // Helper to create a new database instance and run migrations\n const createDatabase = async () => {\n // Create KyselyPGlite instance\n const kyselyPglite = await KyselyPGlite.create(databasePath);\n\n // Create Kysely instance with PGlite dialect\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const kysely = new Kysely<any>({\n dialect: kyselyPglite.dialect,\n });\n\n // Create KyselyAdapter\n const adapter = new KyselyAdapter({\n db: kysely,\n provider: \"postgresql\",\n });\n\n // Run migrations\n const migrator = adapter.createMigrationEngine(schema, namespace);\n const preparedMigration = migrateToVersion\n ? await migrator.prepareMigrationTo(migrateToVersion, {\n updateSettings: false,\n })\n : await migrator.prepareMigration({\n updateSettings: false,\n });\n await preparedMigration.execute();\n\n // Create ORM instance\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const orm = adapter.createQueryEngine(schema, namespace) as AbstractQuery<any>;\n\n return { kysely, adapter, kyselyPglite, orm };\n };\n\n // Create initial database\n let { kysely, adapter, kyselyPglite, orm } = await createDatabase();\n\n // Reset database function - creates a fresh database and re-runs migrations\n const resetDatabase = async () => {\n // Close the old instances\n await kysely.destroy();\n\n try {\n await kyselyPglite.client.close();\n } catch {\n // Ignore if already closed\n }\n\n // Create a new database instance\n const newDb = await createDatabase();\n kysely = newDb.kysely;\n adapter = newDb.adapter;\n kyselyPglite = newDb.kyselyPglite;\n orm = newDb.orm;\n };\n\n // Cleanup function - closes connections and deletes database directory\n const cleanup = async () => {\n await kysely.destroy();\n\n try {\n await kyselyPglite.client.close();\n } catch {\n // Ignore if already closed\n }\n\n // Delete the database directory if it exists and is a file path\n if (databasePath && databasePath !== \":memory:\" && existsSync(databasePath)) {\n await rm(databasePath, { recursive: true, force: true });\n }\n };\n\n return {\n testContext: {\n get db() {\n return orm;\n },\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create Drizzle + PGLite adapter using drizzle-orm/pglite\n */\nexport async function createDrizzlePgliteAdapter(\n config: DrizzlePgliteAdapter,\n schema: AnySchema,\n namespace: string,\n _migrateToVersion?: number,\n): Promise<AdapterFactoryResult<DrizzlePgliteAdapter>> {\n const databasePath = config.databasePath;\n\n // Import drizzle-kit for migrations\n const require = createRequire(import.meta.url);\n const { generateDrizzleJson, generateMigration } =\n require(\"drizzle-kit/api\") as typeof import(\"drizzle-kit/api\");\n\n // Import generateSchema from the properly exported module\n const { generateSchema } = await import(\"@fragno-dev/db/adapters/drizzle/generate\");\n\n // Helper to write schema to file and dynamically import it\n const writeAndLoadSchema = async () => {\n const testDir = join(import.meta.dirname, \"_generated\", \"drizzle-test\");\n await mkdir(testDir, { recursive: true }).catch(() => {\n // Ignore error if directory already exists\n });\n\n const schemaFilePath = join(\n testDir,\n `test-schema-${Date.now()}-${Math.random().toString(36).slice(2, 9)}.ts`,\n );\n\n // Generate and write the Drizzle schema to file\n const drizzleSchemaTs = generateSchema([{ namespace: namespace ?? \"\", schema }], \"postgresql\");\n await writeFile(schemaFilePath, drizzleSchemaTs, \"utf-8\");\n\n // Dynamically import the generated schema (with cache busting)\n const schemaModule = await import(`${schemaFilePath}?t=${Date.now()}`);\n\n const cleanup = async () => {\n await rm(testDir, { recursive: true, force: true });\n };\n\n return { schemaModule, cleanup };\n };\n\n // Helper to create a new database instance and run migrations\n const createDatabase = async () => {\n // Write schema to file and load it\n const { schemaModule, cleanup } = await writeAndLoadSchema();\n\n // Create PGlite instance\n const pglite = new PGlite(databasePath);\n\n // Create Drizzle instance with PGlite\n const db = drizzle(pglite, {\n schema: schemaModule,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n }) as any;\n\n // Generate and run migrations\n const migrationStatements = await generateMigration(\n generateDrizzleJson({}), // Empty schema (starting state)\n generateDrizzleJson(schemaModule), // Target schema\n );\n\n // Execute migration SQL\n for (const statement of migrationStatements) {\n await db.execute(statement);\n }\n\n // Create DrizzleAdapter\n const adapter = new DrizzleAdapter({\n db: () => db,\n provider: \"postgresql\",\n });\n\n // Create ORM instance\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const orm = adapter.createQueryEngine(schema, namespace) as AbstractQuery<any>;\n\n return { drizzle: db, adapter, pglite, cleanup, orm };\n };\n\n // Create initial database\n let { drizzle: drizzleDb, adapter, pglite, cleanup: schemaCleanup, orm } = await createDatabase();\n\n // Reset database function - creates a fresh database and re-runs migrations\n const resetDatabase = async () => {\n // Close the old instances and cleanup\n await pglite.close();\n await schemaCleanup();\n\n // Create a new database instance\n const newDb = await createDatabase();\n drizzleDb = newDb.drizzle;\n adapter = newDb.adapter;\n pglite = newDb.pglite;\n schemaCleanup = newDb.cleanup;\n orm = newDb.orm;\n };\n\n // Cleanup function - closes connections and deletes generated files and database directory\n const cleanup = async () => {\n await pglite.close();\n await schemaCleanup();\n\n // Delete the database directory if it exists and is a file path\n if (databasePath && databasePath !== \":memory:\" && existsSync(databasePath)) {\n await rm(databasePath, { recursive: true, force: true });\n }\n };\n\n return {\n testContext: {\n get db() {\n return orm;\n },\n get drizzle() {\n return drizzleDb;\n },\n get adapter() {\n return adapter;\n },\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create adapter based on configuration\n */\nexport async function createAdapter<T extends SupportedAdapter>(\n adapterConfig: T,\n schema: AnySchema,\n namespace: string,\n migrateToVersion?: number,\n): Promise<AdapterFactoryResult<T>> {\n if (adapterConfig.type === \"kysely-sqlite\") {\n return createKyselySqliteAdapter(adapterConfig, schema, namespace, migrateToVersion) as Promise<\n AdapterFactoryResult<T>\n >;\n } else if (adapterConfig.type === \"kysely-pglite\") {\n return createKyselyPgliteAdapter(adapterConfig, schema, namespace, migrateToVersion) as Promise<\n AdapterFactoryResult<T>\n >;\n } else if (adapterConfig.type === \"drizzle-pglite\") {\n return createDrizzlePgliteAdapter(\n adapterConfig,\n schema,\n namespace,\n migrateToVersion,\n ) as Promise<AdapterFactoryResult<T>>;\n }\n\n throw new Error(`Unsupported adapter type: ${(adapterConfig as SupportedAdapter).type}`);\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { DrizzlePgliteAdapter, KyselyPgliteAdapter, KyselySqliteAdapter, SupportedAdapter, TestContext } from "./adapters.js";
|
|
2
|
-
import { CreateFragmentForTestOptions, CreateFragmentForTestOptions as CreateFragmentForTestOptions$1, FragmentForTest, FragmentForTest as FragmentForTest$1,
|
|
2
|
+
import { CreateFragmentForTestOptions, CreateFragmentForTestOptions as CreateFragmentForTestOptions$1, FragmentForTest, FragmentForTest as FragmentForTest$1, RouteHandlerInputOptions, createFragmentForTest } from "@fragno-dev/core/test";
|
|
3
3
|
import { AnySchema } from "@fragno-dev/db/schema";
|
|
4
4
|
import { FragnoPublicConfig } from "@fragno-dev/core/api/fragment-instantiation";
|
|
5
5
|
import { FragmentDefinition } from "@fragno-dev/core/api/fragment-builder";
|
|
6
|
+
import { AnyRouteOrFactory, FlattenRouteFactories } from "@fragno-dev/core/api/route";
|
|
7
|
+
import { FragnoRouteConfig } from "@fragno-dev/core";
|
|
8
|
+
import { HTTPMethod } from "@fragno-dev/core/api";
|
|
9
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
6
10
|
|
|
7
11
|
//#region src/index.d.ts
|
|
8
12
|
|
|
@@ -18,20 +22,19 @@ interface CreateDatabaseFragmentForTestOptions<TConfig, TDeps, TServices, TAddit
|
|
|
18
22
|
* Result of creating a database fragment for testing
|
|
19
23
|
* All properties are getters that return the current fragment instance
|
|
20
24
|
*/
|
|
21
|
-
interface DatabaseFragmentTestResult<TConfig, TDeps, TServices, TAdditionalContext extends Record<string, unknown>, TOptions extends FragnoPublicConfig, TAdapter extends SupportedAdapter> {
|
|
22
|
-
readonly fragment: FragmentForTest$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions>;
|
|
25
|
+
interface DatabaseFragmentTestResult<TConfig, TDeps, TServices, TAdditionalContext extends Record<string, unknown>, TOptions extends FragnoPublicConfig, TAdapter extends SupportedAdapter, TRoutes extends readonly FragnoRouteConfig<HTTPMethod, string, StandardSchemaV1 | undefined, StandardSchemaV1 | undefined, string, string>[]> {
|
|
26
|
+
readonly fragment: FragmentForTest$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TRoutes>;
|
|
23
27
|
readonly services: TServices;
|
|
24
|
-
readonly
|
|
25
|
-
readonly handler: FragmentForTest$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions>["handler"];
|
|
28
|
+
readonly callRoute: FragmentForTest$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TRoutes>["callRoute"];
|
|
26
29
|
readonly config: TConfig;
|
|
27
30
|
readonly deps: TDeps;
|
|
28
31
|
readonly additionalContext: TAdditionalContext;
|
|
29
32
|
test: TestContext<TAdapter>;
|
|
30
33
|
}
|
|
31
|
-
declare function createDatabaseFragmentForTest<const TConfig, const TDeps, const TServices extends Record<string, unknown>, const TAdditionalContext extends Record<string, unknown>, const TOptions extends FragnoPublicConfig, const TSchema extends AnySchema, const TAdapter extends SupportedAdapter>(fragmentBuilder: {
|
|
34
|
+
declare function createDatabaseFragmentForTest<const TConfig, const TDeps, const TServices extends Record<string, unknown>, const TAdditionalContext extends Record<string, unknown>, const TOptions extends FragnoPublicConfig, const TSchema extends AnySchema, const TAdapter extends SupportedAdapter, const TRoutesOrFactories extends readonly AnyRouteOrFactory[]>(fragmentBuilder: {
|
|
32
35
|
definition: FragmentDefinition<TConfig, TDeps, TServices, TAdditionalContext>;
|
|
33
36
|
$requiredOptions: TOptions;
|
|
34
|
-
}, options: CreateDatabaseFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TAdapter>): Promise<DatabaseFragmentTestResult<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TAdapter
|
|
37
|
+
}, routesOrFactories: TRoutesOrFactories, options: CreateDatabaseFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TAdapter>): Promise<DatabaseFragmentTestResult<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TAdapter, FlattenRouteFactories<TRoutesOrFactories>>>;
|
|
35
38
|
//#endregion
|
|
36
|
-
export { CreateDatabaseFragmentForTestOptions, type CreateFragmentForTestOptions, DatabaseFragmentTestResult, type DrizzlePgliteAdapter, type FragmentForTest, type
|
|
39
|
+
export { CreateDatabaseFragmentForTestOptions, type CreateFragmentForTestOptions, DatabaseFragmentTestResult, type DrizzlePgliteAdapter, type FragmentForTest, type KyselyPgliteAdapter, type KyselySqliteAdapter, type RouteHandlerInputOptions, type SupportedAdapter, type TestContext, createDatabaseFragmentForTest, createFragmentForTest };
|
|
37
40
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;AAyCA;;;AAMmB,UANF,oCAME,CAAA,OAAA,EAAA,KAAA,EAAA,SAAA,EAAA,2BAFU,MAEV,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,iBADA,kBACA,EAAA,iBAAA,gBAAA,CAAA,SACT,IADS,CAEf,8BAFe,CAEc,OAFd,EAEuB,KAFvB,EAE8B,SAF9B,EAEyC,kBAFzC,EAE6D,QAF7D,CAAA,EAAA,QAAA,CAAA,CAAA;EAEc,OAAA,EAGtB,QAHsB;EAAS,gBAAA,CAAA,EAAA,MAAA;EAAO,MAAA,CAAA,EAKtC,OALsC;;;;;;AADvC,UAaO,0BAbP,CAAA,OAAA,EAAA,KAAA,EAAA,SAAA,EAAA,2BAiBmB,MAjBnB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,iBAkBS,kBAlBT,EAAA,iBAmBS,gBAnBT,EAAA,gBAAA,SAoBiB,iBApBjB,CAqBN,UArBM,EAAA,MAAA,EAuBN,gBAvBM,GAAA,SAAA,EAwBN,gBAxBM,GAAA,SAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAAA,CAAA,CAAA;EAAI,SAAA,QAAA,EA6BO,iBA7BP,CA8BV,OA9BU,EA+BV,KA/BU,EAgCV,SAhCU,EAiCV,kBAjCU,EAkCV,QAlCU,EAmCV,OAnCU,CAAA;EAaG,SAAA,QAAA,EAwBI,SAxBJ;EAIY,SAAA,SAAA,EAqBP,iBArBO,CAsBzB,OAtByB,EAuBzB,KAvByB,EAwBzB,SAxByB,EAyBzB,kBAzByB,EA0BzB,QA1ByB,EA2BzB,OA3ByB,CAAA,CAAA,WAAA,CAAA;EACV,SAAA,MAAA,EA4BA,OA5BA;EACA,SAAA,IAAA,EA4BF,KA5BE;EAEf,SAAA,iBAAA,EA2B0B,kBA3B1B;EAEA,IAAA,EA0BI,WA1BJ,CA0BgB,QA1BhB,CAAA;;AAHuB,iBAgCL,6BAhCK,CAAA,aAAA,EAAA,WAAA,EAAA,wBAmCD,MAnCC,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,iCAoCQ,MApCR,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,uBAqCF,kBArCE,EAAA,sBAsCH,SAtCG,EAAA,uBAuCF,gBAvCE,EAAA,iCAAA,SAwCiB,iBAxCjB,EAAA,CAAA,CAAA,eAAA,EAAA;EAUvB,UAAA,EAiCY,kBAjCZ,CAiC+B,OAjC/B,EAiCwC,KAjCxC,EAiC+C,SAjC/C,EAiC0D,kBAjC1D,CAAA;EACA,gBAAA,EAiCkB,QAjClB;CACA,EAAA,iBAAA,EAkCiB,kBAlCjB,EAAA,OAAA,EAmCO,oCAnCP,CAoCA,OApCA,EAqCA,KArCA,EAsCA,SAtCA,EAuCA,kBAvCA,EAwCA,QAxCA,EAyCA,QAzCA,CAAA,CAAA,EA2CD,OA3CC,CA4CF,0BA5CE,CA6CA,OA7CA,EA8CA,KA9CA,EA+CA,SA/CA,EAgDA,kBAhDA,EAiDA,QAjDA,EAkDA,QAlDA,EAmDA,qBAnDA,CAmDsB,kBAnDtB,CAAA,CAAA,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createAdapter } from "./adapters.js";
|
|
|
2
2
|
import { createFragmentForTest, createFragmentForTest as createFragmentForTest$1 } from "@fragno-dev/core/test";
|
|
3
3
|
|
|
4
4
|
//#region src/index.ts
|
|
5
|
-
async function createDatabaseFragmentForTest(fragmentBuilder, options) {
|
|
5
|
+
async function createDatabaseFragmentForTest(fragmentBuilder, routesOrFactories, options) {
|
|
6
6
|
const { adapter: adapterConfig, migrateToVersion, config, options: fragmentOptions, deps, services, additionalContext } = options;
|
|
7
7
|
const fragmentAdditionalContext = fragmentBuilder.definition.additionalContext;
|
|
8
8
|
const schema = fragmentAdditionalContext?.databaseSchema;
|
|
@@ -13,7 +13,7 @@ async function createDatabaseFragmentForTest(fragmentBuilder, options) {
|
|
|
13
13
|
...fragmentOptions,
|
|
14
14
|
databaseAdapter: adapter
|
|
15
15
|
};
|
|
16
|
-
let fragment = createFragmentForTest$1(fragmentBuilder, {
|
|
16
|
+
let fragment = createFragmentForTest$1(fragmentBuilder, routesOrFactories, {
|
|
17
17
|
config,
|
|
18
18
|
options: mergedOptions,
|
|
19
19
|
deps,
|
|
@@ -28,11 +28,8 @@ async function createDatabaseFragmentForTest(fragmentBuilder, options) {
|
|
|
28
28
|
get services() {
|
|
29
29
|
return fragment.services;
|
|
30
30
|
},
|
|
31
|
-
get
|
|
32
|
-
return fragment.
|
|
33
|
-
},
|
|
34
|
-
get handler() {
|
|
35
|
-
return fragment.handler;
|
|
31
|
+
get callRoute() {
|
|
32
|
+
return fragment.callRoute;
|
|
36
33
|
},
|
|
37
34
|
get config() {
|
|
38
35
|
return fragment.config;
|
|
@@ -44,6 +41,12 @@ async function createDatabaseFragmentForTest(fragmentBuilder, options) {
|
|
|
44
41
|
return fragment.additionalContext;
|
|
45
42
|
},
|
|
46
43
|
test: Object.create(originalTestContext, {
|
|
44
|
+
db: {
|
|
45
|
+
get() {
|
|
46
|
+
return originalTestContext.db;
|
|
47
|
+
},
|
|
48
|
+
enumerable: true
|
|
49
|
+
},
|
|
47
50
|
kysely: {
|
|
48
51
|
get() {
|
|
49
52
|
return originalTestContext.kysely;
|
|
@@ -69,7 +72,7 @@ async function createDatabaseFragmentForTest(fragmentBuilder, options) {
|
|
|
69
72
|
...fragmentOptions,
|
|
70
73
|
databaseAdapter: originalTestContext.adapter
|
|
71
74
|
};
|
|
72
|
-
fragment = createFragmentForTest$1(fragmentBuilder, {
|
|
75
|
+
fragment = createFragmentForTest$1(fragmentBuilder, routesOrFactories, {
|
|
73
76
|
config,
|
|
74
77
|
options: mergedOptions,
|
|
75
78
|
deps,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["createFragmentForTest"],"sources":["../src/index.ts"],"sourcesContent":["import type { AnySchema } from \"@fragno-dev/db/schema\";\nimport {\n createFragmentForTest,\n type FragmentForTest,\n type CreateFragmentForTestOptions,\n} from \"@fragno-dev/core/test\";\nimport type { FragnoPublicConfig } from \"@fragno-dev/core/api/fragment-instantiation\";\nimport type { FragmentDefinition } from \"@fragno-dev/core/api/fragment-builder\";\nimport {\n createAdapter,\n type SupportedAdapter,\n type TestContext,\n type KyselySqliteAdapter,\n type KyselyPgliteAdapter,\n type DrizzlePgliteAdapter,\n} from \"./adapters\";\n\n// Re-export utilities from @fragno-dev/core/test\nexport {\n createFragmentForTest,\n type
|
|
1
|
+
{"version":3,"file":"index.js","names":["createFragmentForTest"],"sources":["../src/index.ts"],"sourcesContent":["import type { AnySchema } from \"@fragno-dev/db/schema\";\nimport {\n createFragmentForTest,\n type FragmentForTest,\n type CreateFragmentForTestOptions,\n} from \"@fragno-dev/core/test\";\nimport type { FragnoPublicConfig } from \"@fragno-dev/core/api/fragment-instantiation\";\nimport type { FragmentDefinition } from \"@fragno-dev/core/api/fragment-builder\";\nimport type { AnyRouteOrFactory, FlattenRouteFactories } from \"@fragno-dev/core/api/route\";\nimport {\n createAdapter,\n type SupportedAdapter,\n type TestContext,\n type KyselySqliteAdapter,\n type KyselyPgliteAdapter,\n type DrizzlePgliteAdapter,\n} from \"./adapters\";\nimport type { FragnoRouteConfig } from \"@fragno-dev/core\";\nimport type { HTTPMethod } from \"@fragno-dev/core/api\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n// Re-export utilities from @fragno-dev/core/test\nexport {\n createFragmentForTest,\n type CreateFragmentForTestOptions,\n type RouteHandlerInputOptions,\n type FragmentForTest,\n} from \"@fragno-dev/core/test\";\n\n// Re-export adapter types\nexport type {\n SupportedAdapter,\n KyselySqliteAdapter,\n KyselyPgliteAdapter,\n DrizzlePgliteAdapter,\n TestContext,\n} from \"./adapters\";\n\n/**\n * Options for creating a database fragment for testing\n */\nexport interface CreateDatabaseFragmentForTestOptions<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext extends Record<string, unknown>,\n TOptions extends FragnoPublicConfig,\n TAdapter extends SupportedAdapter,\n> extends Omit<\n CreateFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions>,\n \"config\"\n > {\n adapter: TAdapter;\n migrateToVersion?: number;\n config?: TConfig;\n}\n\n/**\n * Result of creating a database fragment for testing\n * All properties are getters that return the current fragment instance\n */\nexport interface DatabaseFragmentTestResult<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext extends Record<string, unknown>,\n TOptions extends FragnoPublicConfig,\n TAdapter extends SupportedAdapter,\n TRoutes extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n> {\n readonly fragment: FragmentForTest<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext,\n TOptions,\n TRoutes\n >;\n readonly services: TServices;\n readonly callRoute: FragmentForTest<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext,\n TOptions,\n TRoutes\n >[\"callRoute\"];\n readonly config: TConfig;\n readonly deps: TDeps;\n readonly additionalContext: TAdditionalContext;\n test: TestContext<TAdapter>;\n}\n\nexport async function createDatabaseFragmentForTest<\n const TConfig,\n const TDeps,\n const TServices extends Record<string, unknown>,\n const TAdditionalContext extends Record<string, unknown>,\n const TOptions extends FragnoPublicConfig,\n const TSchema extends AnySchema,\n const TAdapter extends SupportedAdapter,\n const TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n>(\n fragmentBuilder: {\n definition: FragmentDefinition<TConfig, TDeps, TServices, TAdditionalContext>;\n $requiredOptions: TOptions;\n },\n routesOrFactories: TRoutesOrFactories,\n options: CreateDatabaseFragmentForTestOptions<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext,\n TOptions,\n TAdapter\n >,\n): Promise<\n DatabaseFragmentTestResult<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext,\n TOptions,\n TAdapter,\n FlattenRouteFactories<TRoutesOrFactories>\n >\n> {\n const {\n adapter: adapterConfig,\n migrateToVersion,\n config,\n options: fragmentOptions,\n deps,\n services,\n additionalContext,\n } = options;\n\n // Get schema and namespace from fragment definition's additionalContext\n // Safe cast: DatabaseFragmentBuilder adds databaseSchema and databaseNamespace to additionalContext\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fragmentAdditionalContext = fragmentBuilder.definition.additionalContext as any;\n const schema = fragmentAdditionalContext?.databaseSchema as TSchema | undefined;\n const namespace = (fragmentAdditionalContext?.databaseNamespace as string | undefined) ?? \"\";\n\n if (!schema) {\n throw new Error(\n `Fragment '${fragmentBuilder.definition.name}' does not have a database schema. ` +\n `Make sure you're using defineFragmentWithDatabase().withDatabase(schema).`,\n );\n }\n\n // Create adapter using the factory\n const { testContext: originalTestContext, adapter } = await createAdapter(\n adapterConfig,\n schema,\n namespace,\n migrateToVersion,\n );\n\n // Create fragment with database adapter in options\n // Safe cast: We're merging the user's options with the databaseAdapter, which is required by TOptions\n // The user's TOptions is constrained to FragnoPublicConfig (or a subtype), which we extend with databaseAdapter\n let mergedOptions = {\n ...fragmentOptions,\n databaseAdapter: adapter,\n } as unknown as TOptions;\n\n let fragment = createFragmentForTest(fragmentBuilder, routesOrFactories, {\n // Safe cast: If config is not provided, we pass undefined as TConfig.\n // The base createFragmentForTest expects config: TConfig, but if TConfig allows undefined\n // or if the fragment doesn't use config in its dependencies function, this will work correctly.\n config: config as TConfig,\n options: mergedOptions,\n deps,\n services,\n additionalContext,\n });\n\n // Wrap resetDatabase to also recreate the fragment with the new adapter\n const originalResetDatabase = originalTestContext.resetDatabase;\n\n // Create test context with getters that always return current values\n // We need to cast to any to avoid TypeScript errors when accessing kysely/drizzle properties\n // that may not exist depending on the adapter type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const testContext: any = Object.create(originalTestContext, {\n db: {\n get() {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (originalTestContext as any).db;\n },\n enumerable: true,\n },\n kysely: {\n get() {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (originalTestContext as any).kysely;\n },\n enumerable: true,\n },\n drizzle: {\n get() {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (originalTestContext as any).drizzle;\n },\n enumerable: true,\n },\n adapter: {\n get() {\n return originalTestContext.adapter;\n },\n enumerable: true,\n },\n resetDatabase: {\n value: async () => {\n // Call the original reset database function\n await originalResetDatabase();\n\n // Recreate the fragment with the new adapter (which has been updated by the factory's resetDatabase)\n mergedOptions = {\n ...fragmentOptions,\n databaseAdapter: originalTestContext.adapter,\n } as unknown as TOptions;\n\n fragment = createFragmentForTest(fragmentBuilder, routesOrFactories, {\n config: config as TConfig,\n options: mergedOptions,\n deps,\n services,\n additionalContext,\n });\n },\n enumerable: true,\n },\n cleanup: {\n value: async () => {\n await originalTestContext.cleanup();\n },\n enumerable: true,\n },\n });\n\n // Return an object with getters for fragment properties so they always reference the current fragment\n return {\n get fragment() {\n return fragment;\n },\n get services() {\n return fragment.services;\n },\n get callRoute() {\n return fragment.callRoute;\n },\n get config() {\n return fragment.config;\n },\n get deps() {\n return fragment.deps;\n },\n get additionalContext() {\n return fragment.additionalContext;\n },\n test: testContext,\n };\n}\n"],"mappings":";;;;AAoGA,eAAsB,8BAUpB,iBAIA,mBACA,SAkBA;CACA,MAAM,EACJ,SAAS,eACT,kBACA,QACA,SAAS,iBACT,MACA,UACA,sBACE;CAKJ,MAAM,4BAA4B,gBAAgB,WAAW;CAC7D,MAAM,SAAS,2BAA2B;CAC1C,MAAM,YAAa,2BAA2B,qBAA4C;AAE1F,KAAI,CAAC,OACH,OAAM,IAAI,MACR,aAAa,gBAAgB,WAAW,KAAK,8GAE9C;CAIH,MAAM,EAAE,aAAa,qBAAqB,YAAY,MAAM,cAC1D,eACA,QACA,WACA,iBACD;CAKD,IAAI,gBAAgB;EAClB,GAAG;EACH,iBAAiB;EAClB;CAED,IAAI,WAAWA,wBAAsB,iBAAiB,mBAAmB;EAI/D;EACR,SAAS;EACT;EACA;EACA;EACD,CAAC;CAGF,MAAM,wBAAwB,oBAAoB;AAgElD,QAAO;EACL,IAAI,WAAW;AACb,UAAO;;EAET,IAAI,WAAW;AACb,UAAO,SAAS;;EAElB,IAAI,YAAY;AACd,UAAO,SAAS;;EAElB,IAAI,SAAS;AACX,UAAO,SAAS;;EAElB,IAAI,OAAO;AACT,UAAO,SAAS;;EAElB,IAAI,oBAAoB;AACtB,UAAO,SAAS;;EAElB,MA7EuB,OAAO,OAAO,qBAAqB;GAC1D,IAAI;IACF,MAAM;AAEJ,YAAQ,oBAA4B;;IAEtC,YAAY;IACb;GACD,QAAQ;IACN,MAAM;AAEJ,YAAQ,oBAA4B;;IAEtC,YAAY;IACb;GACD,SAAS;IACP,MAAM;AAEJ,YAAQ,oBAA4B;;IAEtC,YAAY;IACb;GACD,SAAS;IACP,MAAM;AACJ,YAAO,oBAAoB;;IAE7B,YAAY;IACb;GACD,eAAe;IACb,OAAO,YAAY;AAEjB,WAAM,uBAAuB;AAG7B,qBAAgB;MACd,GAAG;MACH,iBAAiB,oBAAoB;MACtC;AAED,gBAAWA,wBAAsB,iBAAiB,mBAAmB;MAC3D;MACR,SAAS;MACT;MACA;MACA;MACD,CAAC;;IAEJ,YAAY;IACb;GACD,SAAS;IACP,OAAO,YAAY;AACjB,WAAM,oBAAoB,SAAS;;IAErC,YAAY;IACb;GACF,CAAC;EAuBD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragno-dev/test",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
"module": "./dist/index.js",
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"dependencies": {
|
|
16
|
+
"@standard-schema/spec": "^1.0.0",
|
|
16
17
|
"kysely": "^0.28.7",
|
|
17
18
|
"sqlocal": "^0.15.2",
|
|
18
|
-
"@fragno-dev/
|
|
19
|
-
"@fragno-dev/
|
|
19
|
+
"@fragno-dev/core": "0.1.6",
|
|
20
|
+
"@fragno-dev/db": "0.1.13"
|
|
20
21
|
},
|
|
21
22
|
"peerDependencies": {
|
|
22
23
|
"@electric-sql/pglite": "^0.3.11",
|
|
@@ -48,8 +49,8 @@
|
|
|
48
49
|
"kysely-pglite": "^0.6.1",
|
|
49
50
|
"vitest": "^3.2.4",
|
|
50
51
|
"zod": "^4.1.12",
|
|
51
|
-
"@fragno-private/
|
|
52
|
-
"@fragno-private/
|
|
52
|
+
"@fragno-private/typescript-config": "0.0.1",
|
|
53
|
+
"@fragno-private/vitest-config": "0.0.0"
|
|
53
54
|
},
|
|
54
55
|
"repository": {
|
|
55
56
|
"type": "git",
|
package/src/adapters.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { KyselyAdapter } from "@fragno-dev/db/adapters/kysely";
|
|
|
7
7
|
import { DrizzleAdapter } from "@fragno-dev/db/adapters/drizzle";
|
|
8
8
|
import type { AnySchema } from "@fragno-dev/db/schema";
|
|
9
9
|
import type { DatabaseAdapter } from "@fragno-dev/db/adapters";
|
|
10
|
+
import type { AbstractQuery } from "@fragno-dev/db/query";
|
|
10
11
|
import { createRequire } from "node:module";
|
|
11
12
|
import { mkdir, writeFile, rm } from "node:fs/promises";
|
|
12
13
|
import { join } from "node:path";
|
|
@@ -34,6 +35,7 @@ export type TestContext<T extends SupportedAdapter> = T extends
|
|
|
34
35
|
| KyselySqliteAdapter
|
|
35
36
|
| KyselyPgliteAdapter
|
|
36
37
|
? {
|
|
38
|
+
readonly db: AbstractQuery<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
37
39
|
readonly kysely: Kysely<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
38
40
|
readonly adapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
39
41
|
resetDatabase: () => Promise<void>;
|
|
@@ -41,6 +43,7 @@ export type TestContext<T extends SupportedAdapter> = T extends
|
|
|
41
43
|
}
|
|
42
44
|
: T extends DrizzlePgliteAdapter
|
|
43
45
|
? {
|
|
46
|
+
readonly db: AbstractQuery<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
44
47
|
readonly drizzle: ReturnType<typeof drizzle<any>>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
45
48
|
readonly adapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
46
49
|
resetDatabase: () => Promise<void>;
|
|
@@ -90,11 +93,15 @@ export async function createKyselySqliteAdapter(
|
|
|
90
93
|
});
|
|
91
94
|
await preparedMigration.execute();
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
// Create ORM instance
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
+
const orm = adapter.createQueryEngine(schema, namespace) as AbstractQuery<any>;
|
|
99
|
+
|
|
100
|
+
return { kysely, adapter, orm };
|
|
94
101
|
};
|
|
95
102
|
|
|
96
103
|
// Create initial database
|
|
97
|
-
let { kysely, adapter } = await createDatabase();
|
|
104
|
+
let { kysely, adapter, orm } = await createDatabase();
|
|
98
105
|
|
|
99
106
|
// Reset database function - creates a fresh in-memory database and re-runs migrations
|
|
100
107
|
const resetDatabase = async () => {
|
|
@@ -105,6 +112,7 @@ export async function createKyselySqliteAdapter(
|
|
|
105
112
|
const newDb = await createDatabase();
|
|
106
113
|
kysely = newDb.kysely;
|
|
107
114
|
adapter = newDb.adapter;
|
|
115
|
+
orm = newDb.orm;
|
|
108
116
|
};
|
|
109
117
|
|
|
110
118
|
// Cleanup function - closes connections (no files to delete for in-memory)
|
|
@@ -114,6 +122,9 @@ export async function createKyselySqliteAdapter(
|
|
|
114
122
|
|
|
115
123
|
return {
|
|
116
124
|
testContext: {
|
|
125
|
+
get db() {
|
|
126
|
+
return orm;
|
|
127
|
+
},
|
|
117
128
|
get kysely() {
|
|
118
129
|
return kysely;
|
|
119
130
|
},
|
|
@@ -168,11 +179,15 @@ export async function createKyselyPgliteAdapter(
|
|
|
168
179
|
});
|
|
169
180
|
await preparedMigration.execute();
|
|
170
181
|
|
|
171
|
-
|
|
182
|
+
// Create ORM instance
|
|
183
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
184
|
+
const orm = adapter.createQueryEngine(schema, namespace) as AbstractQuery<any>;
|
|
185
|
+
|
|
186
|
+
return { kysely, adapter, kyselyPglite, orm };
|
|
172
187
|
};
|
|
173
188
|
|
|
174
189
|
// Create initial database
|
|
175
|
-
let { kysely, adapter, kyselyPglite } = await createDatabase();
|
|
190
|
+
let { kysely, adapter, kyselyPglite, orm } = await createDatabase();
|
|
176
191
|
|
|
177
192
|
// Reset database function - creates a fresh database and re-runs migrations
|
|
178
193
|
const resetDatabase = async () => {
|
|
@@ -190,6 +205,7 @@ export async function createKyselyPgliteAdapter(
|
|
|
190
205
|
kysely = newDb.kysely;
|
|
191
206
|
adapter = newDb.adapter;
|
|
192
207
|
kyselyPglite = newDb.kyselyPglite;
|
|
208
|
+
orm = newDb.orm;
|
|
193
209
|
};
|
|
194
210
|
|
|
195
211
|
// Cleanup function - closes connections and deletes database directory
|
|
@@ -210,6 +226,9 @@ export async function createKyselyPgliteAdapter(
|
|
|
210
226
|
|
|
211
227
|
return {
|
|
212
228
|
testContext: {
|
|
229
|
+
get db() {
|
|
230
|
+
return orm;
|
|
231
|
+
},
|
|
213
232
|
get kysely() {
|
|
214
233
|
return kysely;
|
|
215
234
|
},
|
|
@@ -301,11 +320,15 @@ export async function createDrizzlePgliteAdapter(
|
|
|
301
320
|
provider: "postgresql",
|
|
302
321
|
});
|
|
303
322
|
|
|
304
|
-
|
|
323
|
+
// Create ORM instance
|
|
324
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
325
|
+
const orm = adapter.createQueryEngine(schema, namespace) as AbstractQuery<any>;
|
|
326
|
+
|
|
327
|
+
return { drizzle: db, adapter, pglite, cleanup, orm };
|
|
305
328
|
};
|
|
306
329
|
|
|
307
330
|
// Create initial database
|
|
308
|
-
let { drizzle: drizzleDb, adapter, pglite, cleanup: schemaCleanup } = await createDatabase();
|
|
331
|
+
let { drizzle: drizzleDb, adapter, pglite, cleanup: schemaCleanup, orm } = await createDatabase();
|
|
309
332
|
|
|
310
333
|
// Reset database function - creates a fresh database and re-runs migrations
|
|
311
334
|
const resetDatabase = async () => {
|
|
@@ -319,6 +342,7 @@ export async function createDrizzlePgliteAdapter(
|
|
|
319
342
|
adapter = newDb.adapter;
|
|
320
343
|
pglite = newDb.pglite;
|
|
321
344
|
schemaCleanup = newDb.cleanup;
|
|
345
|
+
orm = newDb.orm;
|
|
322
346
|
};
|
|
323
347
|
|
|
324
348
|
// Cleanup function - closes connections and deletes generated files and database directory
|
|
@@ -334,6 +358,9 @@ export async function createDrizzlePgliteAdapter(
|
|
|
334
358
|
|
|
335
359
|
return {
|
|
336
360
|
testContext: {
|
|
361
|
+
get db() {
|
|
362
|
+
return orm;
|
|
363
|
+
},
|
|
337
364
|
get drizzle() {
|
|
338
365
|
return drizzleDb;
|
|
339
366
|
},
|
package/src/index.test.ts
CHANGED
|
@@ -55,7 +55,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
it("should use in-memory database by default", async () => {
|
|
58
|
-
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, {
|
|
58
|
+
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
59
59
|
adapter: { type: "kysely-sqlite" },
|
|
60
60
|
});
|
|
61
61
|
|
|
@@ -79,7 +79,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
it("should create database at specified path", async () => {
|
|
82
|
-
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, {
|
|
82
|
+
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
83
83
|
adapter: { type: "kysely-sqlite", databasePath: testDbPath },
|
|
84
84
|
});
|
|
85
85
|
|
|
@@ -103,7 +103,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
103
103
|
|
|
104
104
|
describe("migrateToVersion option", () => {
|
|
105
105
|
it("should migrate to latest version by default", async () => {
|
|
106
|
-
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, {
|
|
106
|
+
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
107
107
|
adapter: { type: "kysely-sqlite" },
|
|
108
108
|
});
|
|
109
109
|
|
|
@@ -124,7 +124,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
124
124
|
|
|
125
125
|
it("should migrate to specific version when specified", async () => {
|
|
126
126
|
// Migrate to version 1 (before 'age' column was added)
|
|
127
|
-
const { test } = await createDatabaseFragmentForTest(testFragmentDef, {
|
|
127
|
+
const { test } = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
128
128
|
adapter: { type: "kysely-sqlite" },
|
|
129
129
|
migrateToVersion: 1,
|
|
130
130
|
});
|
|
@@ -155,7 +155,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
155
155
|
|
|
156
156
|
it("should allow creating user with age when migrated to version 2", async () => {
|
|
157
157
|
// Explicitly migrate to version 2
|
|
158
|
-
const { fragment, test } = await createDatabaseFragmentForTest(testFragmentDef, {
|
|
158
|
+
const { fragment, test } = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
159
159
|
adapter: { type: "kysely-sqlite" },
|
|
160
160
|
migrateToVersion: 2,
|
|
161
161
|
});
|
|
@@ -200,7 +200,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
200
200
|
});
|
|
201
201
|
|
|
202
202
|
it("should work with both databasePath and migrateToVersion", async () => {
|
|
203
|
-
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, {
|
|
203
|
+
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
204
204
|
adapter: { type: "kysely-sqlite", databasePath: testDbPath },
|
|
205
205
|
migrateToVersion: 2,
|
|
206
206
|
});
|
|
@@ -231,7 +231,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
231
231
|
|
|
232
232
|
describe("fragment initialization", () => {
|
|
233
233
|
it("should provide kysely instance", async () => {
|
|
234
|
-
const { test } = await createDatabaseFragmentForTest(testFragmentDef, {
|
|
234
|
+
const { test } = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
235
235
|
adapter: { type: "kysely-sqlite" },
|
|
236
236
|
});
|
|
237
237
|
|
|
@@ -240,7 +240,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
240
240
|
});
|
|
241
241
|
|
|
242
242
|
it("should provide adapter instance", async () => {
|
|
243
|
-
const { test } = await createDatabaseFragmentForTest(testFragmentDef, {
|
|
243
|
+
const { test } = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
244
244
|
adapter: { type: "kysely-sqlite" },
|
|
245
245
|
});
|
|
246
246
|
|
|
@@ -249,13 +249,11 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
249
249
|
});
|
|
250
250
|
|
|
251
251
|
it("should have all standard fragment test properties", async () => {
|
|
252
|
-
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, {
|
|
252
|
+
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
253
253
|
adapter: { type: "kysely-sqlite" },
|
|
254
254
|
});
|
|
255
255
|
|
|
256
256
|
expect(fragment.services).toBeDefined();
|
|
257
|
-
expect(fragment.initRoutes).toBeDefined();
|
|
258
|
-
expect(fragment.handler).toBeDefined();
|
|
259
257
|
});
|
|
260
258
|
|
|
261
259
|
it("should throw error for non-database fragment", async () => {
|
|
@@ -270,7 +268,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
270
268
|
|
|
271
269
|
await expect(
|
|
272
270
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
273
|
-
createDatabaseFragmentForTest(nonDbFragment as any, {
|
|
271
|
+
createDatabaseFragmentForTest(nonDbFragment as any, [], {
|
|
274
272
|
adapter: { type: "kysely-sqlite" },
|
|
275
273
|
}),
|
|
276
274
|
).rejects.toThrow("Fragment 'non-db-fragment' does not have a database schema");
|
|
@@ -279,10 +277,6 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
279
277
|
|
|
280
278
|
describe("route handling with defineRoutes", () => {
|
|
281
279
|
it("should handle route factory with multiple routes", async () => {
|
|
282
|
-
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, {
|
|
283
|
-
adapter: { type: "kysely-sqlite" },
|
|
284
|
-
});
|
|
285
|
-
|
|
286
280
|
type Config = {};
|
|
287
281
|
type Deps = {};
|
|
288
282
|
type Services = {
|
|
@@ -338,10 +332,11 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
338
332
|
]);
|
|
339
333
|
|
|
340
334
|
const routes = [routeFactory] as const;
|
|
341
|
-
const
|
|
342
|
-
|
|
335
|
+
const { fragment } = await createDatabaseFragmentForTest(testFragmentDef, routes, {
|
|
336
|
+
adapter: { type: "kysely-sqlite" },
|
|
337
|
+
});
|
|
343
338
|
// Test creating a user
|
|
344
|
-
const createResponse = await fragment.
|
|
339
|
+
const createResponse = await fragment.callRoute("POST", "/users", {
|
|
345
340
|
body: { name: "John Doe", email: "john@example.com", age: 30 },
|
|
346
341
|
});
|
|
347
342
|
|
|
@@ -356,7 +351,7 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
356
351
|
}
|
|
357
352
|
|
|
358
353
|
// Test getting users
|
|
359
|
-
const getUsersResponse = await fragment.
|
|
354
|
+
const getUsersResponse = await fragment.callRoute("GET", "/users");
|
|
360
355
|
|
|
361
356
|
expect(getUsersResponse.type).toBe("json");
|
|
362
357
|
if (getUsersResponse.type === "json") {
|
|
@@ -372,53 +367,170 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
372
367
|
});
|
|
373
368
|
|
|
374
369
|
describe("resetDatabase", () => {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
370
|
+
const adapters = [
|
|
371
|
+
{ name: "Kysely SQLite", adapter: { type: "kysely-sqlite" as const } },
|
|
372
|
+
{ name: "Kysely PGLite", adapter: { type: "kysely-pglite" as const } },
|
|
373
|
+
{ name: "Drizzle PGLite", adapter: { type: "drizzle-pglite" as const } },
|
|
374
|
+
];
|
|
380
375
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
376
|
+
for (const { name, adapter } of adapters) {
|
|
377
|
+
describe(name, () => {
|
|
378
|
+
it("should clear all data and recreate a fresh database", async () => {
|
|
379
|
+
// Don't destructure so we can access the updated fragment through getters after reset
|
|
380
|
+
const result = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
381
|
+
adapter,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Create some users
|
|
385
|
+
await result.services.createUser({
|
|
386
|
+
name: "User 1",
|
|
387
|
+
email: "user1@example.com",
|
|
388
|
+
age: 25,
|
|
389
|
+
});
|
|
390
|
+
await result.services.createUser({
|
|
391
|
+
name: "User 2",
|
|
392
|
+
email: "user2@example.com",
|
|
393
|
+
age: 30,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Verify users exist
|
|
397
|
+
let users = await result.services.getUsers();
|
|
398
|
+
expect(users).toHaveLength(2);
|
|
399
|
+
|
|
400
|
+
// Reset the database
|
|
401
|
+
await result.test.resetDatabase();
|
|
402
|
+
|
|
403
|
+
// Verify database is empty (accessing through result to get updated fragment)
|
|
404
|
+
users = await result.services.getUsers();
|
|
405
|
+
expect(users).toHaveLength(0);
|
|
406
|
+
|
|
407
|
+
// Verify we can still create new users after reset
|
|
408
|
+
const newUser = await result.services.createUser({
|
|
409
|
+
name: "User After Reset",
|
|
410
|
+
email: "after@example.com",
|
|
411
|
+
age: 35,
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
expect(newUser).toMatchObject({
|
|
415
|
+
id: expect.any(String),
|
|
416
|
+
name: "User After Reset",
|
|
417
|
+
email: "after@example.com",
|
|
418
|
+
age: 35,
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
users = await result.services.getUsers();
|
|
422
|
+
expect(users).toHaveLength(1);
|
|
423
|
+
expect(users[0]).toMatchObject(newUser);
|
|
424
|
+
|
|
425
|
+
// Cleanup
|
|
426
|
+
await result.test.cleanup();
|
|
427
|
+
}, 10000);
|
|
391
428
|
});
|
|
429
|
+
}
|
|
430
|
+
});
|
|
392
431
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
// Verify database is empty (accessing through result to get updated fragment)
|
|
401
|
-
users = await result.services.getUsers();
|
|
402
|
-
expect(users).toHaveLength(0);
|
|
403
|
-
|
|
404
|
-
// Verify we can still create new users after reset
|
|
405
|
-
const newUser = await result.services.createUser({
|
|
406
|
-
name: "User After Reset",
|
|
407
|
-
email: "after@example.com",
|
|
408
|
-
age: 35,
|
|
409
|
-
});
|
|
432
|
+
describe("db property access", () => {
|
|
433
|
+
const adapters = [
|
|
434
|
+
{ name: "Kysely SQLite", adapter: { type: "kysely-sqlite" as const } },
|
|
435
|
+
{ name: "Kysely PGLite", adapter: { type: "kysely-pglite" as const } },
|
|
436
|
+
{ name: "Drizzle PGLite", adapter: { type: "drizzle-pglite" as const } },
|
|
437
|
+
];
|
|
410
438
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
439
|
+
for (const { name, adapter } of adapters) {
|
|
440
|
+
describe(name, () => {
|
|
441
|
+
it("should expose db property for direct ORM queries", async () => {
|
|
442
|
+
const { test } = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
443
|
+
adapter,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// Test creating a record directly using test.db
|
|
447
|
+
const userId = await test.db.create("users", {
|
|
448
|
+
name: "Direct DB User",
|
|
449
|
+
email: "direct@example.com",
|
|
450
|
+
age: 28,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
expect(userId).toBeDefined();
|
|
454
|
+
expect(typeof userId.valueOf()).toBe("string");
|
|
455
|
+
|
|
456
|
+
// Test finding records using test.db
|
|
457
|
+
const users = await test.db.find("users", (b) =>
|
|
458
|
+
b.whereIndex("idx_users_all", (eb) => eb("id", "=", userId)),
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
expect(users).toHaveLength(1);
|
|
462
|
+
expect(users[0]).toMatchObject({
|
|
463
|
+
id: userId,
|
|
464
|
+
name: "Direct DB User",
|
|
465
|
+
email: "direct@example.com",
|
|
466
|
+
age: 28,
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Test findFirst using test.db
|
|
470
|
+
const user = await test.db.findFirst("users", (b) =>
|
|
471
|
+
b.whereIndex("idx_users_all", (eb) => eb("id", "=", userId)),
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
expect(user).toMatchObject({
|
|
475
|
+
id: userId,
|
|
476
|
+
name: "Direct DB User",
|
|
477
|
+
email: "direct@example.com",
|
|
478
|
+
age: 28,
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// Cleanup
|
|
482
|
+
await test.cleanup();
|
|
483
|
+
}, 10000);
|
|
484
|
+
|
|
485
|
+
it("should maintain db property after resetDatabase", async () => {
|
|
486
|
+
const result = await createDatabaseFragmentForTest(testFragmentDef, [], {
|
|
487
|
+
adapter,
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
// Create initial data using test.db
|
|
491
|
+
await result.test.db.create("users", {
|
|
492
|
+
name: "Before Reset",
|
|
493
|
+
email: "before@example.com",
|
|
494
|
+
age: 25,
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// Verify db works before reset
|
|
498
|
+
expect(result.test.db).toBeDefined();
|
|
499
|
+
expect(typeof result.test.db.create).toBe("function");
|
|
500
|
+
|
|
501
|
+
// Verify data exists
|
|
502
|
+
let users = await result.test.db.find("users");
|
|
503
|
+
expect(users).toHaveLength(1);
|
|
504
|
+
|
|
505
|
+
// Reset database
|
|
506
|
+
await result.test.resetDatabase();
|
|
507
|
+
|
|
508
|
+
// Verify database was actually reset (no data)
|
|
509
|
+
users = await result.test.db.find("users");
|
|
510
|
+
expect(users).toHaveLength(0);
|
|
511
|
+
|
|
512
|
+
// Verify we can still use the ORM after reset
|
|
513
|
+
const newUserId = await result.test.db.create("users", {
|
|
514
|
+
name: "After Reset",
|
|
515
|
+
email: "after@example.com",
|
|
516
|
+
age: 30,
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
expect(newUserId).toBeDefined();
|
|
520
|
+
|
|
521
|
+
const newUsers = await result.test.db.find("users");
|
|
522
|
+
expect(newUsers).toHaveLength(1);
|
|
523
|
+
expect(newUsers[0]).toMatchObject({
|
|
524
|
+
name: "After Reset",
|
|
525
|
+
email: "after@example.com",
|
|
526
|
+
age: 30,
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// Cleanup
|
|
530
|
+
await result.test.cleanup();
|
|
531
|
+
}, 10000);
|
|
416
532
|
});
|
|
417
|
-
|
|
418
|
-
users = await result.services.getUsers();
|
|
419
|
-
expect(users).toHaveLength(1);
|
|
420
|
-
expect(users[0]).toMatchObject(newUser);
|
|
421
|
-
});
|
|
533
|
+
}
|
|
422
534
|
});
|
|
423
535
|
|
|
424
536
|
describe("multiple adapters with auth-like schema", () => {
|
|
@@ -475,57 +587,49 @@ describe("createDatabaseFragmentForTest", () => {
|
|
|
475
587
|
|
|
476
588
|
for (const { name, adapter } of adapters) {
|
|
477
589
|
describe(name, () => {
|
|
478
|
-
it(
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
{
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const notFound = await fragment.services.getUserByEmail("nonexistent@test.com");
|
|
523
|
-
expect(notFound).toBeNull();
|
|
524
|
-
|
|
525
|
-
await test.cleanup();
|
|
526
|
-
},
|
|
527
|
-
{ timeout: 10000 },
|
|
528
|
-
);
|
|
590
|
+
it("should create user and session", async () => {
|
|
591
|
+
const { fragment, test } = await createDatabaseFragmentForTest(authFragmentDef, [], {
|
|
592
|
+
adapter,
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// Create a user
|
|
596
|
+
const user = await fragment.services.createUser("test@test.com", "hashed-password");
|
|
597
|
+
expect(user).toMatchObject({
|
|
598
|
+
id: expect.any(String),
|
|
599
|
+
email: "test@test.com",
|
|
600
|
+
passwordHash: "hashed-password",
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// Create a session for the user
|
|
604
|
+
const session = await fragment.services.createSession(user.id);
|
|
605
|
+
expect(session).toMatchObject({
|
|
606
|
+
id: expect.any(String),
|
|
607
|
+
userId: user.id,
|
|
608
|
+
expiresAt: expect.any(Date),
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// Find user by email
|
|
612
|
+
const foundUser = await fragment.services.getUserByEmail("test@test.com");
|
|
613
|
+
expect(foundUser).toMatchObject({
|
|
614
|
+
id: user.id,
|
|
615
|
+
email: "test@test.com",
|
|
616
|
+
passwordHash: "hashed-password",
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// Cleanup
|
|
620
|
+
await test.cleanup();
|
|
621
|
+
}, 10000);
|
|
622
|
+
|
|
623
|
+
it("should return null when user not found", async () => {
|
|
624
|
+
const { fragment, test } = await createDatabaseFragmentForTest(authFragmentDef, [], {
|
|
625
|
+
adapter,
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
const notFound = await fragment.services.getUserByEmail("nonexistent@test.com");
|
|
629
|
+
expect(notFound).toBeNull();
|
|
630
|
+
|
|
631
|
+
await test.cleanup();
|
|
632
|
+
}, 10000);
|
|
529
633
|
});
|
|
530
634
|
}
|
|
531
635
|
});
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "@fragno-dev/core/test";
|
|
7
7
|
import type { FragnoPublicConfig } from "@fragno-dev/core/api/fragment-instantiation";
|
|
8
8
|
import type { FragmentDefinition } from "@fragno-dev/core/api/fragment-builder";
|
|
9
|
+
import type { AnyRouteOrFactory, FlattenRouteFactories } from "@fragno-dev/core/api/route";
|
|
9
10
|
import {
|
|
10
11
|
createAdapter,
|
|
11
12
|
type SupportedAdapter,
|
|
@@ -14,15 +15,16 @@ import {
|
|
|
14
15
|
type KyselyPgliteAdapter,
|
|
15
16
|
type DrizzlePgliteAdapter,
|
|
16
17
|
} from "./adapters";
|
|
18
|
+
import type { FragnoRouteConfig } from "@fragno-dev/core";
|
|
19
|
+
import type { HTTPMethod } from "@fragno-dev/core/api";
|
|
20
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
17
21
|
|
|
18
22
|
// Re-export utilities from @fragno-dev/core/test
|
|
19
23
|
export {
|
|
20
24
|
createFragmentForTest,
|
|
21
|
-
type TestResponse,
|
|
22
25
|
type CreateFragmentForTestOptions,
|
|
23
26
|
type RouteHandlerInputOptions,
|
|
24
27
|
type FragmentForTest,
|
|
25
|
-
type InitRoutesOverrides,
|
|
26
28
|
} from "@fragno-dev/core/test";
|
|
27
29
|
|
|
28
30
|
// Re-export adapter types
|
|
@@ -64,23 +66,32 @@ export interface DatabaseFragmentTestResult<
|
|
|
64
66
|
TAdditionalContext extends Record<string, unknown>,
|
|
65
67
|
TOptions extends FragnoPublicConfig,
|
|
66
68
|
TAdapter extends SupportedAdapter,
|
|
69
|
+
TRoutes extends readonly FragnoRouteConfig<
|
|
70
|
+
HTTPMethod,
|
|
71
|
+
string,
|
|
72
|
+
StandardSchemaV1 | undefined,
|
|
73
|
+
StandardSchemaV1 | undefined,
|
|
74
|
+
string,
|
|
75
|
+
string
|
|
76
|
+
>[],
|
|
67
77
|
> {
|
|
68
|
-
readonly fragment: FragmentForTest<
|
|
69
|
-
readonly services: TServices;
|
|
70
|
-
readonly initRoutes: FragmentForTest<
|
|
78
|
+
readonly fragment: FragmentForTest<
|
|
71
79
|
TConfig,
|
|
72
80
|
TDeps,
|
|
73
81
|
TServices,
|
|
74
82
|
TAdditionalContext,
|
|
75
|
-
TOptions
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
TOptions,
|
|
84
|
+
TRoutes
|
|
85
|
+
>;
|
|
86
|
+
readonly services: TServices;
|
|
87
|
+
readonly callRoute: FragmentForTest<
|
|
78
88
|
TConfig,
|
|
79
89
|
TDeps,
|
|
80
90
|
TServices,
|
|
81
91
|
TAdditionalContext,
|
|
82
|
-
TOptions
|
|
83
|
-
|
|
92
|
+
TOptions,
|
|
93
|
+
TRoutes
|
|
94
|
+
>["callRoute"];
|
|
84
95
|
readonly config: TConfig;
|
|
85
96
|
readonly deps: TDeps;
|
|
86
97
|
readonly additionalContext: TAdditionalContext;
|
|
@@ -95,11 +106,13 @@ export async function createDatabaseFragmentForTest<
|
|
|
95
106
|
const TOptions extends FragnoPublicConfig,
|
|
96
107
|
const TSchema extends AnySchema,
|
|
97
108
|
const TAdapter extends SupportedAdapter,
|
|
109
|
+
const TRoutesOrFactories extends readonly AnyRouteOrFactory[],
|
|
98
110
|
>(
|
|
99
111
|
fragmentBuilder: {
|
|
100
112
|
definition: FragmentDefinition<TConfig, TDeps, TServices, TAdditionalContext>;
|
|
101
113
|
$requiredOptions: TOptions;
|
|
102
114
|
},
|
|
115
|
+
routesOrFactories: TRoutesOrFactories,
|
|
103
116
|
options: CreateDatabaseFragmentForTestOptions<
|
|
104
117
|
TConfig,
|
|
105
118
|
TDeps,
|
|
@@ -109,7 +122,15 @@ export async function createDatabaseFragmentForTest<
|
|
|
109
122
|
TAdapter
|
|
110
123
|
>,
|
|
111
124
|
): Promise<
|
|
112
|
-
DatabaseFragmentTestResult<
|
|
125
|
+
DatabaseFragmentTestResult<
|
|
126
|
+
TConfig,
|
|
127
|
+
TDeps,
|
|
128
|
+
TServices,
|
|
129
|
+
TAdditionalContext,
|
|
130
|
+
TOptions,
|
|
131
|
+
TAdapter,
|
|
132
|
+
FlattenRouteFactories<TRoutesOrFactories>
|
|
133
|
+
>
|
|
113
134
|
> {
|
|
114
135
|
const {
|
|
115
136
|
adapter: adapterConfig,
|
|
@@ -151,10 +172,10 @@ export async function createDatabaseFragmentForTest<
|
|
|
151
172
|
databaseAdapter: adapter,
|
|
152
173
|
} as unknown as TOptions;
|
|
153
174
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
175
|
+
let fragment = createFragmentForTest(fragmentBuilder, routesOrFactories, {
|
|
176
|
+
// Safe cast: If config is not provided, we pass undefined as TConfig.
|
|
177
|
+
// The base createFragmentForTest expects config: TConfig, but if TConfig allows undefined
|
|
178
|
+
// or if the fragment doesn't use config in its dependencies function, this will work correctly.
|
|
158
179
|
config: config as TConfig,
|
|
159
180
|
options: mergedOptions,
|
|
160
181
|
deps,
|
|
@@ -170,6 +191,13 @@ export async function createDatabaseFragmentForTest<
|
|
|
170
191
|
// that may not exist depending on the adapter type
|
|
171
192
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
172
193
|
const testContext: any = Object.create(originalTestContext, {
|
|
194
|
+
db: {
|
|
195
|
+
get() {
|
|
196
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
197
|
+
return (originalTestContext as any).db;
|
|
198
|
+
},
|
|
199
|
+
enumerable: true,
|
|
200
|
+
},
|
|
173
201
|
kysely: {
|
|
174
202
|
get() {
|
|
175
203
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -201,7 +229,7 @@ export async function createDatabaseFragmentForTest<
|
|
|
201
229
|
databaseAdapter: originalTestContext.adapter,
|
|
202
230
|
} as unknown as TOptions;
|
|
203
231
|
|
|
204
|
-
fragment = createFragmentForTest(fragmentBuilder, {
|
|
232
|
+
fragment = createFragmentForTest(fragmentBuilder, routesOrFactories, {
|
|
205
233
|
config: config as TConfig,
|
|
206
234
|
options: mergedOptions,
|
|
207
235
|
deps,
|
|
@@ -227,11 +255,8 @@ export async function createDatabaseFragmentForTest<
|
|
|
227
255
|
get services() {
|
|
228
256
|
return fragment.services;
|
|
229
257
|
},
|
|
230
|
-
get
|
|
231
|
-
return fragment.
|
|
232
|
-
},
|
|
233
|
-
get handler() {
|
|
234
|
-
return fragment.handler;
|
|
258
|
+
get callRoute() {
|
|
259
|
+
return fragment.callRoute;
|
|
235
260
|
},
|
|
236
261
|
get config() {
|
|
237
262
|
return fragment.config;
|