@zenstackhq/testtools 3.0.0-beta.9 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +121 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -8
- package/dist/index.d.ts +56 -8
- package/dist/index.js +124 -57
- package/dist/index.js.map +1 -1
- package/package.json +15 -14
package/dist/index.d.cts
CHANGED
|
@@ -1,41 +1,89 @@
|
|
|
1
|
-
import { ClientOptions, ClientContract } from '@zenstackhq/
|
|
2
|
-
import { SchemaDef } from '@zenstackhq/
|
|
1
|
+
import { ClientOptions, ClientContract } from '@zenstackhq/orm';
|
|
2
|
+
import { SchemaDef } from '@zenstackhq/orm/schema';
|
|
3
3
|
import { LogEvent } from 'kysely';
|
|
4
4
|
import * as _zenstackhq_language_ast from '@zenstackhq/language/ast';
|
|
5
|
-
import { SchemaDef as SchemaDef$1 } from '@zenstackhq/
|
|
5
|
+
import { SchemaDef as SchemaDef$1 } from '@zenstackhq/schema';
|
|
6
6
|
|
|
7
7
|
declare function getTestDbProvider(): "sqlite" | "postgresql";
|
|
8
|
-
|
|
8
|
+
declare const TEST_PG_CONFIG: {
|
|
9
|
+
host: string;
|
|
10
|
+
port: number;
|
|
11
|
+
user: string;
|
|
12
|
+
password: string;
|
|
13
|
+
};
|
|
14
|
+
declare const TEST_PG_URL: string;
|
|
15
|
+
type ExtraTestClientOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* Database provider
|
|
18
|
+
*/
|
|
9
19
|
provider?: 'sqlite' | 'postgresql';
|
|
20
|
+
/**
|
|
21
|
+
* The main ZModel file. Only used when `usePrismaPush` is true and `schema` is an object.
|
|
22
|
+
*/
|
|
23
|
+
schemaFile?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Database name. If not provided, a name will be generated based on the test name.
|
|
26
|
+
*/
|
|
10
27
|
dbName?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Use `prisma db push` instead of ZenStack's `$pushSchema` for database initialization.
|
|
30
|
+
*/
|
|
11
31
|
usePrismaPush?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Extra source files to create and compile.
|
|
34
|
+
*/
|
|
12
35
|
extraSourceFiles?: Record<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* Working directory for the test client. If not provided, a temporary directory will be created.
|
|
38
|
+
*/
|
|
13
39
|
workDir?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Debug mode.
|
|
42
|
+
*/
|
|
14
43
|
debug?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* A sqlite database file to be used for the test. Only supported for sqlite provider.
|
|
46
|
+
*/
|
|
47
|
+
dbFile?: string;
|
|
48
|
+
/**
|
|
49
|
+
* PostgreSQL extensions to be added to the datasource. Only supported for postgresql provider.
|
|
50
|
+
*/
|
|
51
|
+
dataSourceExtensions?: string[];
|
|
52
|
+
/**
|
|
53
|
+
* Additional files to be copied to the working directory. The glob pattern is relative to the test file.
|
|
54
|
+
*/
|
|
55
|
+
copyFiles?: {
|
|
56
|
+
globPattern: string;
|
|
57
|
+
destination: string;
|
|
58
|
+
}[];
|
|
15
59
|
};
|
|
16
|
-
|
|
17
|
-
declare function createTestClient<Schema extends SchemaDef
|
|
60
|
+
type CreateTestClientOptions<Schema extends SchemaDef> = Omit<ClientOptions<Schema>, 'dialect'> & ExtraTestClientOptions;
|
|
61
|
+
declare function createTestClient<Schema extends SchemaDef, Options extends ClientOptions<Schema>, CreateOptions = Omit<Options, 'dialect'>>(schema: Schema, options?: CreateOptions): Promise<ClientContract<Schema, Options>>;
|
|
62
|
+
declare function createTestClient(schema: string, options?: CreateTestClientOptions<SchemaDef>): Promise<any>;
|
|
18
63
|
declare function createPolicyTestClient<Schema extends SchemaDef>(schema: Schema, options?: CreateTestClientOptions<Schema>): Promise<ClientContract<Schema>>;
|
|
19
64
|
declare function createPolicyTestClient<Schema extends SchemaDef>(schema: string, options?: CreateTestClientOptions<Schema>): Promise<any>;
|
|
20
65
|
declare function testLogger(e: LogEvent): void;
|
|
21
66
|
|
|
22
67
|
declare function createTestProject(zmodelContent?: string): string;
|
|
23
68
|
|
|
24
|
-
declare function generateTsSchema(schemaText: string, provider?: 'sqlite' | 'postgresql', dbUrl?: string, extraSourceFiles?: Record<string, string
|
|
69
|
+
declare function generateTsSchema(schemaText: string, provider?: 'sqlite' | 'postgresql', dbUrl?: string, extraSourceFiles?: Record<string, string>, withLiteSchema?: boolean): Promise<{
|
|
25
70
|
model: _zenstackhq_language_ast.Model;
|
|
26
71
|
workDir: string;
|
|
27
72
|
schema: SchemaDef$1;
|
|
73
|
+
schemaLite: SchemaDef$1 | undefined;
|
|
28
74
|
}>;
|
|
29
75
|
declare function generateTsSchemaFromFile(filePath: string): Promise<{
|
|
30
76
|
model: _zenstackhq_language_ast.Model;
|
|
31
77
|
workDir: string;
|
|
32
78
|
schema: SchemaDef$1;
|
|
79
|
+
schemaLite: SchemaDef$1 | undefined;
|
|
33
80
|
}>;
|
|
34
81
|
declare function generateTsSchemaInPlace(schemaPath: string): Promise<{
|
|
35
82
|
workDir: string;
|
|
36
83
|
schema: SchemaDef$1;
|
|
84
|
+
schemaLite: SchemaDef$1 | undefined;
|
|
37
85
|
}>;
|
|
38
86
|
declare function loadSchema(schema: string, additionalSchemas?: Record<string, string>): Promise<_zenstackhq_language_ast.Model>;
|
|
39
87
|
declare function loadSchemaWithError(schema: string, error: string | RegExp): Promise<void>;
|
|
40
88
|
|
|
41
|
-
export { type CreateTestClientOptions, createPolicyTestClient, createTestClient, createTestProject, generateTsSchema, generateTsSchemaFromFile, generateTsSchemaInPlace, getTestDbProvider, loadSchema, loadSchemaWithError, testLogger };
|
|
89
|
+
export { type CreateTestClientOptions, TEST_PG_CONFIG, TEST_PG_URL, createPolicyTestClient, createTestClient, createTestProject, generateTsSchema, generateTsSchemaFromFile, generateTsSchemaInPlace, getTestDbProvider, loadSchema, loadSchemaWithError, testLogger };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,41 +1,89 @@
|
|
|
1
|
-
import { ClientOptions, ClientContract } from '@zenstackhq/
|
|
2
|
-
import { SchemaDef } from '@zenstackhq/
|
|
1
|
+
import { ClientOptions, ClientContract } from '@zenstackhq/orm';
|
|
2
|
+
import { SchemaDef } from '@zenstackhq/orm/schema';
|
|
3
3
|
import { LogEvent } from 'kysely';
|
|
4
4
|
import * as _zenstackhq_language_ast from '@zenstackhq/language/ast';
|
|
5
|
-
import { SchemaDef as SchemaDef$1 } from '@zenstackhq/
|
|
5
|
+
import { SchemaDef as SchemaDef$1 } from '@zenstackhq/schema';
|
|
6
6
|
|
|
7
7
|
declare function getTestDbProvider(): "sqlite" | "postgresql";
|
|
8
|
-
|
|
8
|
+
declare const TEST_PG_CONFIG: {
|
|
9
|
+
host: string;
|
|
10
|
+
port: number;
|
|
11
|
+
user: string;
|
|
12
|
+
password: string;
|
|
13
|
+
};
|
|
14
|
+
declare const TEST_PG_URL: string;
|
|
15
|
+
type ExtraTestClientOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* Database provider
|
|
18
|
+
*/
|
|
9
19
|
provider?: 'sqlite' | 'postgresql';
|
|
20
|
+
/**
|
|
21
|
+
* The main ZModel file. Only used when `usePrismaPush` is true and `schema` is an object.
|
|
22
|
+
*/
|
|
23
|
+
schemaFile?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Database name. If not provided, a name will be generated based on the test name.
|
|
26
|
+
*/
|
|
10
27
|
dbName?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Use `prisma db push` instead of ZenStack's `$pushSchema` for database initialization.
|
|
30
|
+
*/
|
|
11
31
|
usePrismaPush?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Extra source files to create and compile.
|
|
34
|
+
*/
|
|
12
35
|
extraSourceFiles?: Record<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* Working directory for the test client. If not provided, a temporary directory will be created.
|
|
38
|
+
*/
|
|
13
39
|
workDir?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Debug mode.
|
|
42
|
+
*/
|
|
14
43
|
debug?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* A sqlite database file to be used for the test. Only supported for sqlite provider.
|
|
46
|
+
*/
|
|
47
|
+
dbFile?: string;
|
|
48
|
+
/**
|
|
49
|
+
* PostgreSQL extensions to be added to the datasource. Only supported for postgresql provider.
|
|
50
|
+
*/
|
|
51
|
+
dataSourceExtensions?: string[];
|
|
52
|
+
/**
|
|
53
|
+
* Additional files to be copied to the working directory. The glob pattern is relative to the test file.
|
|
54
|
+
*/
|
|
55
|
+
copyFiles?: {
|
|
56
|
+
globPattern: string;
|
|
57
|
+
destination: string;
|
|
58
|
+
}[];
|
|
15
59
|
};
|
|
16
|
-
|
|
17
|
-
declare function createTestClient<Schema extends SchemaDef
|
|
60
|
+
type CreateTestClientOptions<Schema extends SchemaDef> = Omit<ClientOptions<Schema>, 'dialect'> & ExtraTestClientOptions;
|
|
61
|
+
declare function createTestClient<Schema extends SchemaDef, Options extends ClientOptions<Schema>, CreateOptions = Omit<Options, 'dialect'>>(schema: Schema, options?: CreateOptions): Promise<ClientContract<Schema, Options>>;
|
|
62
|
+
declare function createTestClient(schema: string, options?: CreateTestClientOptions<SchemaDef>): Promise<any>;
|
|
18
63
|
declare function createPolicyTestClient<Schema extends SchemaDef>(schema: Schema, options?: CreateTestClientOptions<Schema>): Promise<ClientContract<Schema>>;
|
|
19
64
|
declare function createPolicyTestClient<Schema extends SchemaDef>(schema: string, options?: CreateTestClientOptions<Schema>): Promise<any>;
|
|
20
65
|
declare function testLogger(e: LogEvent): void;
|
|
21
66
|
|
|
22
67
|
declare function createTestProject(zmodelContent?: string): string;
|
|
23
68
|
|
|
24
|
-
declare function generateTsSchema(schemaText: string, provider?: 'sqlite' | 'postgresql', dbUrl?: string, extraSourceFiles?: Record<string, string
|
|
69
|
+
declare function generateTsSchema(schemaText: string, provider?: 'sqlite' | 'postgresql', dbUrl?: string, extraSourceFiles?: Record<string, string>, withLiteSchema?: boolean): Promise<{
|
|
25
70
|
model: _zenstackhq_language_ast.Model;
|
|
26
71
|
workDir: string;
|
|
27
72
|
schema: SchemaDef$1;
|
|
73
|
+
schemaLite: SchemaDef$1 | undefined;
|
|
28
74
|
}>;
|
|
29
75
|
declare function generateTsSchemaFromFile(filePath: string): Promise<{
|
|
30
76
|
model: _zenstackhq_language_ast.Model;
|
|
31
77
|
workDir: string;
|
|
32
78
|
schema: SchemaDef$1;
|
|
79
|
+
schemaLite: SchemaDef$1 | undefined;
|
|
33
80
|
}>;
|
|
34
81
|
declare function generateTsSchemaInPlace(schemaPath: string): Promise<{
|
|
35
82
|
workDir: string;
|
|
36
83
|
schema: SchemaDef$1;
|
|
84
|
+
schemaLite: SchemaDef$1 | undefined;
|
|
37
85
|
}>;
|
|
38
86
|
declare function loadSchema(schema: string, additionalSchemas?: Record<string, string>): Promise<_zenstackhq_language_ast.Model>;
|
|
39
87
|
declare function loadSchemaWithError(schema: string, error: string | RegExp): Promise<void>;
|
|
40
88
|
|
|
41
|
-
export { type CreateTestClientOptions, createPolicyTestClient, createTestClient, createTestProject, generateTsSchema, generateTsSchemaFromFile, generateTsSchemaInPlace, getTestDbProvider, loadSchema, loadSchemaWithError, testLogger };
|
|
89
|
+
export { type CreateTestClientOptions, TEST_PG_CONFIG, TEST_PG_URL, createPolicyTestClient, createTestClient, createTestProject, generateTsSchema, generateTsSchemaFromFile, generateTsSchemaInPlace, getTestDbProvider, loadSchema, loadSchemaWithError, testLogger };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
|
|
4
10
|
// src/client.ts
|
|
5
11
|
import { invariant as invariant2 } from "@zenstackhq/common-helpers";
|
|
6
|
-
import {
|
|
12
|
+
import { ZenStackClient } from "@zenstackhq/orm";
|
|
7
13
|
import { PolicyPlugin } from "@zenstackhq/plugin-policy";
|
|
8
|
-
import { ZenStackClient } from "@zenstackhq/runtime";
|
|
9
14
|
import { PrismaSchemaGenerator } from "@zenstackhq/sdk";
|
|
10
15
|
import SQLite from "better-sqlite3";
|
|
16
|
+
import { glob } from "glob";
|
|
11
17
|
import { PostgresDialect, SqliteDialect } from "kysely";
|
|
12
18
|
import { execSync as execSync2 } from "child_process";
|
|
13
19
|
import { createHash } from "crypto";
|
|
@@ -35,7 +41,7 @@ function createTestProject(zmodelContent) {
|
|
|
35
41
|
const zenstackPackages = [
|
|
36
42
|
"language",
|
|
37
43
|
"sdk",
|
|
38
|
-
"
|
|
44
|
+
"orm",
|
|
39
45
|
"cli"
|
|
40
46
|
];
|
|
41
47
|
fs.mkdirSync(path.join(workDir, "node_modules/@zenstackhq"));
|
|
@@ -69,7 +75,6 @@ __name(createTestProject, "createTestProject");
|
|
|
69
75
|
|
|
70
76
|
// src/schema.ts
|
|
71
77
|
import { invariant } from "@zenstackhq/common-helpers";
|
|
72
|
-
import { loadDocument } from "@zenstackhq/language";
|
|
73
78
|
import { TsSchemaGenerator } from "@zenstackhq/sdk";
|
|
74
79
|
import { execSync } from "child_process";
|
|
75
80
|
import crypto from "crypto";
|
|
@@ -78,6 +83,18 @@ import os from "os";
|
|
|
78
83
|
import path2 from "path";
|
|
79
84
|
import { match } from "ts-pattern";
|
|
80
85
|
import { expect } from "vitest";
|
|
86
|
+
|
|
87
|
+
// src/utils.ts
|
|
88
|
+
import { loadDocument } from "@zenstackhq/language";
|
|
89
|
+
function loadDocumentWithPlugins(filePath) {
|
|
90
|
+
const pluginModelFiles = [
|
|
91
|
+
__require.resolve("@zenstackhq/plugin-policy/plugin.zmodel")
|
|
92
|
+
];
|
|
93
|
+
return loadDocument(filePath, pluginModelFiles);
|
|
94
|
+
}
|
|
95
|
+
__name(loadDocumentWithPlugins, "loadDocumentWithPlugins");
|
|
96
|
+
|
|
97
|
+
// src/schema.ts
|
|
81
98
|
function makePrelude(provider, dbUrl) {
|
|
82
99
|
return match(provider).with("sqlite", () => {
|
|
83
100
|
return `
|
|
@@ -96,19 +113,27 @@ datasource db {
|
|
|
96
113
|
}).exhaustive();
|
|
97
114
|
}
|
|
98
115
|
__name(makePrelude, "makePrelude");
|
|
99
|
-
|
|
116
|
+
function replacePlaceholders(schemaText, provider, dbUrl) {
|
|
117
|
+
const url = dbUrl ?? (provider === "sqlite" ? "file:./test.db" : "postgres://postgres:postgres@localhost:5432/db");
|
|
118
|
+
return schemaText.replace(/\$DB_URL/g, url).replace(/\$PROVIDER/g, provider);
|
|
119
|
+
}
|
|
120
|
+
__name(replacePlaceholders, "replacePlaceholders");
|
|
121
|
+
async function generateTsSchema(schemaText, provider = "sqlite", dbUrl, extraSourceFiles, withLiteSchema) {
|
|
100
122
|
const workDir = createTestProject();
|
|
101
123
|
const zmodelPath = path2.join(workDir, "schema.zmodel");
|
|
102
124
|
const noPrelude = schemaText.includes("datasource ");
|
|
103
125
|
fs2.writeFileSync(zmodelPath, `${noPrelude ? "" : makePrelude(provider, dbUrl)}
|
|
104
126
|
|
|
105
|
-
${schemaText}`);
|
|
106
|
-
const result = await
|
|
127
|
+
${replacePlaceholders(schemaText, provider, dbUrl)}`);
|
|
128
|
+
const result = await loadDocumentWithPlugins(zmodelPath);
|
|
107
129
|
if (!result.success) {
|
|
108
130
|
throw new Error(`Failed to load schema from ${zmodelPath}: ${result.errors}`);
|
|
109
131
|
}
|
|
110
132
|
const generator = new TsSchemaGenerator();
|
|
111
|
-
await generator.generate(result.model,
|
|
133
|
+
await generator.generate(result.model, {
|
|
134
|
+
outDir: workDir,
|
|
135
|
+
lite: withLiteSchema
|
|
136
|
+
});
|
|
112
137
|
if (extraSourceFiles) {
|
|
113
138
|
for (const [fileName, content] of Object.entries(extraSourceFiles)) {
|
|
114
139
|
const filePath = path2.resolve(workDir, !fileName.endsWith(".ts") ? `${fileName}.ts` : fileName);
|
|
@@ -130,9 +155,15 @@ async function compileAndLoad(workDir) {
|
|
|
130
155
|
stdio: "inherit"
|
|
131
156
|
});
|
|
132
157
|
const module = await import(path2.join(workDir, "schema.js"));
|
|
158
|
+
let moduleLite;
|
|
159
|
+
try {
|
|
160
|
+
moduleLite = await import(path2.join(workDir, "schema-lite.js"));
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
133
163
|
return {
|
|
134
164
|
workDir,
|
|
135
|
-
schema: module.schema
|
|
165
|
+
schema: module.schema,
|
|
166
|
+
schemaLite: moduleLite?.schema
|
|
136
167
|
};
|
|
137
168
|
}
|
|
138
169
|
__name(compileAndLoad, "compileAndLoad");
|
|
@@ -143,12 +174,14 @@ function generateTsSchemaFromFile(filePath) {
|
|
|
143
174
|
__name(generateTsSchemaFromFile, "generateTsSchemaFromFile");
|
|
144
175
|
async function generateTsSchemaInPlace(schemaPath) {
|
|
145
176
|
const workDir = path2.dirname(schemaPath);
|
|
146
|
-
const result = await
|
|
177
|
+
const result = await loadDocumentWithPlugins(schemaPath);
|
|
147
178
|
if (!result.success) {
|
|
148
179
|
throw new Error(`Failed to load schema from ${schemaPath}: ${result.errors}`);
|
|
149
180
|
}
|
|
150
181
|
const generator = new TsSchemaGenerator();
|
|
151
|
-
await generator.generate(result.model,
|
|
182
|
+
await generator.generate(result.model, {
|
|
183
|
+
outDir: workDir
|
|
184
|
+
});
|
|
152
185
|
return compileAndLoad(workDir);
|
|
153
186
|
}
|
|
154
187
|
__name(generateTsSchemaInPlace, "generateTsSchemaInPlace");
|
|
@@ -171,7 +204,7 @@ ${schema}`;
|
|
|
171
204
|
fs2.writeFileSync(filePath, content);
|
|
172
205
|
}
|
|
173
206
|
}
|
|
174
|
-
const r = await
|
|
207
|
+
const r = await loadDocumentWithPlugins(tempFile);
|
|
175
208
|
expect(r).toSatisfy((r2) => r2.success, `Failed to load schema: ${r.errors?.map((e) => e.toString()).join(", ")}`);
|
|
176
209
|
invariant(r.success);
|
|
177
210
|
return r.model;
|
|
@@ -185,7 +218,7 @@ ${schema}`;
|
|
|
185
218
|
}
|
|
186
219
|
const tempFile = path2.join(os.tmpdir(), `zenstack-schema-${crypto.randomUUID()}.zmodel`);
|
|
187
220
|
fs2.writeFileSync(tempFile, schema);
|
|
188
|
-
const r = await
|
|
221
|
+
const r = await loadDocumentWithPlugins(tempFile);
|
|
189
222
|
expect(r.success).toBe(false);
|
|
190
223
|
invariant(!r.success);
|
|
191
224
|
if (typeof error === "string") {
|
|
@@ -214,20 +247,22 @@ var TEST_PG_CONFIG = {
|
|
|
214
247
|
user: process.env["TEST_PG_USER"] ?? "postgres",
|
|
215
248
|
password: process.env["TEST_PG_PASSWORD"] ?? "postgres"
|
|
216
249
|
};
|
|
217
|
-
|
|
250
|
+
var TEST_PG_URL = `postgres://${TEST_PG_CONFIG.user}:${TEST_PG_CONFIG.password}@${TEST_PG_CONFIG.host}:${TEST_PG_CONFIG.port}`;
|
|
251
|
+
async function createTestClient(schema, options) {
|
|
218
252
|
let workDir = options?.workDir;
|
|
219
253
|
let _schema;
|
|
220
254
|
const provider = options?.provider ?? getTestDbProvider() ?? "sqlite";
|
|
221
255
|
const dbName = options?.dbName ?? getTestDbName(provider);
|
|
222
|
-
const dbUrl = provider === "sqlite" ? `file:${dbName}` :
|
|
256
|
+
const dbUrl = provider === "sqlite" ? `file:${dbName}` : `${TEST_PG_URL}/${dbName}`;
|
|
223
257
|
let model;
|
|
224
258
|
if (typeof schema === "string") {
|
|
225
|
-
const generated = await generateTsSchema(schema, provider, dbUrl, options?.extraSourceFiles);
|
|
259
|
+
const generated = await generateTsSchema(schema, provider, dbUrl, options?.extraSourceFiles, void 0);
|
|
226
260
|
workDir = generated.workDir;
|
|
227
261
|
model = generated.model;
|
|
228
262
|
_schema = {
|
|
229
263
|
...generated.schema,
|
|
230
264
|
provider: {
|
|
265
|
+
...generated.schema.provider,
|
|
231
266
|
type: provider
|
|
232
267
|
}
|
|
233
268
|
};
|
|
@@ -239,49 +274,80 @@ async function createTestClient(schema, options, schemaFile) {
|
|
|
239
274
|
}
|
|
240
275
|
};
|
|
241
276
|
workDir ??= createTestProject();
|
|
242
|
-
if (schemaFile) {
|
|
243
|
-
let schemaContent = fs3.readFileSync(schemaFile, "utf-8");
|
|
277
|
+
if (options?.schemaFile) {
|
|
278
|
+
let schemaContent = fs3.readFileSync(options.schemaFile, "utf-8");
|
|
244
279
|
if (dbUrl) {
|
|
245
280
|
schemaContent = schemaContent.replace(/datasource\s+db\s*{[^}]*}/m, `datasource db {
|
|
246
281
|
provider = '${provider}'
|
|
247
282
|
url = '${dbUrl}'
|
|
283
|
+
${options.dataSourceExtensions ? `extensions = [${options.dataSourceExtensions.join(", ")}]` : ""}
|
|
248
284
|
}`);
|
|
249
285
|
}
|
|
250
286
|
fs3.writeFileSync(path3.join(workDir, "schema.zmodel"), schemaContent);
|
|
251
287
|
}
|
|
252
288
|
}
|
|
253
289
|
invariant2(workDir);
|
|
254
|
-
if (options?.debug) {
|
|
255
|
-
console.log(`Work directory: ${workDir}`);
|
|
256
|
-
}
|
|
257
290
|
const { plugins, ...rest } = options ?? {};
|
|
258
291
|
const _options = {
|
|
259
292
|
...rest
|
|
260
293
|
};
|
|
261
|
-
if (options?.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
294
|
+
if (options?.debug) {
|
|
295
|
+
console.log(`Work directory: ${workDir}`);
|
|
296
|
+
console.log(`Database name: ${dbName}`);
|
|
297
|
+
_options.log = testLogger;
|
|
298
|
+
}
|
|
299
|
+
if (options?.dbFile) {
|
|
300
|
+
if (provider !== "sqlite") {
|
|
301
|
+
throw new Error("dbFile option is only supported for sqlite provider");
|
|
302
|
+
}
|
|
303
|
+
fs3.copyFileSync(options.dbFile, path3.join(workDir, dbName));
|
|
304
|
+
}
|
|
305
|
+
if (options?.copyFiles) {
|
|
306
|
+
const state = expect2.getState();
|
|
307
|
+
const currentTestPath = state.testPath;
|
|
308
|
+
if (!currentTestPath) {
|
|
309
|
+
throw new Error("Unable to determine current test file path");
|
|
310
|
+
}
|
|
311
|
+
for (const { globPattern, destination } of options.copyFiles) {
|
|
312
|
+
const files = glob.sync(globPattern, {
|
|
313
|
+
cwd: path3.dirname(currentTestPath)
|
|
314
|
+
});
|
|
315
|
+
for (const file of files) {
|
|
316
|
+
const src = path3.resolve(path3.dirname(currentTestPath), file);
|
|
317
|
+
const dest = path3.resolve(workDir, destination, path3.basename(file));
|
|
318
|
+
fs3.mkdirSync(path3.dirname(dest), {
|
|
319
|
+
recursive: true
|
|
320
|
+
});
|
|
321
|
+
fs3.copyFileSync(src, dest);
|
|
267
322
|
}
|
|
268
|
-
model = r.model;
|
|
269
323
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
await
|
|
283
|
-
|
|
284
|
-
|
|
324
|
+
}
|
|
325
|
+
if (!options?.dbFile) {
|
|
326
|
+
if (options?.usePrismaPush) {
|
|
327
|
+
invariant2(typeof schema === "string" || options?.schemaFile, "a schema file must be provided when using prisma db push");
|
|
328
|
+
if (!model) {
|
|
329
|
+
const r = await loadDocumentWithPlugins(path3.join(workDir, "schema.zmodel"));
|
|
330
|
+
if (!r.success) {
|
|
331
|
+
throw new Error(r.errors.join("\n"));
|
|
332
|
+
}
|
|
333
|
+
model = r.model;
|
|
334
|
+
}
|
|
335
|
+
const prismaSchema = new PrismaSchemaGenerator(model);
|
|
336
|
+
const prismaSchemaText = await prismaSchema.generate();
|
|
337
|
+
fs3.writeFileSync(path3.resolve(workDir, "schema.prisma"), prismaSchemaText);
|
|
338
|
+
execSync2("npx prisma db push --schema ./schema.prisma --skip-generate --force-reset", {
|
|
339
|
+
cwd: workDir,
|
|
340
|
+
stdio: "ignore"
|
|
341
|
+
});
|
|
342
|
+
} else {
|
|
343
|
+
if (provider === "postgresql") {
|
|
344
|
+
invariant2(dbName, "dbName is required");
|
|
345
|
+
const pgClient = new PGClient(TEST_PG_CONFIG);
|
|
346
|
+
await pgClient.connect();
|
|
347
|
+
await pgClient.query(`DROP DATABASE IF EXISTS "${dbName}"`);
|
|
348
|
+
await pgClient.query(`CREATE DATABASE "${dbName}"`);
|
|
349
|
+
await pgClient.end();
|
|
350
|
+
}
|
|
285
351
|
}
|
|
286
352
|
}
|
|
287
353
|
if (provider === "postgresql") {
|
|
@@ -297,7 +363,7 @@ async function createTestClient(schema, options, schemaFile) {
|
|
|
297
363
|
});
|
|
298
364
|
}
|
|
299
365
|
let client = new ZenStackClient(_schema, _options);
|
|
300
|
-
if (!options?.usePrismaPush) {
|
|
366
|
+
if (!options?.usePrismaPush && !options?.dbFile) {
|
|
301
367
|
await client.$pushSchema();
|
|
302
368
|
}
|
|
303
369
|
if (plugins) {
|
|
@@ -326,38 +392,37 @@ function getTestDbName(provider) {
|
|
|
326
392
|
if (provider === "sqlite") {
|
|
327
393
|
return "./test.db";
|
|
328
394
|
}
|
|
329
|
-
const testName = expect2.getState().currentTestName;
|
|
395
|
+
const testName = expect2.getState().currentTestName ?? "unnamed";
|
|
330
396
|
const testPath = expect2.getState().testPath ?? "";
|
|
331
|
-
invariant2(testName);
|
|
332
397
|
const digest = createHash("md5").update(testName + testPath).digest("hex");
|
|
333
398
|
return "test_" + testName.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").substring(0, 30) + digest.slice(0, 6);
|
|
334
399
|
}
|
|
335
400
|
__name(getTestDbName, "getTestDbName");
|
|
336
401
|
|
|
337
402
|
// src/vitest-ext.ts
|
|
338
|
-
import {
|
|
403
|
+
import { ORMError, ORMErrorReason } from "@zenstackhq/orm";
|
|
339
404
|
import { expect as expect3 } from "vitest";
|
|
340
405
|
function isPromise(value) {
|
|
341
406
|
return typeof value.then === "function" && typeof value.catch === "function";
|
|
342
407
|
}
|
|
343
408
|
__name(isPromise, "isPromise");
|
|
344
|
-
function
|
|
345
|
-
if (err instanceof
|
|
409
|
+
function expectErrorReason(err, errorReason) {
|
|
410
|
+
if (err instanceof ORMError && err.reason === errorReason) {
|
|
346
411
|
return {
|
|
347
412
|
message: /* @__PURE__ */ __name(() => "", "message"),
|
|
348
413
|
pass: true
|
|
349
414
|
};
|
|
350
415
|
} else {
|
|
351
416
|
return {
|
|
352
|
-
message: /* @__PURE__ */ __name(() => `expected ${
|
|
417
|
+
message: /* @__PURE__ */ __name(() => `expected ORMError of reason ${errorReason}, got ${err}`, "message"),
|
|
353
418
|
pass: false
|
|
354
419
|
};
|
|
355
420
|
}
|
|
356
421
|
}
|
|
357
|
-
__name(
|
|
422
|
+
__name(expectErrorReason, "expectErrorReason");
|
|
358
423
|
function expectErrorMessages(expectedMessages, message) {
|
|
359
424
|
for (const m of expectedMessages) {
|
|
360
|
-
if (!message.includes(m)) {
|
|
425
|
+
if (!message.toLowerCase().includes(m.toLowerCase())) {
|
|
361
426
|
return {
|
|
362
427
|
message: /* @__PURE__ */ __name(() => `expected message not found in error: ${m}, got message: ${message}`, "message"),
|
|
363
428
|
pass: false
|
|
@@ -424,7 +489,7 @@ expect3.extend({
|
|
|
424
489
|
try {
|
|
425
490
|
await received;
|
|
426
491
|
} catch (err) {
|
|
427
|
-
return
|
|
492
|
+
return expectErrorReason(err, ORMErrorReason.NOT_FOUND);
|
|
428
493
|
}
|
|
429
494
|
return {
|
|
430
495
|
message: /* @__PURE__ */ __name(() => `expected NotFoundError, got no error`, "message"),
|
|
@@ -441,13 +506,13 @@ expect3.extend({
|
|
|
441
506
|
try {
|
|
442
507
|
await received;
|
|
443
508
|
} catch (err) {
|
|
444
|
-
if (expectedMessages && err instanceof
|
|
509
|
+
if (expectedMessages && err instanceof ORMError && err.reason === ORMErrorReason.REJECTED_BY_POLICY) {
|
|
445
510
|
const r = expectErrorMessages(expectedMessages, err.message || "");
|
|
446
511
|
if (r) {
|
|
447
512
|
return r;
|
|
448
513
|
}
|
|
449
514
|
}
|
|
450
|
-
return
|
|
515
|
+
return expectErrorReason(err, ORMErrorReason.REJECTED_BY_POLICY);
|
|
451
516
|
}
|
|
452
517
|
return {
|
|
453
518
|
message: /* @__PURE__ */ __name(() => `expected PolicyError, got no error`, "message"),
|
|
@@ -464,13 +529,13 @@ expect3.extend({
|
|
|
464
529
|
try {
|
|
465
530
|
await received;
|
|
466
531
|
} catch (err) {
|
|
467
|
-
if (expectedMessages && err instanceof
|
|
532
|
+
if (expectedMessages && err instanceof ORMError && err.reason === ORMErrorReason.INVALID_INPUT) {
|
|
468
533
|
const r = expectErrorMessages(expectedMessages, err.message || "");
|
|
469
534
|
if (r) {
|
|
470
535
|
return r;
|
|
471
536
|
}
|
|
472
537
|
}
|
|
473
|
-
return
|
|
538
|
+
return expectErrorReason(err, ORMErrorReason.INVALID_INPUT);
|
|
474
539
|
}
|
|
475
540
|
return {
|
|
476
541
|
message: /* @__PURE__ */ __name(() => `expected InputValidationError, got no error`, "message"),
|
|
@@ -479,6 +544,8 @@ expect3.extend({
|
|
|
479
544
|
}
|
|
480
545
|
});
|
|
481
546
|
export {
|
|
547
|
+
TEST_PG_CONFIG,
|
|
548
|
+
TEST_PG_URL,
|
|
482
549
|
createPolicyTestClient,
|
|
483
550
|
createTestClient,
|
|
484
551
|
createTestProject,
|