@zenstackhq/testtools 3.0.0-alpha.9 → 3.0.0-beta.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/dist/index.cjs +406 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -6
- package/dist/index.d.ts +33 -6
- package/dist/index.js +404 -14
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/package.json +19 -9
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,41 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClientOptions, ClientContract } from '@zenstackhq/runtime';
|
|
2
|
+
import { SchemaDef } from '@zenstackhq/runtime/schema';
|
|
3
|
+
import { LogEvent } from 'kysely';
|
|
4
|
+
import * as _zenstackhq_language_ast from '@zenstackhq/language/ast';
|
|
5
|
+
import { SchemaDef as SchemaDef$1 } from '@zenstackhq/sdk/schema';
|
|
2
6
|
|
|
3
|
-
declare function
|
|
7
|
+
declare function getTestDbProvider(): "sqlite" | "postgresql";
|
|
8
|
+
type CreateTestClientOptions<Schema extends SchemaDef> = Omit<ClientOptions<Schema>, 'dialect'> & {
|
|
9
|
+
provider?: 'sqlite' | 'postgresql';
|
|
10
|
+
dbName?: string;
|
|
11
|
+
usePrismaPush?: boolean;
|
|
12
|
+
extraSourceFiles?: Record<string, string>;
|
|
13
|
+
workDir?: string;
|
|
14
|
+
debug?: boolean;
|
|
15
|
+
};
|
|
16
|
+
declare function createTestClient<Schema extends SchemaDef>(schema: Schema, options?: CreateTestClientOptions<Schema>, schemaFile?: string): Promise<ClientContract<Schema>>;
|
|
17
|
+
declare function createTestClient<Schema extends SchemaDef>(schema: string, options?: CreateTestClientOptions<Schema>): Promise<any>;
|
|
18
|
+
declare function createPolicyTestClient<Schema extends SchemaDef>(schema: Schema, options?: CreateTestClientOptions<Schema>): Promise<ClientContract<Schema>>;
|
|
19
|
+
declare function createPolicyTestClient<Schema extends SchemaDef>(schema: string, options?: CreateTestClientOptions<Schema>): Promise<any>;
|
|
20
|
+
declare function testLogger(e: LogEvent): void;
|
|
4
21
|
|
|
5
|
-
declare function
|
|
22
|
+
declare function createTestProject(zmodelContent?: string): string;
|
|
23
|
+
|
|
24
|
+
declare function generateTsSchema(schemaText: string, provider?: 'sqlite' | 'postgresql', dbUrl?: string, extraSourceFiles?: Record<string, string>): Promise<{
|
|
25
|
+
model: _zenstackhq_language_ast.Model;
|
|
6
26
|
workDir: string;
|
|
7
|
-
schema: SchemaDef;
|
|
27
|
+
schema: SchemaDef$1;
|
|
8
28
|
}>;
|
|
9
29
|
declare function generateTsSchemaFromFile(filePath: string): Promise<{
|
|
30
|
+
model: _zenstackhq_language_ast.Model;
|
|
31
|
+
workDir: string;
|
|
32
|
+
schema: SchemaDef$1;
|
|
33
|
+
}>;
|
|
34
|
+
declare function generateTsSchemaInPlace(schemaPath: string): Promise<{
|
|
10
35
|
workDir: string;
|
|
11
|
-
schema: SchemaDef;
|
|
36
|
+
schema: SchemaDef$1;
|
|
12
37
|
}>;
|
|
38
|
+
declare function loadSchema(schema: string, additionalSchemas?: Record<string, string>): Promise<_zenstackhq_language_ast.Model>;
|
|
39
|
+
declare function loadSchemaWithError(schema: string, error: string | RegExp): Promise<void>;
|
|
13
40
|
|
|
14
|
-
export { createTestProject, generateTsSchema, generateTsSchemaFromFile };
|
|
41
|
+
export { type CreateTestClientOptions, createPolicyTestClient, createTestClient, createTestProject, generateTsSchema, generateTsSchemaFromFile, generateTsSchemaInPlace, getTestDbProvider, loadSchema, loadSchemaWithError, testLogger };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
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
|
+
});
|
|
9
|
+
|
|
10
|
+
// src/client.ts
|
|
11
|
+
import { invariant as invariant2 } from "@zenstackhq/common-helpers";
|
|
12
|
+
import { PolicyPlugin } from "@zenstackhq/plugin-policy";
|
|
13
|
+
import { ZenStackClient } from "@zenstackhq/runtime";
|
|
14
|
+
import { PrismaSchemaGenerator } from "@zenstackhq/sdk";
|
|
15
|
+
import SQLite from "better-sqlite3";
|
|
16
|
+
import { PostgresDialect, SqliteDialect } from "kysely";
|
|
17
|
+
import { execSync as execSync2 } from "child_process";
|
|
18
|
+
import { createHash } from "crypto";
|
|
19
|
+
import fs3 from "fs";
|
|
20
|
+
import path3 from "path";
|
|
21
|
+
import { Client as PGClient, Pool } from "pg";
|
|
22
|
+
import { expect as expect2 } from "vitest";
|
|
3
23
|
|
|
4
24
|
// src/project.ts
|
|
5
25
|
import fs from "fs";
|
|
6
26
|
import path from "path";
|
|
7
27
|
import tmp from "tmp";
|
|
8
|
-
function createTestProject() {
|
|
28
|
+
function createTestProject(zmodelContent) {
|
|
9
29
|
const { name: workDir } = tmp.dirSync({
|
|
10
30
|
unsafeCleanup: true
|
|
11
31
|
});
|
|
@@ -45,56 +65,82 @@ function createTestProject() {
|
|
|
45
65
|
"**/*.ts"
|
|
46
66
|
]
|
|
47
67
|
}, null, 4));
|
|
68
|
+
if (zmodelContent) {
|
|
69
|
+
fs.writeFileSync(path.join(workDir, "schema.zmodel"), zmodelContent);
|
|
70
|
+
}
|
|
48
71
|
return workDir;
|
|
49
72
|
}
|
|
50
73
|
__name(createTestProject, "createTestProject");
|
|
51
74
|
|
|
52
75
|
// src/schema.ts
|
|
76
|
+
import { invariant } from "@zenstackhq/common-helpers";
|
|
53
77
|
import { TsSchemaGenerator } from "@zenstackhq/sdk";
|
|
54
|
-
import { glob } from "glob";
|
|
55
78
|
import { execSync } from "child_process";
|
|
79
|
+
import crypto from "crypto";
|
|
56
80
|
import fs2 from "fs";
|
|
81
|
+
import os from "os";
|
|
57
82
|
import path2 from "path";
|
|
58
83
|
import { match } from "ts-pattern";
|
|
59
|
-
|
|
84
|
+
import { expect } from "vitest";
|
|
85
|
+
|
|
86
|
+
// src/utils.ts
|
|
87
|
+
import { loadDocument } from "@zenstackhq/language";
|
|
88
|
+
function loadDocumentWithPlugins(filePath) {
|
|
89
|
+
const pluginModelFiles = [
|
|
90
|
+
__require.resolve("@zenstackhq/plugin-policy/plugin.zmodel")
|
|
91
|
+
];
|
|
92
|
+
return loadDocument(filePath, pluginModelFiles);
|
|
93
|
+
}
|
|
94
|
+
__name(loadDocumentWithPlugins, "loadDocumentWithPlugins");
|
|
95
|
+
|
|
96
|
+
// src/schema.ts
|
|
97
|
+
function makePrelude(provider, dbUrl) {
|
|
60
98
|
return match(provider).with("sqlite", () => {
|
|
61
99
|
return `
|
|
62
100
|
datasource db {
|
|
63
101
|
provider = 'sqlite'
|
|
64
|
-
url = '${
|
|
102
|
+
url = '${dbUrl ?? "file:./test.db"}'
|
|
65
103
|
}
|
|
66
104
|
`;
|
|
67
105
|
}).with("postgresql", () => {
|
|
68
106
|
return `
|
|
69
107
|
datasource db {
|
|
70
108
|
provider = 'postgresql'
|
|
71
|
-
url = 'postgres://postgres:postgres@localhost:5432
|
|
109
|
+
url = '${dbUrl ?? "postgres://postgres:postgres@localhost:5432/db"}'
|
|
72
110
|
}
|
|
73
111
|
`;
|
|
74
112
|
}).exhaustive();
|
|
75
113
|
}
|
|
76
114
|
__name(makePrelude, "makePrelude");
|
|
77
|
-
async function generateTsSchema(schemaText, provider = "sqlite",
|
|
115
|
+
async function generateTsSchema(schemaText, provider = "sqlite", dbUrl, extraSourceFiles) {
|
|
78
116
|
const workDir = createTestProject();
|
|
79
|
-
console.log(`Work directory: ${workDir}`);
|
|
80
117
|
const zmodelPath = path2.join(workDir, "schema.zmodel");
|
|
81
118
|
const noPrelude = schemaText.includes("datasource ");
|
|
82
|
-
fs2.writeFileSync(zmodelPath, `${noPrelude ? "" : makePrelude(provider,
|
|
119
|
+
fs2.writeFileSync(zmodelPath, `${noPrelude ? "" : makePrelude(provider, dbUrl)}
|
|
83
120
|
|
|
84
121
|
${schemaText}`);
|
|
85
|
-
const
|
|
122
|
+
const result = await loadDocumentWithPlugins(zmodelPath);
|
|
123
|
+
if (!result.success) {
|
|
124
|
+
throw new Error(`Failed to load schema from ${zmodelPath}: ${result.errors}`);
|
|
125
|
+
}
|
|
86
126
|
const generator = new TsSchemaGenerator();
|
|
87
|
-
|
|
88
|
-
await generator.generate(zmodelPath, pluginModelFiles, tsPath);
|
|
127
|
+
await generator.generate(result.model, workDir);
|
|
89
128
|
if (extraSourceFiles) {
|
|
90
129
|
for (const [fileName, content] of Object.entries(extraSourceFiles)) {
|
|
91
|
-
const filePath = path2.resolve(workDir, `${fileName}.ts`);
|
|
130
|
+
const filePath = path2.resolve(workDir, !fileName.endsWith(".ts") ? `${fileName}.ts` : fileName);
|
|
92
131
|
fs2.mkdirSync(path2.dirname(filePath), {
|
|
93
132
|
recursive: true
|
|
94
133
|
});
|
|
95
134
|
fs2.writeFileSync(filePath, content);
|
|
96
135
|
}
|
|
97
136
|
}
|
|
137
|
+
return {
|
|
138
|
+
...await compileAndLoad(workDir),
|
|
139
|
+
model: result.model
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
__name(generateTsSchema, "generateTsSchema");
|
|
143
|
+
async function compileAndLoad(workDir) {
|
|
98
144
|
execSync("npx tsc", {
|
|
99
145
|
cwd: workDir,
|
|
100
146
|
stdio: "inherit"
|
|
@@ -105,15 +151,359 @@ ${schemaText}`);
|
|
|
105
151
|
schema: module.schema
|
|
106
152
|
};
|
|
107
153
|
}
|
|
108
|
-
__name(
|
|
154
|
+
__name(compileAndLoad, "compileAndLoad");
|
|
109
155
|
function generateTsSchemaFromFile(filePath) {
|
|
110
156
|
const schemaText = fs2.readFileSync(filePath, "utf8");
|
|
111
157
|
return generateTsSchema(schemaText);
|
|
112
158
|
}
|
|
113
159
|
__name(generateTsSchemaFromFile, "generateTsSchemaFromFile");
|
|
160
|
+
async function generateTsSchemaInPlace(schemaPath) {
|
|
161
|
+
const workDir = path2.dirname(schemaPath);
|
|
162
|
+
const result = await loadDocumentWithPlugins(schemaPath);
|
|
163
|
+
if (!result.success) {
|
|
164
|
+
throw new Error(`Failed to load schema from ${schemaPath}: ${result.errors}`);
|
|
165
|
+
}
|
|
166
|
+
const generator = new TsSchemaGenerator();
|
|
167
|
+
await generator.generate(result.model, workDir);
|
|
168
|
+
return compileAndLoad(workDir);
|
|
169
|
+
}
|
|
170
|
+
__name(generateTsSchemaInPlace, "generateTsSchemaInPlace");
|
|
171
|
+
async function loadSchema(schema, additionalSchemas) {
|
|
172
|
+
if (!schema.includes("datasource ")) {
|
|
173
|
+
schema = `${makePrelude("sqlite")}
|
|
174
|
+
|
|
175
|
+
${schema}`;
|
|
176
|
+
}
|
|
177
|
+
const tempDir = fs2.mkdtempSync(path2.join(os.tmpdir(), "zenstack-schema"));
|
|
178
|
+
const tempFile = path2.join(tempDir, `schema.zmodel`);
|
|
179
|
+
fs2.writeFileSync(tempFile, schema);
|
|
180
|
+
if (additionalSchemas) {
|
|
181
|
+
for (const [fileName, content] of Object.entries(additionalSchemas)) {
|
|
182
|
+
let name = fileName;
|
|
183
|
+
if (!name.endsWith(".zmodel")) {
|
|
184
|
+
name += ".zmodel";
|
|
185
|
+
}
|
|
186
|
+
const filePath = path2.join(tempDir, name);
|
|
187
|
+
fs2.writeFileSync(filePath, content);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const r = await loadDocumentWithPlugins(tempFile);
|
|
191
|
+
expect(r).toSatisfy((r2) => r2.success, `Failed to load schema: ${r.errors?.map((e) => e.toString()).join(", ")}`);
|
|
192
|
+
invariant(r.success);
|
|
193
|
+
return r.model;
|
|
194
|
+
}
|
|
195
|
+
__name(loadSchema, "loadSchema");
|
|
196
|
+
async function loadSchemaWithError(schema, error) {
|
|
197
|
+
if (!schema.includes("datasource ")) {
|
|
198
|
+
schema = `${makePrelude("sqlite")}
|
|
199
|
+
|
|
200
|
+
${schema}`;
|
|
201
|
+
}
|
|
202
|
+
const tempFile = path2.join(os.tmpdir(), `zenstack-schema-${crypto.randomUUID()}.zmodel`);
|
|
203
|
+
fs2.writeFileSync(tempFile, schema);
|
|
204
|
+
const r = await loadDocumentWithPlugins(tempFile);
|
|
205
|
+
expect(r.success).toBe(false);
|
|
206
|
+
invariant(!r.success);
|
|
207
|
+
if (typeof error === "string") {
|
|
208
|
+
expect(r).toSatisfy((r2) => r2.errors.some((e) => e.toString().toLowerCase().includes(error.toLowerCase())), `Expected error message to include "${error}" but got: ${r.errors.map((e) => e.toString()).join(", ")}`);
|
|
209
|
+
} else {
|
|
210
|
+
expect(r).toSatisfy((r2) => r2.errors.some((e) => error.test(e)), `Expected error message to match "${error}" but got: ${r.errors.map((e) => e.toString()).join(", ")}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
__name(loadSchemaWithError, "loadSchemaWithError");
|
|
214
|
+
|
|
215
|
+
// src/client.ts
|
|
216
|
+
function getTestDbProvider() {
|
|
217
|
+
const val = process.env["TEST_DB_PROVIDER"] ?? "sqlite";
|
|
218
|
+
if (![
|
|
219
|
+
"sqlite",
|
|
220
|
+
"postgresql"
|
|
221
|
+
].includes(val)) {
|
|
222
|
+
throw new Error(`Invalid TEST_DB_PROVIDER value: ${val}`);
|
|
223
|
+
}
|
|
224
|
+
return val;
|
|
225
|
+
}
|
|
226
|
+
__name(getTestDbProvider, "getTestDbProvider");
|
|
227
|
+
var TEST_PG_CONFIG = {
|
|
228
|
+
host: process.env["TEST_PG_HOST"] ?? "localhost",
|
|
229
|
+
port: process.env["TEST_PG_PORT"] ? parseInt(process.env["TEST_PG_PORT"]) : 5432,
|
|
230
|
+
user: process.env["TEST_PG_USER"] ?? "postgres",
|
|
231
|
+
password: process.env["TEST_PG_PASSWORD"] ?? "postgres"
|
|
232
|
+
};
|
|
233
|
+
async function createTestClient(schema, options, schemaFile) {
|
|
234
|
+
let workDir = options?.workDir;
|
|
235
|
+
let _schema;
|
|
236
|
+
const provider = options?.provider ?? getTestDbProvider() ?? "sqlite";
|
|
237
|
+
const dbName = options?.dbName ?? getTestDbName(provider);
|
|
238
|
+
const dbUrl = provider === "sqlite" ? `file:${dbName}` : `postgres://${TEST_PG_CONFIG.user}:${TEST_PG_CONFIG.password}@${TEST_PG_CONFIG.host}:${TEST_PG_CONFIG.port}/${dbName}`;
|
|
239
|
+
let model;
|
|
240
|
+
if (typeof schema === "string") {
|
|
241
|
+
const generated = await generateTsSchema(schema, provider, dbUrl, options?.extraSourceFiles);
|
|
242
|
+
workDir = generated.workDir;
|
|
243
|
+
model = generated.model;
|
|
244
|
+
_schema = {
|
|
245
|
+
...generated.schema,
|
|
246
|
+
provider: {
|
|
247
|
+
type: provider
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
} else {
|
|
251
|
+
_schema = {
|
|
252
|
+
...schema,
|
|
253
|
+
provider: {
|
|
254
|
+
type: provider
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
workDir ??= createTestProject();
|
|
258
|
+
if (schemaFile) {
|
|
259
|
+
let schemaContent = fs3.readFileSync(schemaFile, "utf-8");
|
|
260
|
+
if (dbUrl) {
|
|
261
|
+
schemaContent = schemaContent.replace(/datasource\s+db\s*{[^}]*}/m, `datasource db {
|
|
262
|
+
provider = '${provider}'
|
|
263
|
+
url = '${dbUrl}'
|
|
264
|
+
}`);
|
|
265
|
+
}
|
|
266
|
+
fs3.writeFileSync(path3.join(workDir, "schema.zmodel"), schemaContent);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
invariant2(workDir);
|
|
270
|
+
if (options?.debug) {
|
|
271
|
+
console.log(`Work directory: ${workDir}`);
|
|
272
|
+
}
|
|
273
|
+
const { plugins, ...rest } = options ?? {};
|
|
274
|
+
const _options = {
|
|
275
|
+
...rest
|
|
276
|
+
};
|
|
277
|
+
if (options?.usePrismaPush) {
|
|
278
|
+
invariant2(typeof schema === "string" || schemaFile, "a schema file must be provided when using prisma db push");
|
|
279
|
+
if (!model) {
|
|
280
|
+
const r = await loadDocumentWithPlugins(path3.join(workDir, "schema.zmodel"));
|
|
281
|
+
if (!r.success) {
|
|
282
|
+
throw new Error(r.errors.join("\n"));
|
|
283
|
+
}
|
|
284
|
+
model = r.model;
|
|
285
|
+
}
|
|
286
|
+
const prismaSchema = new PrismaSchemaGenerator(model);
|
|
287
|
+
const prismaSchemaText = await prismaSchema.generate();
|
|
288
|
+
fs3.writeFileSync(path3.resolve(workDir, "schema.prisma"), prismaSchemaText);
|
|
289
|
+
execSync2("npx prisma db push --schema ./schema.prisma --skip-generate --force-reset", {
|
|
290
|
+
cwd: workDir,
|
|
291
|
+
stdio: "ignore"
|
|
292
|
+
});
|
|
293
|
+
} else {
|
|
294
|
+
if (provider === "postgresql") {
|
|
295
|
+
invariant2(dbName, "dbName is required");
|
|
296
|
+
const pgClient = new PGClient(TEST_PG_CONFIG);
|
|
297
|
+
await pgClient.connect();
|
|
298
|
+
await pgClient.query(`DROP DATABASE IF EXISTS "${dbName}"`);
|
|
299
|
+
await pgClient.query(`CREATE DATABASE "${dbName}"`);
|
|
300
|
+
await pgClient.end();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (provider === "postgresql") {
|
|
304
|
+
_options.dialect = new PostgresDialect({
|
|
305
|
+
pool: new Pool({
|
|
306
|
+
...TEST_PG_CONFIG,
|
|
307
|
+
database: dbName
|
|
308
|
+
})
|
|
309
|
+
});
|
|
310
|
+
} else {
|
|
311
|
+
_options.dialect = new SqliteDialect({
|
|
312
|
+
database: new SQLite(path3.join(workDir, dbName))
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
let client = new ZenStackClient(_schema, _options);
|
|
316
|
+
if (!options?.usePrismaPush) {
|
|
317
|
+
await client.$pushSchema();
|
|
318
|
+
}
|
|
319
|
+
if (plugins) {
|
|
320
|
+
for (const plugin of plugins) {
|
|
321
|
+
client = client.$use(plugin);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return client;
|
|
325
|
+
}
|
|
326
|
+
__name(createTestClient, "createTestClient");
|
|
327
|
+
async function createPolicyTestClient(schema, options) {
|
|
328
|
+
return createTestClient(schema, {
|
|
329
|
+
...options,
|
|
330
|
+
plugins: [
|
|
331
|
+
...options?.plugins ?? [],
|
|
332
|
+
new PolicyPlugin()
|
|
333
|
+
]
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
__name(createPolicyTestClient, "createPolicyTestClient");
|
|
337
|
+
function testLogger(e) {
|
|
338
|
+
console.log(e.query.sql, e.query.parameters);
|
|
339
|
+
}
|
|
340
|
+
__name(testLogger, "testLogger");
|
|
341
|
+
function getTestDbName(provider) {
|
|
342
|
+
if (provider === "sqlite") {
|
|
343
|
+
return "./test.db";
|
|
344
|
+
}
|
|
345
|
+
const testName = expect2.getState().currentTestName;
|
|
346
|
+
const testPath = expect2.getState().testPath ?? "";
|
|
347
|
+
invariant2(testName);
|
|
348
|
+
const digest = createHash("md5").update(testName + testPath).digest("hex");
|
|
349
|
+
return "test_" + testName.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").substring(0, 30) + digest.slice(0, 6);
|
|
350
|
+
}
|
|
351
|
+
__name(getTestDbName, "getTestDbName");
|
|
352
|
+
|
|
353
|
+
// src/vitest-ext.ts
|
|
354
|
+
import { InputValidationError, NotFoundError, RejectedByPolicyError } from "@zenstackhq/runtime";
|
|
355
|
+
import { expect as expect3 } from "vitest";
|
|
356
|
+
function isPromise(value) {
|
|
357
|
+
return typeof value.then === "function" && typeof value.catch === "function";
|
|
358
|
+
}
|
|
359
|
+
__name(isPromise, "isPromise");
|
|
360
|
+
function expectError(err, errorType) {
|
|
361
|
+
if (err instanceof errorType) {
|
|
362
|
+
return {
|
|
363
|
+
message: /* @__PURE__ */ __name(() => "", "message"),
|
|
364
|
+
pass: true
|
|
365
|
+
};
|
|
366
|
+
} else {
|
|
367
|
+
return {
|
|
368
|
+
message: /* @__PURE__ */ __name(() => `expected ${errorType}, got ${err}`, "message"),
|
|
369
|
+
pass: false
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
__name(expectError, "expectError");
|
|
374
|
+
function expectErrorMessages(expectedMessages, message) {
|
|
375
|
+
for (const m of expectedMessages) {
|
|
376
|
+
if (!message.includes(m)) {
|
|
377
|
+
return {
|
|
378
|
+
message: /* @__PURE__ */ __name(() => `expected message not found in error: ${m}, got message: ${message}`, "message"),
|
|
379
|
+
pass: false
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return void 0;
|
|
384
|
+
}
|
|
385
|
+
__name(expectErrorMessages, "expectErrorMessages");
|
|
386
|
+
expect3.extend({
|
|
387
|
+
async toResolveTruthy(received) {
|
|
388
|
+
if (!isPromise(received)) {
|
|
389
|
+
return {
|
|
390
|
+
message: /* @__PURE__ */ __name(() => "a promise is expected", "message"),
|
|
391
|
+
pass: false
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
const r = await received;
|
|
395
|
+
return {
|
|
396
|
+
pass: !!r,
|
|
397
|
+
message: /* @__PURE__ */ __name(() => `Expected promise to resolve to a truthy value, but got ${r}`, "message")
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
async toResolveFalsy(received) {
|
|
401
|
+
if (!isPromise(received)) {
|
|
402
|
+
return {
|
|
403
|
+
message: /* @__PURE__ */ __name(() => "a promise is expected", "message"),
|
|
404
|
+
pass: false
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const r = await received;
|
|
408
|
+
return {
|
|
409
|
+
pass: !r,
|
|
410
|
+
message: /* @__PURE__ */ __name(() => `Expected promise to resolve to a falsy value, but got ${r}`, "message")
|
|
411
|
+
};
|
|
412
|
+
},
|
|
413
|
+
async toResolveNull(received) {
|
|
414
|
+
if (!isPromise(received)) {
|
|
415
|
+
return {
|
|
416
|
+
message: /* @__PURE__ */ __name(() => "a promise is expected", "message"),
|
|
417
|
+
pass: false
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
const r = await received;
|
|
421
|
+
return {
|
|
422
|
+
pass: r === null,
|
|
423
|
+
message: /* @__PURE__ */ __name(() => `Expected promise to resolve to a null value, but got ${r}`, "message")
|
|
424
|
+
};
|
|
425
|
+
},
|
|
426
|
+
async toResolveWithLength(received, length) {
|
|
427
|
+
const r = await received;
|
|
428
|
+
return {
|
|
429
|
+
pass: Array.isArray(r) && r.length === length,
|
|
430
|
+
message: /* @__PURE__ */ __name(() => `Expected promise to resolve with an array with length ${length}, but got ${r}`, "message")
|
|
431
|
+
};
|
|
432
|
+
},
|
|
433
|
+
async toBeRejectedNotFound(received) {
|
|
434
|
+
if (!isPromise(received)) {
|
|
435
|
+
return {
|
|
436
|
+
message: /* @__PURE__ */ __name(() => "a promise is expected", "message"),
|
|
437
|
+
pass: false
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
try {
|
|
441
|
+
await received;
|
|
442
|
+
} catch (err) {
|
|
443
|
+
return expectError(err, NotFoundError);
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
message: /* @__PURE__ */ __name(() => `expected NotFoundError, got no error`, "message"),
|
|
447
|
+
pass: false
|
|
448
|
+
};
|
|
449
|
+
},
|
|
450
|
+
async toBeRejectedByPolicy(received, expectedMessages) {
|
|
451
|
+
if (!isPromise(received)) {
|
|
452
|
+
return {
|
|
453
|
+
message: /* @__PURE__ */ __name(() => "a promise is expected", "message"),
|
|
454
|
+
pass: false
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
await received;
|
|
459
|
+
} catch (err) {
|
|
460
|
+
if (expectedMessages && err instanceof RejectedByPolicyError) {
|
|
461
|
+
const r = expectErrorMessages(expectedMessages, err.message || "");
|
|
462
|
+
if (r) {
|
|
463
|
+
return r;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return expectError(err, RejectedByPolicyError);
|
|
467
|
+
}
|
|
468
|
+
return {
|
|
469
|
+
message: /* @__PURE__ */ __name(() => `expected PolicyError, got no error`, "message"),
|
|
470
|
+
pass: false
|
|
471
|
+
};
|
|
472
|
+
},
|
|
473
|
+
async toBeRejectedByValidation(received, expectedMessages) {
|
|
474
|
+
if (!isPromise(received)) {
|
|
475
|
+
return {
|
|
476
|
+
message: /* @__PURE__ */ __name(() => "a promise is expected", "message"),
|
|
477
|
+
pass: false
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
await received;
|
|
482
|
+
} catch (err) {
|
|
483
|
+
if (expectedMessages && err instanceof InputValidationError) {
|
|
484
|
+
const r = expectErrorMessages(expectedMessages, err.message || "");
|
|
485
|
+
if (r) {
|
|
486
|
+
return r;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return expectError(err, InputValidationError);
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
message: /* @__PURE__ */ __name(() => `expected InputValidationError, got no error`, "message"),
|
|
493
|
+
pass: false
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
});
|
|
114
497
|
export {
|
|
498
|
+
createPolicyTestClient,
|
|
499
|
+
createTestClient,
|
|
115
500
|
createTestProject,
|
|
116
501
|
generateTsSchema,
|
|
117
|
-
generateTsSchemaFromFile
|
|
502
|
+
generateTsSchemaFromFile,
|
|
503
|
+
generateTsSchemaInPlace,
|
|
504
|
+
getTestDbProvider,
|
|
505
|
+
loadSchema,
|
|
506
|
+
loadSchemaWithError,
|
|
507
|
+
testLogger
|
|
118
508
|
};
|
|
119
509
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/project.ts","../src/schema.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport tmp from 'tmp';\n\nexport function createTestProject() {\n const { name: workDir } = tmp.dirSync({ unsafeCleanup: true });\n\n fs.mkdirSync(path.join(workDir, 'node_modules'));\n\n // symlink all entries from \"node_modules\"\n const nodeModules = fs.readdirSync(path.join(__dirname, '../node_modules'));\n for (const entry of nodeModules) {\n if (entry.startsWith('@zenstackhq')) {\n continue;\n }\n fs.symlinkSync(\n path.join(__dirname, '../node_modules', entry),\n path.join(workDir, 'node_modules', entry),\n 'dir',\n );\n }\n\n // in addition, symlink zenstack packages\n const zenstackPackages = ['language', 'sdk', 'runtime', 'cli'];\n fs.mkdirSync(path.join(workDir, 'node_modules/@zenstackhq'));\n for (const pkg of zenstackPackages) {\n fs.symlinkSync(\n path.join(__dirname, `../../${pkg}`),\n path.join(workDir, `node_modules/@zenstackhq/${pkg}`),\n 'dir',\n );\n }\n\n fs.writeFileSync(\n path.join(workDir, 'package.json'),\n JSON.stringify(\n {\n name: 'test',\n version: '1.0.0',\n type: 'module',\n },\n null,\n 4,\n ),\n );\n\n fs.writeFileSync(\n path.join(workDir, 'tsconfig.json'),\n JSON.stringify(\n {\n compilerOptions: {\n module: 'ESNext',\n target: 'ESNext',\n moduleResolution: 'Bundler',\n esModuleInterop: true,\n skipLibCheck: true,\n strict: true,\n },\n include: ['**/*.ts'],\n },\n null,\n 4,\n ),\n );\n\n return workDir;\n}\n","import { TsSchemaGenerator } from '@zenstackhq/sdk';\nimport type { SchemaDef } from '@zenstackhq/sdk/schema';\nimport { glob } from 'glob';\nimport { execSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { match } from 'ts-pattern';\nimport { createTestProject } from './project';\n\nfunction makePrelude(provider: 'sqlite' | 'postgresql', dbName?: string) {\n return match(provider)\n .with('sqlite', () => {\n return `\ndatasource db {\n provider = 'sqlite'\n url = '${dbName ?? ':memory:'}'\n}\n`;\n })\n .with('postgresql', () => {\n return `\ndatasource db {\n provider = 'postgresql'\n url = 'postgres://postgres:postgres@localhost:5432/${dbName}'\n}\n`;\n })\n .exhaustive();\n}\n\nexport async function generateTsSchema(\n schemaText: string,\n provider: 'sqlite' | 'postgresql' = 'sqlite',\n dbName?: string,\n extraSourceFiles?: Record<string, string>,\n) {\n const workDir = createTestProject();\n console.log(`Work directory: ${workDir}`);\n\n const zmodelPath = path.join(workDir, 'schema.zmodel');\n const noPrelude = schemaText.includes('datasource ');\n fs.writeFileSync(zmodelPath, `${noPrelude ? '' : makePrelude(provider, dbName)}\\n\\n${schemaText}`);\n\n const pluginModelFiles = glob.sync(path.resolve(__dirname, '../../runtime/src/plugins/**/plugin.zmodel'));\n\n const generator = new TsSchemaGenerator();\n const tsPath = path.join(workDir, 'schema.ts');\n await generator.generate(zmodelPath, pluginModelFiles, tsPath);\n\n if (extraSourceFiles) {\n for (const [fileName, content] of Object.entries(extraSourceFiles)) {\n const filePath = path.resolve(workDir, `${fileName}.ts`);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content);\n }\n }\n\n // compile the generated TS schema\n execSync('npx tsc', {\n cwd: workDir,\n stdio: 'inherit',\n });\n\n // load the schema module\n const module = await import(path.join(workDir, 'schema.js'));\n return { workDir, schema: module.schema as SchemaDef };\n}\n\nexport function generateTsSchemaFromFile(filePath: string) {\n const schemaText = fs.readFileSync(filePath, 'utf8');\n return generateTsSchema(schemaText);\n}\n"],"mappings":"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","names":["fs","path","tmp","createTestProject","name","workDir","tmp","dirSync","unsafeCleanup","fs","mkdirSync","path","join","nodeModules","readdirSync","__dirname","entry","startsWith","symlinkSync","zenstackPackages","pkg","writeFileSync","JSON","stringify","version","type","compilerOptions","module","target","moduleResolution","esModuleInterop","skipLibCheck","strict","include","TsSchemaGenerator","glob","execSync","fs","path","match","makePrelude","provider","dbName","match","with","exhaustive","generateTsSchema","schemaText","extraSourceFiles","workDir","createTestProject","console","log","zmodelPath","path","join","noPrelude","includes","fs","writeFileSync","pluginModelFiles","glob","sync","resolve","__dirname","generator","TsSchemaGenerator","tsPath","generate","fileName","content","Object","entries","filePath","mkdirSync","dirname","recursive","execSync","cwd","stdio","module","schema","generateTsSchemaFromFile","readFileSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/project.ts","../src/schema.ts","../src/utils.ts","../src/vitest-ext.ts"],"sourcesContent":["import { invariant } from '@zenstackhq/common-helpers';\nimport type { Model } from '@zenstackhq/language/ast';\nimport { PolicyPlugin } from '@zenstackhq/plugin-policy';\nimport { ZenStackClient, type ClientContract, type ClientOptions } from '@zenstackhq/runtime';\nimport type { SchemaDef } from '@zenstackhq/runtime/schema';\nimport { PrismaSchemaGenerator } from '@zenstackhq/sdk';\nimport SQLite from 'better-sqlite3';\nimport { PostgresDialect, SqliteDialect, type LogEvent } from 'kysely';\nimport { execSync } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { Client as PGClient, Pool } from 'pg';\nimport { expect } from 'vitest';\nimport { createTestProject } from './project';\nimport { generateTsSchema } from './schema';\nimport { loadDocumentWithPlugins } from './utils';\n\nexport function getTestDbProvider() {\n const val = process.env['TEST_DB_PROVIDER'] ?? 'sqlite';\n if (!['sqlite', 'postgresql'].includes(val!)) {\n throw new Error(`Invalid TEST_DB_PROVIDER value: ${val}`);\n }\n return val as 'sqlite' | 'postgresql';\n}\n\nconst TEST_PG_CONFIG = {\n host: process.env['TEST_PG_HOST'] ?? 'localhost',\n port: process.env['TEST_PG_PORT'] ? parseInt(process.env['TEST_PG_PORT']) : 5432,\n user: process.env['TEST_PG_USER'] ?? 'postgres',\n password: process.env['TEST_PG_PASSWORD'] ?? 'postgres',\n};\n\nexport type CreateTestClientOptions<Schema extends SchemaDef> = Omit<ClientOptions<Schema>, 'dialect'> & {\n provider?: 'sqlite' | 'postgresql';\n dbName?: string;\n usePrismaPush?: boolean;\n extraSourceFiles?: Record<string, string>;\n workDir?: string;\n debug?: boolean;\n};\n\nexport async function createTestClient<Schema extends SchemaDef>(\n schema: Schema,\n options?: CreateTestClientOptions<Schema>,\n schemaFile?: string,\n): Promise<ClientContract<Schema>>;\nexport async function createTestClient<Schema extends SchemaDef>(\n schema: string,\n options?: CreateTestClientOptions<Schema>,\n): Promise<any>;\nexport async function createTestClient<Schema extends SchemaDef>(\n schema: Schema | string,\n options?: CreateTestClientOptions<Schema>,\n schemaFile?: string,\n): Promise<any> {\n let workDir = options?.workDir;\n let _schema: Schema;\n const provider = options?.provider ?? getTestDbProvider() ?? 'sqlite';\n\n const dbName = options?.dbName ?? getTestDbName(provider);\n\n const dbUrl =\n provider === 'sqlite'\n ? `file:${dbName}`\n : `postgres://${TEST_PG_CONFIG.user}:${TEST_PG_CONFIG.password}@${TEST_PG_CONFIG.host}:${TEST_PG_CONFIG.port}/${dbName}`;\n\n let model: Model | undefined;\n\n if (typeof schema === 'string') {\n const generated = await generateTsSchema(schema, provider, dbUrl, options?.extraSourceFiles);\n workDir = generated.workDir;\n model = generated.model;\n // replace schema's provider\n _schema = {\n ...generated.schema,\n provider: {\n type: provider,\n },\n } as Schema;\n } else {\n // replace schema's provider\n _schema = {\n ...schema,\n provider: {\n type: provider,\n },\n };\n workDir ??= createTestProject();\n if (schemaFile) {\n let schemaContent = fs.readFileSync(schemaFile, 'utf-8');\n if (dbUrl) {\n // replace `datasource db { }` section\n schemaContent = schemaContent.replace(\n /datasource\\s+db\\s*{[^}]*}/m,\n `datasource db {\n provider = '${provider}'\n url = '${dbUrl}'\n}`,\n );\n }\n fs.writeFileSync(path.join(workDir, 'schema.zmodel'), schemaContent);\n }\n }\n\n invariant(workDir);\n if (options?.debug) {\n console.log(`Work directory: ${workDir}`);\n }\n\n const { plugins, ...rest } = options ?? {};\n const _options: ClientOptions<Schema> = {\n ...rest,\n } as ClientOptions<Schema>;\n\n if (options?.usePrismaPush) {\n invariant(typeof schema === 'string' || schemaFile, 'a schema file must be provided when using prisma db push');\n if (!model) {\n const r = await loadDocumentWithPlugins(path.join(workDir, 'schema.zmodel'));\n if (!r.success) {\n throw new Error(r.errors.join('\\n'));\n }\n model = r.model;\n }\n const prismaSchema = new PrismaSchemaGenerator(model);\n const prismaSchemaText = await prismaSchema.generate();\n fs.writeFileSync(path.resolve(workDir!, 'schema.prisma'), prismaSchemaText);\n execSync('npx prisma db push --schema ./schema.prisma --skip-generate --force-reset', {\n cwd: workDir,\n stdio: 'ignore',\n });\n } else {\n if (provider === 'postgresql') {\n invariant(dbName, 'dbName is required');\n const pgClient = new PGClient(TEST_PG_CONFIG);\n await pgClient.connect();\n await pgClient.query(`DROP DATABASE IF EXISTS \"${dbName}\"`);\n await pgClient.query(`CREATE DATABASE \"${dbName}\"`);\n await pgClient.end();\n }\n }\n\n if (provider === 'postgresql') {\n _options.dialect = new PostgresDialect({\n pool: new Pool({\n ...TEST_PG_CONFIG,\n database: dbName,\n }),\n });\n } else {\n _options.dialect = new SqliteDialect({\n database: new SQLite(path.join(workDir!, dbName)),\n });\n }\n\n let client = new ZenStackClient(_schema, _options);\n\n if (!options?.usePrismaPush) {\n await client.$pushSchema();\n }\n\n if (plugins) {\n for (const plugin of plugins) {\n client = client.$use(plugin);\n }\n }\n\n return client;\n}\n\nexport async function createPolicyTestClient<Schema extends SchemaDef>(\n schema: Schema,\n options?: CreateTestClientOptions<Schema>,\n): Promise<ClientContract<Schema>>;\nexport async function createPolicyTestClient<Schema extends SchemaDef>(\n schema: string,\n options?: CreateTestClientOptions<Schema>,\n): Promise<any>;\nexport async function createPolicyTestClient<Schema extends SchemaDef>(\n schema: Schema | string,\n options?: CreateTestClientOptions<Schema>,\n): Promise<any> {\n return createTestClient(\n schema as any,\n {\n ...options,\n plugins: [...(options?.plugins ?? []), new PolicyPlugin()],\n } as any,\n );\n}\n\nexport function testLogger(e: LogEvent) {\n console.log(e.query.sql, e.query.parameters);\n}\n\nfunction getTestDbName(provider: string) {\n if (provider === 'sqlite') {\n return './test.db';\n }\n const testName = expect.getState().currentTestName;\n const testPath = expect.getState().testPath ?? '';\n invariant(testName);\n // digest test name\n const digest = createHash('md5')\n .update(testName + testPath)\n .digest('hex');\n // compute a database name based on test name\n return (\n 'test_' +\n testName\n .toLowerCase()\n .replace(/[^a-z0-9_]/g, '_')\n .replace(/_+/g, '_')\n .substring(0, 30) +\n digest.slice(0, 6)\n );\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport tmp from 'tmp';\n\nexport function createTestProject(zmodelContent?: string) {\n const { name: workDir } = tmp.dirSync({ unsafeCleanup: true });\n\n fs.mkdirSync(path.join(workDir, 'node_modules'));\n\n // symlink all entries from \"node_modules\"\n const nodeModules = fs.readdirSync(path.join(__dirname, '../node_modules'));\n for (const entry of nodeModules) {\n if (entry.startsWith('@zenstackhq')) {\n continue;\n }\n fs.symlinkSync(\n path.join(__dirname, '../node_modules', entry),\n path.join(workDir, 'node_modules', entry),\n 'dir',\n );\n }\n\n // in addition, symlink zenstack packages\n const zenstackPackages = ['language', 'sdk', 'runtime', 'cli'];\n fs.mkdirSync(path.join(workDir, 'node_modules/@zenstackhq'));\n for (const pkg of zenstackPackages) {\n fs.symlinkSync(\n path.join(__dirname, `../../${pkg}`),\n path.join(workDir, `node_modules/@zenstackhq/${pkg}`),\n 'dir',\n );\n }\n\n fs.writeFileSync(\n path.join(workDir, 'package.json'),\n JSON.stringify(\n {\n name: 'test',\n version: '1.0.0',\n type: 'module',\n },\n null,\n 4,\n ),\n );\n\n fs.writeFileSync(\n path.join(workDir, 'tsconfig.json'),\n JSON.stringify(\n {\n compilerOptions: {\n module: 'ESNext',\n target: 'ESNext',\n moduleResolution: 'Bundler',\n esModuleInterop: true,\n skipLibCheck: true,\n strict: true,\n },\n include: ['**/*.ts'],\n },\n null,\n 4,\n ),\n );\n\n if (zmodelContent) {\n fs.writeFileSync(path.join(workDir, 'schema.zmodel'), zmodelContent);\n }\n\n return workDir;\n}\n","import { invariant } from '@zenstackhq/common-helpers';\nimport { TsSchemaGenerator } from '@zenstackhq/sdk';\nimport type { SchemaDef } from '@zenstackhq/sdk/schema';\nimport { execSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { match } from 'ts-pattern';\nimport { expect } from 'vitest';\nimport { createTestProject } from './project';\nimport { loadDocumentWithPlugins } from './utils';\n\nfunction makePrelude(provider: 'sqlite' | 'postgresql', dbUrl?: string) {\n return match(provider)\n .with('sqlite', () => {\n return `\ndatasource db {\n provider = 'sqlite'\n url = '${dbUrl ?? 'file:./test.db'}'\n}\n`;\n })\n .with('postgresql', () => {\n return `\ndatasource db {\n provider = 'postgresql'\n url = '${dbUrl ?? 'postgres://postgres:postgres@localhost:5432/db'}'\n}\n`;\n })\n .exhaustive();\n}\n\nexport async function generateTsSchema(\n schemaText: string,\n provider: 'sqlite' | 'postgresql' = 'sqlite',\n dbUrl?: string,\n extraSourceFiles?: Record<string, string>,\n) {\n const workDir = createTestProject();\n\n const zmodelPath = path.join(workDir, 'schema.zmodel');\n const noPrelude = schemaText.includes('datasource ');\n fs.writeFileSync(zmodelPath, `${noPrelude ? '' : makePrelude(provider, dbUrl)}\\n\\n${schemaText}`);\n\n const result = await loadDocumentWithPlugins(zmodelPath);\n if (!result.success) {\n throw new Error(`Failed to load schema from ${zmodelPath}: ${result.errors}`);\n }\n\n const generator = new TsSchemaGenerator();\n await generator.generate(result.model, workDir);\n\n if (extraSourceFiles) {\n for (const [fileName, content] of Object.entries(extraSourceFiles)) {\n const filePath = path.resolve(workDir, !fileName.endsWith('.ts') ? `${fileName}.ts` : fileName);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content);\n }\n }\n\n // compile the generated TS schema\n return { ...(await compileAndLoad(workDir)), model: result.model };\n}\n\nasync function compileAndLoad(workDir: string) {\n execSync('npx tsc', {\n cwd: workDir,\n stdio: 'inherit',\n });\n\n // load the schema module\n const module = await import(path.join(workDir, 'schema.js'));\n return { workDir, schema: module.schema as SchemaDef };\n}\n\nexport function generateTsSchemaFromFile(filePath: string) {\n const schemaText = fs.readFileSync(filePath, 'utf8');\n return generateTsSchema(schemaText);\n}\n\nexport async function generateTsSchemaInPlace(schemaPath: string) {\n const workDir = path.dirname(schemaPath);\n const result = await loadDocumentWithPlugins(schemaPath);\n if (!result.success) {\n throw new Error(`Failed to load schema from ${schemaPath}: ${result.errors}`);\n }\n const generator = new TsSchemaGenerator();\n await generator.generate(result.model, workDir);\n return compileAndLoad(workDir);\n}\n\nexport async function loadSchema(schema: string, additionalSchemas?: Record<string, string>) {\n if (!schema.includes('datasource ')) {\n schema = `${makePrelude('sqlite')}\\n\\n${schema}`;\n }\n\n // create a temp folder\n const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'zenstack-schema'));\n\n // create a temp file\n const tempFile = path.join(tempDir, `schema.zmodel`);\n fs.writeFileSync(tempFile, schema);\n\n if (additionalSchemas) {\n for (const [fileName, content] of Object.entries(additionalSchemas)) {\n let name = fileName;\n if (!name.endsWith('.zmodel')) {\n name += '.zmodel';\n }\n const filePath = path.join(tempDir, name);\n fs.writeFileSync(filePath, content);\n }\n }\n\n const r = await loadDocumentWithPlugins(tempFile);\n expect(r).toSatisfy(\n (r) => r.success,\n `Failed to load schema: ${(r as any).errors?.map((e: any) => e.toString()).join(', ')}`,\n );\n invariant(r.success);\n return r.model;\n}\n\nexport async function loadSchemaWithError(schema: string, error: string | RegExp) {\n if (!schema.includes('datasource ')) {\n schema = `${makePrelude('sqlite')}\\n\\n${schema}`;\n }\n\n // create a temp file\n const tempFile = path.join(os.tmpdir(), `zenstack-schema-${crypto.randomUUID()}.zmodel`);\n fs.writeFileSync(tempFile, schema);\n const r = await loadDocumentWithPlugins(tempFile);\n expect(r.success).toBe(false);\n invariant(!r.success);\n if (typeof error === 'string') {\n expect(r).toSatisfy(\n (r) => r.errors.some((e: any) => e.toString().toLowerCase().includes(error.toLowerCase())),\n `Expected error message to include \"${error}\" but got: ${r.errors.map((e: any) => e.toString()).join(', ')}`,\n );\n } else {\n expect(r).toSatisfy(\n (r) => r.errors.some((e: any) => error.test(e)),\n `Expected error message to match \"${error}\" but got: ${r.errors.map((e: any) => e.toString()).join(', ')}`,\n );\n }\n}\n","import { loadDocument } from '@zenstackhq/language';\n\nexport function loadDocumentWithPlugins(filePath: string) {\n const pluginModelFiles = [require.resolve('@zenstackhq/plugin-policy/plugin.zmodel')];\n return loadDocument(filePath, pluginModelFiles);\n}\n","import { InputValidationError, NotFoundError, RejectedByPolicyError } from '@zenstackhq/runtime';\nimport { expect } from 'vitest';\n\nfunction isPromise(value: any) {\n return typeof value.then === 'function' && typeof value.catch === 'function';\n}\n\nfunction expectError(err: any, errorType: any) {\n if (err instanceof errorType) {\n return {\n message: () => '',\n pass: true,\n };\n } else {\n return {\n message: () => `expected ${errorType}, got ${err}`,\n pass: false,\n };\n }\n}\n\nfunction expectErrorMessages(expectedMessages: string[], message: string) {\n for (const m of expectedMessages) {\n if (!message.includes(m)) {\n return {\n message: () => `expected message not found in error: ${m}, got message: ${message}`,\n pass: false,\n };\n }\n }\n return undefined;\n}\n\nexpect.extend({\n async toResolveTruthy(received: Promise<unknown>) {\n if (!isPromise(received)) {\n return { message: () => 'a promise is expected', pass: false };\n }\n const r = await received;\n return {\n pass: !!r,\n message: () => `Expected promise to resolve to a truthy value, but got ${r}`,\n };\n },\n\n async toResolveFalsy(received: Promise<unknown>) {\n if (!isPromise(received)) {\n return { message: () => 'a promise is expected', pass: false };\n }\n const r = await received;\n return {\n pass: !r,\n message: () => `Expected promise to resolve to a falsy value, but got ${r}`,\n };\n },\n\n async toResolveNull(received: Promise<unknown>) {\n if (!isPromise(received)) {\n return { message: () => 'a promise is expected', pass: false };\n }\n const r = await received;\n return {\n pass: r === null,\n message: () => `Expected promise to resolve to a null value, but got ${r}`,\n };\n },\n\n async toResolveWithLength(received: Promise<unknown>, length: number) {\n const r = await received;\n return {\n pass: Array.isArray(r) && r.length === length,\n message: () => `Expected promise to resolve with an array with length ${length}, but got ${r}`,\n };\n },\n\n async toBeRejectedNotFound(received: Promise<unknown>) {\n if (!isPromise(received)) {\n return { message: () => 'a promise is expected', pass: false };\n }\n try {\n await received;\n } catch (err) {\n return expectError(err, NotFoundError);\n }\n return {\n message: () => `expected NotFoundError, got no error`,\n pass: false,\n };\n },\n\n async toBeRejectedByPolicy(received: Promise<unknown>, expectedMessages?: string[]) {\n if (!isPromise(received)) {\n return { message: () => 'a promise is expected', pass: false };\n }\n try {\n await received;\n } catch (err) {\n if (expectedMessages && err instanceof RejectedByPolicyError) {\n const r = expectErrorMessages(expectedMessages, err.message || '');\n if (r) {\n return r;\n }\n }\n return expectError(err, RejectedByPolicyError);\n }\n return {\n message: () => `expected PolicyError, got no error`,\n pass: false,\n };\n },\n\n async toBeRejectedByValidation(received: Promise<unknown>, expectedMessages?: string[]) {\n if (!isPromise(received)) {\n return { message: () => 'a promise is expected', pass: false };\n }\n try {\n await received;\n } catch (err) {\n if (expectedMessages && err instanceof InputValidationError) {\n const r = expectErrorMessages(expectedMessages, err.message || '');\n if (r) {\n return r;\n }\n }\n return expectError(err, InputValidationError);\n }\n return {\n message: () => `expected InputValidationError, got no error`,\n pass: false,\n };\n },\n});\n"],"mappings":"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","names":["invariant","PolicyPlugin","ZenStackClient","PrismaSchemaGenerator","SQLite","PostgresDialect","SqliteDialect","execSync","createHash","fs","path","Client","PGClient","Pool","expect","fs","path","tmp","createTestProject","zmodelContent","name","workDir","tmp","dirSync","unsafeCleanup","fs","mkdirSync","path","join","nodeModules","readdirSync","__dirname","entry","startsWith","symlinkSync","zenstackPackages","pkg","writeFileSync","JSON","stringify","version","type","compilerOptions","module","target","moduleResolution","esModuleInterop","skipLibCheck","strict","include","invariant","TsSchemaGenerator","execSync","crypto","fs","os","path","match","expect","loadDocument","loadDocumentWithPlugins","filePath","pluginModelFiles","require","resolve","loadDocument","makePrelude","provider","dbUrl","match","with","exhaustive","generateTsSchema","schemaText","extraSourceFiles","workDir","createTestProject","zmodelPath","path","join","noPrelude","includes","fs","writeFileSync","result","loadDocumentWithPlugins","success","Error","errors","generator","TsSchemaGenerator","generate","model","fileName","content","Object","entries","filePath","resolve","endsWith","mkdirSync","dirname","recursive","compileAndLoad","execSync","cwd","stdio","module","schema","generateTsSchemaFromFile","readFileSync","generateTsSchemaInPlace","schemaPath","loadSchema","additionalSchemas","tempDir","mkdtempSync","os","tmpdir","tempFile","name","r","expect","toSatisfy","map","e","toString","invariant","loadSchemaWithError","error","crypto","randomUUID","toBe","some","toLowerCase","test","getTestDbProvider","val","process","env","includes","Error","TEST_PG_CONFIG","host","port","parseInt","user","password","createTestClient","schema","options","schemaFile","workDir","_schema","provider","dbName","getTestDbName","dbUrl","model","generated","generateTsSchema","extraSourceFiles","type","createTestProject","schemaContent","fs","readFileSync","replace","writeFileSync","path","join","invariant","debug","console","log","plugins","rest","_options","usePrismaPush","r","loadDocumentWithPlugins","success","errors","prismaSchema","PrismaSchemaGenerator","prismaSchemaText","generate","resolve","execSync","cwd","stdio","pgClient","PGClient","connect","query","end","dialect","PostgresDialect","pool","Pool","database","SqliteDialect","SQLite","client","ZenStackClient","$pushSchema","plugin","$use","createPolicyTestClient","PolicyPlugin","testLogger","e","sql","parameters","testName","expect","getState","currentTestName","testPath","digest","createHash","update","toLowerCase","substring","slice","InputValidationError","NotFoundError","RejectedByPolicyError","expect","isPromise","value","then","catch","expectError","err","errorType","message","pass","expectErrorMessages","expectedMessages","m","includes","undefined","expect","extend","toResolveTruthy","received","r","toResolveFalsy","toResolveNull","toResolveWithLength","length","Array","isArray","toBeRejectedNotFound","NotFoundError","toBeRejectedByPolicy","RejectedByPolicyError","toBeRejectedByValidation","InputValidationError"]}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import 'vitest';
|
|
2
|
+
|
|
3
|
+
interface CustomMatchers<R = unknown> {
|
|
4
|
+
toResolveTruthy: () => Promise<R>;
|
|
5
|
+
toResolveFalsy: () => Promise<R>;
|
|
6
|
+
toResolveNull: () => Promise<R>;
|
|
7
|
+
toResolveWithLength: (length: number) => Promise<R>;
|
|
8
|
+
toBeRejectedNotFound: () => Promise<R>;
|
|
9
|
+
toBeRejectedByPolicy: (expectedMessages?: string[]) => Promise<R>;
|
|
10
|
+
toBeRejectedByValidation: (expectedMessages?: string[]) => Promise<R>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare module 'vitest' {
|
|
14
|
+
interface Assertion<T = any> extends CustomMatchers<T> {}
|
|
15
|
+
interface AsymmetricMatchersContaining extends CustomMatchers {}
|
|
16
|
+
}
|