@fragno-dev/test 2.0.0 → 2.0.2
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 +41 -31
- package/CHANGELOG.md +69 -0
- package/dist/adapters.d.ts +4 -7
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +18 -302
- package/dist/adapters.js.map +1 -1
- package/dist/db-test.d.ts +120 -18
- package/dist/db-test.d.ts.map +1 -1
- package/dist/db-test.js +203 -55
- package/dist/db-test.js.map +1 -1
- package/dist/durable-hooks.d.ts +6 -2
- package/dist/durable-hooks.d.ts.map +1 -1
- package/dist/durable-hooks.js +10 -5
- package/dist/durable-hooks.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/model-checker-actors.d.ts.map +1 -1
- package/dist/model-checker-actors.js +1 -1
- package/dist/model-checker-actors.js.map +1 -1
- package/dist/model-checker-adapter.d.ts +1 -1
- package/dist/model-checker-adapter.d.ts.map +1 -1
- package/dist/model-checker-adapter.js.map +1 -1
- package/dist/model-checker.d.ts.map +1 -1
- package/dist/model-checker.js.map +1 -1
- package/dist/test-adapters/drizzle-pglite.js +116 -0
- package/dist/test-adapters/drizzle-pglite.js.map +1 -0
- package/dist/test-adapters/in-memory.js +39 -0
- package/dist/test-adapters/in-memory.js.map +1 -0
- package/dist/test-adapters/kysely-pglite.js +105 -0
- package/dist/test-adapters/kysely-pglite.js.map +1 -0
- package/dist/test-adapters/kysely-sqlite.js +87 -0
- package/dist/test-adapters/kysely-sqlite.js.map +1 -0
- package/dist/test-adapters/model-checker.js +41 -0
- package/dist/test-adapters/model-checker.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +32 -33
- package/src/adapter-conformance.test.ts +3 -1
- package/src/adapters.ts +24 -455
- package/src/db-roundtrip-guard.test.ts +206 -0
- package/src/db-test.test.ts +131 -77
- package/src/db-test.ts +530 -96
- package/src/durable-hooks.test.ts +58 -0
- package/src/durable-hooks.ts +23 -8
- package/src/index.test.ts +188 -104
- package/src/index.ts +6 -2
- package/src/model-checker-actors.test.ts +5 -2
- package/src/model-checker-actors.ts +2 -1
- package/src/model-checker-adapter.ts +3 -2
- package/src/model-checker.test.ts +4 -1
- package/src/model-checker.ts +4 -3
- package/src/test-adapters/drizzle-pglite.ts +162 -0
- package/src/test-adapters/in-memory.ts +56 -0
- package/src/test-adapters/kysely-pglite.ts +151 -0
- package/src/test-adapters/kysely-sqlite.ts +119 -0
- package/src/test-adapters/model-checker.ts +58 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +1 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { rm } from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
import { SqlAdapter } from "@fragno-dev/db/adapters/sql";
|
|
5
|
+
import { PGLiteDriverConfig } from "@fragno-dev/db/drivers";
|
|
6
|
+
import type { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
7
|
+
import type { AnySchema } from "@fragno-dev/db/schema";
|
|
8
|
+
import { drizzle } from "drizzle-orm/pglite";
|
|
9
|
+
import { KyselyPGlite } from "kysely-pglite";
|
|
10
|
+
|
|
11
|
+
import { internalFragmentDef } from "@fragno-dev/db";
|
|
12
|
+
|
|
13
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
14
|
+
|
|
15
|
+
import type { AdapterFactoryResult, DrizzlePgliteAdapter, SchemaConfig } from "../adapters";
|
|
16
|
+
|
|
17
|
+
const createCommonTestContextMethods = (
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
ormMap: Map<string | null, SimpleQueryInterface<any, any>>,
|
|
20
|
+
) => ({
|
|
21
|
+
getOrm: <TSchema extends AnySchema>(namespace: string | null): SimpleQueryInterface<TSchema> => {
|
|
22
|
+
const orm = ormMap.get(namespace);
|
|
23
|
+
if (!orm) {
|
|
24
|
+
throw new Error(`No ORM found for namespace: ${String(namespace)}`);
|
|
25
|
+
}
|
|
26
|
+
return orm as SimpleQueryInterface<TSchema>;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const runInternalFragmentMigrations = async (
|
|
31
|
+
adapter: SqlAdapter,
|
|
32
|
+
): Promise<SchemaConfig | undefined> => {
|
|
33
|
+
const dependencies = internalFragmentDef.dependencies;
|
|
34
|
+
if (!dependencies) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const databaseDeps = dependencies({
|
|
39
|
+
config: {},
|
|
40
|
+
options: { databaseAdapter: adapter, databaseNamespace: null },
|
|
41
|
+
});
|
|
42
|
+
if (databaseDeps?.schema) {
|
|
43
|
+
const migrations = adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace);
|
|
44
|
+
await migrations.executeWithDriver(adapter.driver, 0);
|
|
45
|
+
return { schema: databaseDeps.schema, namespace: databaseDeps.namespace };
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const resolveSchemaName = (adapter: SqlAdapter, namespace: string | null): string | null => {
|
|
51
|
+
if (adapter.namingStrategy.namespaceScope !== "schema") {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (!namespace || namespace.length === 0) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return adapter.namingStrategy.namespaceToSchema(namespace);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export async function createDrizzlePgliteAdapter(
|
|
61
|
+
config: DrizzlePgliteAdapter,
|
|
62
|
+
schemas: SchemaConfig[],
|
|
63
|
+
): Promise<AdapterFactoryResult<DrizzlePgliteAdapter>> {
|
|
64
|
+
const databasePath = config.databasePath;
|
|
65
|
+
let internalSchemaConfig: SchemaConfig | undefined;
|
|
66
|
+
|
|
67
|
+
const createDatabase = async () => {
|
|
68
|
+
const pglite = new PGlite(databasePath);
|
|
69
|
+
const { dialect } = new KyselyPGlite(pglite);
|
|
70
|
+
|
|
71
|
+
const adapter = new SqlAdapter({
|
|
72
|
+
dialect,
|
|
73
|
+
driverConfig: new PGLiteDriverConfig(),
|
|
74
|
+
uowConfig: config.uowConfig,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
internalSchemaConfig = await runInternalFragmentMigrations(adapter);
|
|
78
|
+
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
+
const ormMap = new Map<string | null, SimpleQueryInterface<any, any>>();
|
|
81
|
+
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
82
|
+
const preparedMigrations = adapter.prepareMigrations(schema, namespace);
|
|
83
|
+
if (migrateToVersion !== undefined) {
|
|
84
|
+
await preparedMigrations.execute(0, migrateToVersion, {
|
|
85
|
+
updateVersionInMigration: false,
|
|
86
|
+
});
|
|
87
|
+
} else {
|
|
88
|
+
await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const orm = adapter.createQueryEngine(schema, namespace);
|
|
92
|
+
ormMap.set(namespace, orm);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
96
|
+
const db = drizzle(pglite) as any;
|
|
97
|
+
|
|
98
|
+
return { drizzle: db, adapter, ormMap };
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const { drizzle: drizzleDb, adapter, ormMap } = await createDatabase();
|
|
102
|
+
|
|
103
|
+
const resetDatabase = async () => {
|
|
104
|
+
if (databasePath && databasePath !== ":memory:") {
|
|
105
|
+
throw new Error("resetDatabase is only supported for in-memory databases");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const schemasToTruncate = internalSchemaConfig ? [internalSchemaConfig, ...schemas] : schemas;
|
|
109
|
+
const useTruncate = adapter.adapterMetadata?.databaseType === "postgresql";
|
|
110
|
+
|
|
111
|
+
for (const { schema, namespace } of schemasToTruncate) {
|
|
112
|
+
const tableNames = Object.keys(schema.tables);
|
|
113
|
+
if (useTruncate) {
|
|
114
|
+
const qualifiedTables = tableNames.map((tableName) => {
|
|
115
|
+
const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);
|
|
116
|
+
const schemaName = resolveSchemaName(adapter, namespace);
|
|
117
|
+
return schemaName ? `"${schemaName}"."${physicalTableName}"` : `"${physicalTableName}"`;
|
|
118
|
+
});
|
|
119
|
+
if (qualifiedTables.length > 0) {
|
|
120
|
+
await drizzleDb.execute(`TRUNCATE ${qualifiedTables.join(", ")} CASCADE`);
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const tableName of tableNames.slice().reverse()) {
|
|
126
|
+
const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);
|
|
127
|
+
const schemaName = resolveSchemaName(adapter, namespace);
|
|
128
|
+
const qualifiedTable = schemaName
|
|
129
|
+
? `"${schemaName}"."${physicalTableName}"`
|
|
130
|
+
: `"${physicalTableName}"`;
|
|
131
|
+
await drizzleDb.execute(`DELETE FROM ${qualifiedTable}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const cleanup = async () => {
|
|
137
|
+
await adapter.close();
|
|
138
|
+
|
|
139
|
+
if (databasePath && databasePath !== ":memory:" && existsSync(databasePath)) {
|
|
140
|
+
await rm(databasePath, { recursive: true, force: true });
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const commonMethods = createCommonTestContextMethods(ormMap);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
testContext: {
|
|
148
|
+
get drizzle() {
|
|
149
|
+
return drizzleDb;
|
|
150
|
+
},
|
|
151
|
+
get adapter() {
|
|
152
|
+
return adapter;
|
|
153
|
+
},
|
|
154
|
+
...commonMethods,
|
|
155
|
+
resetDatabase,
|
|
156
|
+
cleanup,
|
|
157
|
+
},
|
|
158
|
+
get adapter() {
|
|
159
|
+
return adapter;
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { InMemoryAdapter } from "@fragno-dev/db/adapters/in-memory";
|
|
2
|
+
import type { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
3
|
+
import type { AnySchema } from "@fragno-dev/db/schema";
|
|
4
|
+
|
|
5
|
+
import type { AdapterFactoryResult, InMemoryAdapterConfig, SchemaConfig } from "../adapters";
|
|
6
|
+
|
|
7
|
+
const createCommonTestContextMethods = (
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
ormMap: Map<string | null, SimpleQueryInterface<any, any>>,
|
|
10
|
+
) => ({
|
|
11
|
+
getOrm: <TSchema extends AnySchema>(namespace: string | null): SimpleQueryInterface<TSchema> => {
|
|
12
|
+
const orm = ormMap.get(namespace);
|
|
13
|
+
if (!orm) {
|
|
14
|
+
throw new Error(`No ORM found for namespace: ${String(namespace)}`);
|
|
15
|
+
}
|
|
16
|
+
return orm as SimpleQueryInterface<TSchema>;
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export async function createInMemoryAdapter(
|
|
21
|
+
config: InMemoryAdapterConfig,
|
|
22
|
+
schemas: SchemaConfig[],
|
|
23
|
+
): Promise<AdapterFactoryResult<InMemoryAdapterConfig>> {
|
|
24
|
+
const adapter = new InMemoryAdapter(config.options);
|
|
25
|
+
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
const ormMap = new Map<string | null, SimpleQueryInterface<any, any>>();
|
|
28
|
+
for (const { schema, namespace } of schemas) {
|
|
29
|
+
const orm = adapter.createQueryEngine(schema, namespace);
|
|
30
|
+
ormMap.set(namespace, orm);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const resetDatabase = async () => {
|
|
34
|
+
await adapter.reset();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const cleanup = async () => {
|
|
38
|
+
await adapter.close();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const commonMethods = createCommonTestContextMethods(ormMap);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
testContext: {
|
|
45
|
+
get adapter() {
|
|
46
|
+
return adapter;
|
|
47
|
+
},
|
|
48
|
+
...commonMethods,
|
|
49
|
+
resetDatabase,
|
|
50
|
+
cleanup,
|
|
51
|
+
},
|
|
52
|
+
get adapter() {
|
|
53
|
+
return adapter;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { rm } from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
import { SqlAdapter } from "@fragno-dev/db/adapters/sql";
|
|
5
|
+
import { PGLiteDriverConfig } from "@fragno-dev/db/drivers";
|
|
6
|
+
import type { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
7
|
+
import type { AnySchema } from "@fragno-dev/db/schema";
|
|
8
|
+
import { Kysely } from "kysely";
|
|
9
|
+
import { KyselyPGlite } from "kysely-pglite";
|
|
10
|
+
|
|
11
|
+
import { internalFragmentDef } from "@fragno-dev/db";
|
|
12
|
+
|
|
13
|
+
import type { AdapterFactoryResult, KyselyPgliteAdapter, SchemaConfig } from "../adapters";
|
|
14
|
+
|
|
15
|
+
const createCommonTestContextMethods = (
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
ormMap: Map<string | null, SimpleQueryInterface<any, any>>,
|
|
18
|
+
) => ({
|
|
19
|
+
getOrm: <TSchema extends AnySchema>(namespace: string | null): SimpleQueryInterface<TSchema> => {
|
|
20
|
+
const orm = ormMap.get(namespace);
|
|
21
|
+
if (!orm) {
|
|
22
|
+
throw new Error(`No ORM found for namespace: ${String(namespace)}`);
|
|
23
|
+
}
|
|
24
|
+
return orm as SimpleQueryInterface<TSchema>;
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const runInternalFragmentMigrations = async (
|
|
29
|
+
adapter: SqlAdapter,
|
|
30
|
+
): Promise<SchemaConfig | undefined> => {
|
|
31
|
+
const dependencies = internalFragmentDef.dependencies;
|
|
32
|
+
if (!dependencies) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const databaseDeps = dependencies({
|
|
37
|
+
config: {},
|
|
38
|
+
options: { databaseAdapter: adapter, databaseNamespace: null },
|
|
39
|
+
});
|
|
40
|
+
if (databaseDeps?.schema) {
|
|
41
|
+
const migrations = adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace);
|
|
42
|
+
await migrations.executeWithDriver(adapter.driver, 0);
|
|
43
|
+
return { schema: databaseDeps.schema, namespace: databaseDeps.namespace };
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const resolveSchemaName = (adapter: SqlAdapter, namespace: string | null): string | null => {
|
|
49
|
+
if (adapter.namingStrategy.namespaceScope !== "schema") {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
if (!namespace || namespace.length === 0) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return adapter.namingStrategy.namespaceToSchema(namespace);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export async function createKyselyPgliteAdapter(
|
|
59
|
+
config: KyselyPgliteAdapter,
|
|
60
|
+
schemas: SchemaConfig[],
|
|
61
|
+
): Promise<AdapterFactoryResult<KyselyPgliteAdapter>> {
|
|
62
|
+
const databasePath = config.databasePath;
|
|
63
|
+
let internalSchemaConfig: SchemaConfig | undefined;
|
|
64
|
+
|
|
65
|
+
const createDatabase = async () => {
|
|
66
|
+
const kyselyPglite = await KyselyPGlite.create(databasePath);
|
|
67
|
+
|
|
68
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
69
|
+
const kysely = new Kysely<any>({
|
|
70
|
+
dialect: kyselyPglite.dialect,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const adapter = new SqlAdapter({
|
|
74
|
+
dialect: kyselyPglite.dialect,
|
|
75
|
+
driverConfig: new PGLiteDriverConfig(),
|
|
76
|
+
uowConfig: config.uowConfig,
|
|
77
|
+
});
|
|
78
|
+
internalSchemaConfig = await runInternalFragmentMigrations(adapter);
|
|
79
|
+
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
81
|
+
const ormMap = new Map<string | null, SimpleQueryInterface<any, any>>();
|
|
82
|
+
|
|
83
|
+
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
84
|
+
const preparedMigrations = adapter.prepareMigrations(schema, namespace);
|
|
85
|
+
if (migrateToVersion !== undefined) {
|
|
86
|
+
await preparedMigrations.execute(0, migrateToVersion, {
|
|
87
|
+
updateVersionInMigration: false,
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const orm = adapter.createQueryEngine(schema, namespace);
|
|
94
|
+
ormMap.set(namespace, orm);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { kysely, adapter, kyselyPglite, ormMap };
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const { kysely, adapter, kyselyPglite, ormMap } = await createDatabase();
|
|
101
|
+
|
|
102
|
+
const resetDatabase = async () => {
|
|
103
|
+
if (databasePath && databasePath !== ":memory:") {
|
|
104
|
+
throw new Error("resetDatabase is only supported for in-memory databases");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const schemasToTruncate = internalSchemaConfig ? [internalSchemaConfig, ...schemas] : schemas;
|
|
108
|
+
|
|
109
|
+
for (const { schema, namespace } of schemasToTruncate) {
|
|
110
|
+
for (const tableName of Object.keys(schema.tables)) {
|
|
111
|
+
const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);
|
|
112
|
+
const schemaName = resolveSchemaName(adapter, namespace);
|
|
113
|
+
const scopedKysely = schemaName ? kysely.withSchema(schemaName) : kysely;
|
|
114
|
+
await scopedKysely.deleteFrom(physicalTableName).execute();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const cleanup = async () => {
|
|
120
|
+
await kysely.destroy();
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await kyselyPglite.client.close();
|
|
124
|
+
} catch {
|
|
125
|
+
// Ignore if already closed
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (databasePath && databasePath !== ":memory:" && existsSync(databasePath)) {
|
|
129
|
+
await rm(databasePath, { recursive: true, force: true });
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const commonMethods = createCommonTestContextMethods(ormMap);
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
testContext: {
|
|
137
|
+
get kysely() {
|
|
138
|
+
return kysely;
|
|
139
|
+
},
|
|
140
|
+
get adapter() {
|
|
141
|
+
return adapter;
|
|
142
|
+
},
|
|
143
|
+
...commonMethods,
|
|
144
|
+
resetDatabase,
|
|
145
|
+
cleanup,
|
|
146
|
+
},
|
|
147
|
+
get adapter() {
|
|
148
|
+
return adapter;
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { SqlAdapter } from "@fragno-dev/db/adapters/sql";
|
|
2
|
+
import { SQLocalDriverConfig } from "@fragno-dev/db/drivers";
|
|
3
|
+
import type { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
4
|
+
import type { AnySchema } from "@fragno-dev/db/schema";
|
|
5
|
+
import { Kysely } from "kysely";
|
|
6
|
+
import { SQLocalKysely } from "sqlocal/kysely";
|
|
7
|
+
|
|
8
|
+
import { internalFragmentDef } from "@fragno-dev/db";
|
|
9
|
+
|
|
10
|
+
import type { KyselySqliteAdapter, AdapterFactoryResult, SchemaConfig } from "../adapters";
|
|
11
|
+
|
|
12
|
+
const createCommonTestContextMethods = (
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
ormMap: Map<string | null, SimpleQueryInterface<any, any>>,
|
|
15
|
+
) => ({
|
|
16
|
+
getOrm: <TSchema extends AnySchema>(namespace: string | null): SimpleQueryInterface<TSchema> => {
|
|
17
|
+
const orm = ormMap.get(namespace);
|
|
18
|
+
if (!orm) {
|
|
19
|
+
throw new Error(`No ORM found for namespace: ${String(namespace)}`);
|
|
20
|
+
}
|
|
21
|
+
return orm as SimpleQueryInterface<TSchema>;
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const runInternalFragmentMigrations = async (
|
|
26
|
+
adapter: SqlAdapter,
|
|
27
|
+
): Promise<SchemaConfig | undefined> => {
|
|
28
|
+
const dependencies = internalFragmentDef.dependencies;
|
|
29
|
+
if (!dependencies) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const databaseDeps = dependencies({
|
|
34
|
+
config: {},
|
|
35
|
+
options: { databaseAdapter: adapter, databaseNamespace: null },
|
|
36
|
+
});
|
|
37
|
+
if (databaseDeps?.schema) {
|
|
38
|
+
const migrations = adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace);
|
|
39
|
+
await migrations.executeWithDriver(adapter.driver, 0);
|
|
40
|
+
return { schema: databaseDeps.schema, namespace: databaseDeps.namespace };
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export async function createKyselySqliteAdapter(
|
|
46
|
+
config: KyselySqliteAdapter,
|
|
47
|
+
schemas: SchemaConfig[],
|
|
48
|
+
): Promise<AdapterFactoryResult<KyselySqliteAdapter>> {
|
|
49
|
+
let internalSchemaConfig: SchemaConfig | undefined;
|
|
50
|
+
|
|
51
|
+
const createDatabase = async () => {
|
|
52
|
+
const { dialect } = new SQLocalKysely(":memory:");
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
const kysely = new Kysely<any>({
|
|
55
|
+
dialect,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const adapter = new SqlAdapter({
|
|
59
|
+
dialect,
|
|
60
|
+
driverConfig: new SQLocalDriverConfig(),
|
|
61
|
+
uowConfig: config.uowConfig,
|
|
62
|
+
});
|
|
63
|
+
internalSchemaConfig = await runInternalFragmentMigrations(adapter);
|
|
64
|
+
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
const ormMap = new Map<string | null, SimpleQueryInterface<any, any>>();
|
|
67
|
+
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
68
|
+
const preparedMigrations = adapter.prepareMigrations(schema, namespace);
|
|
69
|
+
if (migrateToVersion !== undefined) {
|
|
70
|
+
await preparedMigrations.execute(0, migrateToVersion, {
|
|
71
|
+
updateVersionInMigration: false,
|
|
72
|
+
});
|
|
73
|
+
} else {
|
|
74
|
+
await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const orm = adapter.createQueryEngine(schema, namespace);
|
|
78
|
+
ormMap.set(namespace, orm);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { kysely, adapter, ormMap };
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
let { kysely, adapter, ormMap } = await createDatabase();
|
|
85
|
+
|
|
86
|
+
const resetDatabase = async () => {
|
|
87
|
+
const schemasToTruncate = internalSchemaConfig ? [internalSchemaConfig, ...schemas] : schemas;
|
|
88
|
+
|
|
89
|
+
for (const { schema, namespace } of schemasToTruncate) {
|
|
90
|
+
for (const tableName of Object.keys(schema.tables)) {
|
|
91
|
+
const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);
|
|
92
|
+
await kysely.deleteFrom(physicalTableName).execute();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const cleanup = async () => {
|
|
98
|
+
await kysely.destroy();
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const commonMethods = createCommonTestContextMethods(ormMap);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
testContext: {
|
|
105
|
+
get kysely() {
|
|
106
|
+
return kysely;
|
|
107
|
+
},
|
|
108
|
+
get adapter() {
|
|
109
|
+
return adapter;
|
|
110
|
+
},
|
|
111
|
+
...commonMethods,
|
|
112
|
+
resetDatabase,
|
|
113
|
+
cleanup,
|
|
114
|
+
},
|
|
115
|
+
get adapter() {
|
|
116
|
+
return adapter;
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { InMemoryAdapter } from "@fragno-dev/db/adapters/in-memory";
|
|
2
|
+
import type { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
3
|
+
import type { AnySchema } from "@fragno-dev/db/schema";
|
|
4
|
+
|
|
5
|
+
import type { AdapterFactoryResult, ModelCheckerAdapterConfig, SchemaConfig } from "../adapters";
|
|
6
|
+
import { ModelCheckerAdapter } from "../model-checker-adapter";
|
|
7
|
+
|
|
8
|
+
const createCommonTestContextMethods = (
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
ormMap: Map<string | null, SimpleQueryInterface<any, any>>,
|
|
11
|
+
) => ({
|
|
12
|
+
getOrm: <TSchema extends AnySchema>(namespace: string | null): SimpleQueryInterface<TSchema> => {
|
|
13
|
+
const orm = ormMap.get(namespace);
|
|
14
|
+
if (!orm) {
|
|
15
|
+
throw new Error(`No ORM found for namespace: ${String(namespace)}`);
|
|
16
|
+
}
|
|
17
|
+
return orm as SimpleQueryInterface<TSchema>;
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export async function createModelCheckerAdapter(
|
|
22
|
+
config: ModelCheckerAdapterConfig,
|
|
23
|
+
schemas: SchemaConfig[],
|
|
24
|
+
): Promise<AdapterFactoryResult<ModelCheckerAdapterConfig>> {
|
|
25
|
+
const baseAdapter = new InMemoryAdapter(config.options);
|
|
26
|
+
const adapter = new ModelCheckerAdapter(baseAdapter);
|
|
27
|
+
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
const ormMap = new Map<string | null, SimpleQueryInterface<any, any>>();
|
|
30
|
+
for (const { schema, namespace } of schemas) {
|
|
31
|
+
const orm = adapter.createQueryEngine(schema, namespace);
|
|
32
|
+
ormMap.set(namespace, orm);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const resetDatabase = async () => {
|
|
36
|
+
await baseAdapter.reset();
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const cleanup = async () => {
|
|
40
|
+
await adapter.close();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const commonMethods = createCommonTestContextMethods(ormMap);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
testContext: {
|
|
47
|
+
get adapter() {
|
|
48
|
+
return adapter;
|
|
49
|
+
},
|
|
50
|
+
...commonMethods,
|
|
51
|
+
resetDatabase,
|
|
52
|
+
cleanup,
|
|
53
|
+
},
|
|
54
|
+
get adapter() {
|
|
55
|
+
return adapter;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
package/tsconfig.json
CHANGED