From 95f82dd2b1796baca613ae712a6ca1acb65fb28a Mon Sep 17 00:00:00 2001
From: ymc9 <104139426+ymc9@users.noreply.github.com>
Date: Wed, 11 Oct 2023 15:55:50 -0700
Subject: [PATCH 1/2] fix: support string literal keys for object expressions
in ZModel
---
packages/language/src/generated/ast.ts | 2 +-
packages/language/src/generated/grammar.ts | 22 +++++++---
packages/language/src/zmodel.langium | 2 +-
packages/schema/package.json | 1 +
.../tests/generator/expression-writer.test.ts | 2 +-
.../tests/generator/prisma-generator.test.ts | 2 +-
.../tests/generator/zmodel-generator.test.ts | 2 +-
packages/schema/tests/schema/abstract.test.ts | 2 +-
packages/schema/tests/schema/cal-com.test.ts | 2 +-
packages/schema/tests/schema/parser.test.ts | 2 +-
.../schema/tests/schema/sample-todo.test.ts | 2 +-
packages/schema/tests/schema/stdlib.test.ts | 2 +-
.../schema/tests/schema/trigger-dev.test.ts | 2 +-
.../validation/attribute-validation.test.ts | 2 +-
.../validation/datamodel-validation.test.ts | 2 +-
.../validation/datasource-validation.test.ts | 2 +-
.../schema/validation/enum-validation.test.ts | 2 +-
.../validation/schema-validation.test.ts | 2 +-
packages/sdk/src/utils.ts | 13 ++++--
packages/testtools/package.json | 2 +
packages/testtools/src/index.ts | 3 +-
.../tests/utils.ts => testtools/src/model.ts} | 12 +++---
pnpm-lock.yaml | 9 +++++
.../tests/regression/issue-744.test.ts | 40 +++++++++++++++++++
24 files changed, 104 insertions(+), 30 deletions(-)
rename packages/{schema/tests/utils.ts => testtools/src/model.ts} (86%)
create mode 100644 tests/integration/tests/regression/issue-744.test.ts
diff --git a/packages/language/src/generated/ast.ts b/packages/language/src/generated/ast.ts
index d5f462277..1f4b25b84 100644
--- a/packages/language/src/generated/ast.ts
+++ b/packages/language/src/generated/ast.ts
@@ -386,7 +386,7 @@ export function isEnumField(item: unknown): item is EnumField {
export interface FieldInitializer extends AstNode {
readonly $container: ObjectExpr;
readonly $type: 'FieldInitializer';
- name: RegularID
+ name: RegularID | string
value: Expression
}
diff --git a/packages/language/src/generated/grammar.ts b/packages/language/src/generated/grammar.ts
index 7bdf53b6a..8cad3d2cf 100644
--- a/packages/language/src/generated/grammar.ts
+++ b/packages/language/src/generated/grammar.ts
@@ -1171,11 +1171,23 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel
"feature": "name",
"operator": "=",
"terminal": {
- "$type": "RuleCall",
- "rule": {
- "$ref": "#/rules@47"
- },
- "arguments": []
+ "$type": "Alternatives",
+ "elements": [
+ {
+ "$type": "RuleCall",
+ "rule": {
+ "$ref": "#/rules@47"
+ },
+ "arguments": []
+ },
+ {
+ "$type": "RuleCall",
+ "rule": {
+ "$ref": "#/rules@67"
+ },
+ "arguments": []
+ }
+ ]
}
},
{
diff --git a/packages/language/src/zmodel.langium b/packages/language/src/zmodel.langium
index 5f1aa5008..47dfa1866 100644
--- a/packages/language/src/zmodel.langium
+++ b/packages/language/src/zmodel.langium
@@ -90,7 +90,7 @@ ObjectExpr:
'}';
FieldInitializer:
- name=RegularID ':' value=(Expression);
+ name=(RegularID | STRING) ':' value=(Expression);
InvocationExpr:
function=[FunctionDecl] '(' ArgumentList? ')';
diff --git a/packages/schema/package.json b/packages/schema/package.json
index 3b35633c5..486c158cd 100644
--- a/packages/schema/package.json
+++ b/packages/schema/package.json
@@ -126,6 +126,7 @@
"@typescript-eslint/parser": "^5.42.0",
"@vscode/vsce": "^2.19.0",
"@zenstackhq/runtime": "workspace:*",
+ "@zenstackhq/testtools": "workspace:*",
"concurrently": "^7.4.0",
"copyfiles": "^2.4.1",
"dotenv": "^16.0.3",
diff --git a/packages/schema/tests/generator/expression-writer.test.ts b/packages/schema/tests/generator/expression-writer.test.ts
index d4a5fe5db..5c905d904 100644
--- a/packages/schema/tests/generator/expression-writer.test.ts
+++ b/packages/schema/tests/generator/expression-writer.test.ts
@@ -4,7 +4,7 @@ import { DataModel, Enum, Expression, isDataModel, isEnum } from '@zenstackhq/la
import * as tmp from 'tmp';
import { Project, VariableDeclarationKind } from 'ts-morph';
import { ExpressionWriter } from '../../src/plugins/access-policy/expression-writer';
-import { loadModel } from '../utils';
+import { loadModel } from '@zenstackhq/testtools';
describe('Expression Writer Tests', () => {
it('boolean literal', async () => {
diff --git a/packages/schema/tests/generator/prisma-generator.test.ts b/packages/schema/tests/generator/prisma-generator.test.ts
index 8ba127842..c1d835245 100644
--- a/packages/schema/tests/generator/prisma-generator.test.ts
+++ b/packages/schema/tests/generator/prisma-generator.test.ts
@@ -6,7 +6,7 @@ import path from 'path';
import tmp from 'tmp';
import { loadDocument } from '../../src/cli/cli-util';
import PrismaSchemaGenerator from '../../src/plugins/prisma/schema-generator';
-import { loadModel } from '../utils';
+import { loadModel } from '@zenstackhq/testtools';
describe('Prisma generator test', () => {
it('datasource coverage', async () => {
diff --git a/packages/schema/tests/generator/zmodel-generator.test.ts b/packages/schema/tests/generator/zmodel-generator.test.ts
index 91ddacca2..9240ae741 100644
--- a/packages/schema/tests/generator/zmodel-generator.test.ts
+++ b/packages/schema/tests/generator/zmodel-generator.test.ts
@@ -1,4 +1,4 @@
-import { loadModel } from '../utils';
+import { loadModel } from '@zenstackhq/testtools';
import ZModelCodeGenerator from '../../src/plugins/prisma/zmodel-code-generator';
import { DataModel, DataModelAttribute, DataModelFieldAttribute } from '@zenstackhq/language/ast';
diff --git a/packages/schema/tests/schema/abstract.test.ts b/packages/schema/tests/schema/abstract.test.ts
index 621e7998a..6681a1e58 100644
--- a/packages/schema/tests/schema/abstract.test.ts
+++ b/packages/schema/tests/schema/abstract.test.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
-import { loadModel } from '../utils';
+import { loadModel } from '@zenstackhq/testtools';
describe('Abstract Schema Tests', () => {
it('model loading', async () => {
diff --git a/packages/schema/tests/schema/cal-com.test.ts b/packages/schema/tests/schema/cal-com.test.ts
index 05da241b9..9bf58e64b 100644
--- a/packages/schema/tests/schema/cal-com.test.ts
+++ b/packages/schema/tests/schema/cal-com.test.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
-import { loadModel } from '../utils';
+import { loadModel } from '@zenstackhq/testtools';
describe('Cal.com Schema Tests', () => {
it('model loading', async () => {
diff --git a/packages/schema/tests/schema/parser.test.ts b/packages/schema/tests/schema/parser.test.ts
index 9b4150cd5..99a1753f3 100644
--- a/packages/schema/tests/schema/parser.test.ts
+++ b/packages/schema/tests/schema/parser.test.ts
@@ -20,7 +20,7 @@ import {
StringLiteral,
UnaryExpr,
} from '@zenstackhq/language/ast';
-import { loadModel } from '../utils';
+import { loadModel } from '@zenstackhq/testtools';
describe('Parsing Tests', () => {
it('data source', async () => {
diff --git a/packages/schema/tests/schema/sample-todo.test.ts b/packages/schema/tests/schema/sample-todo.test.ts
index 40387604c..1f4eaefbe 100644
--- a/packages/schema/tests/schema/sample-todo.test.ts
+++ b/packages/schema/tests/schema/sample-todo.test.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
-import { loadModel } from '../utils';
+import { loadModel } from '@zenstackhq/testtools';
describe('Sample Todo Schema Tests', () => {
it('model loading', async () => {
diff --git a/packages/schema/tests/schema/stdlib.test.ts b/packages/schema/tests/schema/stdlib.test.ts
index e4fa5b966..c34accd11 100644
--- a/packages/schema/tests/schema/stdlib.test.ts
+++ b/packages/schema/tests/schema/stdlib.test.ts
@@ -1,8 +1,8 @@
+import { SchemaLoadingError } from '@zenstackhq/testtools';
import { NodeFileSystem } from 'langium/node';
import path from 'path';
import { URI } from 'vscode-uri';
import { createZModelServices } from '../../src/language-server/zmodel-module';
-import { SchemaLoadingError } from '../utils';
describe('Stdlib Tests', () => {
it('stdlib', async () => {
diff --git a/packages/schema/tests/schema/trigger-dev.test.ts b/packages/schema/tests/schema/trigger-dev.test.ts
index c712ad25d..599ec7a4f 100644
--- a/packages/schema/tests/schema/trigger-dev.test.ts
+++ b/packages/schema/tests/schema/trigger-dev.test.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
-import { loadModel } from '../utils';
+import { loadModel } from '@zenstackhq/testtools';
describe('Trigger.dev Schema Tests', () => {
it('model loading', async () => {
diff --git a/packages/schema/tests/schema/validation/attribute-validation.test.ts b/packages/schema/tests/schema/validation/attribute-validation.test.ts
index 5c638a841..51af8f460 100644
--- a/packages/schema/tests/schema/validation/attribute-validation.test.ts
+++ b/packages/schema/tests/schema/validation/attribute-validation.test.ts
@@ -1,6 +1,6 @@
///
-import { loadModel, loadModelWithError } from '../../utils';
+import { loadModel, loadModelWithError } from '@zenstackhq/testtools';
describe('Attribute tests', () => {
const prelude = `
diff --git a/packages/schema/tests/schema/validation/datamodel-validation.test.ts b/packages/schema/tests/schema/validation/datamodel-validation.test.ts
index a2d68f2c0..bcfc4f54a 100644
--- a/packages/schema/tests/schema/validation/datamodel-validation.test.ts
+++ b/packages/schema/tests/schema/validation/datamodel-validation.test.ts
@@ -1,4 +1,4 @@
-import { loadModel, loadModelWithError } from '../../utils';
+import { loadModel, loadModelWithError } from '@zenstackhq/testtools';
describe('Data Model Validation Tests', () => {
const prelude = `
diff --git a/packages/schema/tests/schema/validation/datasource-validation.test.ts b/packages/schema/tests/schema/validation/datasource-validation.test.ts
index 19be1f076..a6596b7cd 100644
--- a/packages/schema/tests/schema/validation/datasource-validation.test.ts
+++ b/packages/schema/tests/schema/validation/datasource-validation.test.ts
@@ -1,4 +1,4 @@
-import { loadModel, loadModelWithError } from '../../utils';
+import { loadModel, loadModelWithError } from '@zenstackhq/testtools';
describe('Datasource Validation Tests', () => {
it('missing fields', async () => {
diff --git a/packages/schema/tests/schema/validation/enum-validation.test.ts b/packages/schema/tests/schema/validation/enum-validation.test.ts
index fc31a0c92..c20d386d5 100644
--- a/packages/schema/tests/schema/validation/enum-validation.test.ts
+++ b/packages/schema/tests/schema/validation/enum-validation.test.ts
@@ -1,4 +1,4 @@
-import { loadModelWithError } from '../../utils';
+import { loadModelWithError } from '@zenstackhq/testtools';
describe('Enum Validation Tests', () => {
const prelude = `
diff --git a/packages/schema/tests/schema/validation/schema-validation.test.ts b/packages/schema/tests/schema/validation/schema-validation.test.ts
index c7b000338..9e90b28ec 100644
--- a/packages/schema/tests/schema/validation/schema-validation.test.ts
+++ b/packages/schema/tests/schema/validation/schema-validation.test.ts
@@ -1,4 +1,4 @@
-import { loadModelWithError } from '../../utils';
+import { loadModelWithError } from '@zenstackhq/testtools';
describe('Toplevel Schema Validation Tests', () => {
it('too many datasources', async () => {
diff --git a/packages/sdk/src/utils.ts b/packages/sdk/src/utils.ts
index 80366480a..f10ae7b1e 100644
--- a/packages/sdk/src/utils.ts
+++ b/packages/sdk/src/utils.ts
@@ -49,10 +49,17 @@ export function resolved(ref: Reference): T {
export function getLiteral(
expr: Expression | ConfigExpr | undefined
): T | undefined {
- if (!isLiteralExpr(expr)) {
- return getObjectLiteral(expr);
+ switch (expr?.$type) {
+ case 'ObjectExpr':
+ return getObjectLiteral(expr);
+ case 'StringLiteral':
+ case 'BooleanLiteral':
+ return expr.value as T;
+ case 'NumberLiteral':
+ return parseFloat(expr.value) as T;
+ default:
+ return undefined;
}
- return expr.value as T;
}
export function getArray(expr: Expression | ConfigExpr | undefined) {
diff --git a/packages/testtools/package.json b/packages/testtools/package.json
index 4e9e5118f..9624ef461 100644
--- a/packages/testtools/package.json
+++ b/packages/testtools/package.json
@@ -24,8 +24,10 @@
"@zenstackhq/runtime": "workspace:*",
"@zenstackhq/sdk": "workspace:*",
"json5": "^2.2.3",
+ "langium": "1.2.0",
"pg": "^8.11.1",
"tmp": "^0.2.1",
+ "vscode-uri": "^3.0.6",
"zenstack": "workspace:*"
},
"devDependencies": {
diff --git a/packages/testtools/src/index.ts b/packages/testtools/src/index.ts
index 3c1996686..488498b2f 100644
--- a/packages/testtools/src/index.ts
+++ b/packages/testtools/src/index.ts
@@ -1,2 +1,3 @@
-export * from './schema';
export * from './db';
+export * from './model';
+export * from './schema';
diff --git a/packages/schema/tests/utils.ts b/packages/testtools/src/model.ts
similarity index 86%
rename from packages/schema/tests/utils.ts
rename to packages/testtools/src/model.ts
index f362b4019..4be8a1613 100644
--- a/packages/schema/tests/utils.ts
+++ b/packages/testtools/src/model.ts
@@ -1,11 +1,11 @@
-import { Model } from '@zenstackhq/language/ast';
+import { Model } from '@zenstackhq/sdk/ast';
import * as fs from 'fs';
import { NodeFileSystem } from 'langium/node';
import * as path from 'path';
import * as tmp from 'tmp';
import { URI } from 'vscode-uri';
-import { createZModelServices } from '../src/language-server/zmodel-module';
-import { mergeBaseModel } from '../src/utils/ast-utils';
+import { createZModelServices } from 'zenstack/language-server/zmodel-module';
+import { mergeBaseModel } from 'zenstack/utils/ast-utils';
export class SchemaLoadingError extends Error {
constructor(public readonly errors: string[]) {
@@ -18,7 +18,7 @@ export async function loadModel(content: string, validate = true, verbose = true
fs.writeFileSync(docPath, content);
const { shared } = createZModelServices(NodeFileSystem);
const stdLib = shared.workspace.LangiumDocuments.getOrCreateDocument(
- URI.file(path.resolve('src/res/stdlib.zmodel'))
+ URI.file(path.resolve(__dirname, '../../schema/src/res/stdlib.zmodel'))
);
const doc = shared.workspace.LangiumDocuments.getOrCreateDocument(URI.file(docPath));
@@ -60,7 +60,9 @@ export async function loadModelWithError(content: string, verbose = false) {
try {
await loadModel(content, true, verbose);
} catch (err) {
- expect(err).toBeInstanceOf(SchemaLoadingError);
+ if (!(err instanceof SchemaLoadingError)) {
+ throw err;
+ }
return (err as SchemaLoadingError).errors;
}
throw new Error('No error is thrown');
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 09952fa02..ad62571c9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -567,6 +567,9 @@ importers:
'@zenstackhq/runtime':
specifier: workspace:*
version: link:../runtime/dist
+ '@zenstackhq/testtools':
+ specifier: workspace:*
+ version: link:../testtools/dist
concurrently:
specifier: ^7.4.0
version: 7.4.0
@@ -779,12 +782,18 @@ importers:
json5:
specifier: ^2.2.3
version: 2.2.3
+ langium:
+ specifier: 1.2.0
+ version: 1.2.0
pg:
specifier: ^8.11.1
version: 8.11.1
tmp:
specifier: ^0.2.1
version: 0.2.1
+ vscode-uri:
+ specifier: ^3.0.6
+ version: 3.0.7
zenstack:
specifier: workspace:*
version: link:../schema/dist
diff --git a/tests/integration/tests/regression/issue-744.test.ts b/tests/integration/tests/regression/issue-744.test.ts
new file mode 100644
index 000000000..d46d110ec
--- /dev/null
+++ b/tests/integration/tests/regression/issue-744.test.ts
@@ -0,0 +1,40 @@
+import { getObjectLiteral } from '@zenstackhq/sdk';
+import { Plugin, PluginField, isPlugin } from '@zenstackhq/sdk/ast';
+import { loadModel } from '@zenstackhq/testtools';
+
+describe('Regression: issue 744', () => {
+ it('regression', async () => {
+ const model = await loadModel(
+ `
+ generator client {
+ provider = "prisma-client-js"
+ }
+
+ datasource db {
+ provider = "postgresql"
+ url = env("DATABASE_URL")
+ }
+
+ plugin zod {
+ provider = '@core/zod'
+ settings = {
+ '200': { status: 'ok' },
+ 'x-y-z': 200,
+ foo: 'bar'
+ }
+ }
+
+ model Foo {
+ id String @id @default(cuid())
+ }
+ `
+ );
+
+ const plugin = model.declarations.find((d): d is Plugin => isPlugin(d));
+ const settings = plugin?.fields.find((f): f is PluginField => f.name === 'settings');
+ const value: any = getObjectLiteral(settings?.value);
+ expect(value['200']).toMatchObject({ status: 'ok' });
+ expect(value['x-y-z']).toBe(200);
+ expect(value.foo).toBe('bar');
+ });
+});
From 9774fab1133780390456d8899962f64079ad660a Mon Sep 17 00:00:00 2001
From: ymc9 <104139426+ymc9@users.noreply.github.com>
Date: Wed, 11 Oct 2023 16:29:22 -0700
Subject: [PATCH 2/2] fix circular dependency
---
packages/schema/package.json | 1 -
.../tests/generator/expression-writer.test.ts | 2 +-
.../tests/generator/prisma-generator.test.ts | 2 +-
.../tests/generator/zmodel-generator.test.ts | 2 +-
packages/schema/tests/schema/abstract.test.ts | 2 +-
packages/schema/tests/schema/cal-com.test.ts | 2 +-
packages/schema/tests/schema/parser.test.ts | 2 +-
.../schema/tests/schema/sample-todo.test.ts | 2 +-
packages/schema/tests/schema/stdlib.test.ts | 2 +-
.../schema/tests/schema/trigger-dev.test.ts | 2 +-
.../validation/attribute-validation.test.ts | 2 +-
.../validation/datamodel-validation.test.ts | 2 +-
.../validation/datasource-validation.test.ts | 2 +-
.../schema/validation/enum-validation.test.ts | 2 +-
.../validation/schema-validation.test.ts | 2 +-
packages/schema/tests/utils.ts | 69 +++++++++++++++++++
pnpm-lock.yaml | 46 +++++++++++--
17 files changed, 122 insertions(+), 22 deletions(-)
create mode 100644 packages/schema/tests/utils.ts
diff --git a/packages/schema/package.json b/packages/schema/package.json
index 486c158cd..3b35633c5 100644
--- a/packages/schema/package.json
+++ b/packages/schema/package.json
@@ -126,7 +126,6 @@
"@typescript-eslint/parser": "^5.42.0",
"@vscode/vsce": "^2.19.0",
"@zenstackhq/runtime": "workspace:*",
- "@zenstackhq/testtools": "workspace:*",
"concurrently": "^7.4.0",
"copyfiles": "^2.4.1",
"dotenv": "^16.0.3",
diff --git a/packages/schema/tests/generator/expression-writer.test.ts b/packages/schema/tests/generator/expression-writer.test.ts
index 5c905d904..d4a5fe5db 100644
--- a/packages/schema/tests/generator/expression-writer.test.ts
+++ b/packages/schema/tests/generator/expression-writer.test.ts
@@ -4,7 +4,7 @@ import { DataModel, Enum, Expression, isDataModel, isEnum } from '@zenstackhq/la
import * as tmp from 'tmp';
import { Project, VariableDeclarationKind } from 'ts-morph';
import { ExpressionWriter } from '../../src/plugins/access-policy/expression-writer';
-import { loadModel } from '@zenstackhq/testtools';
+import { loadModel } from '../utils';
describe('Expression Writer Tests', () => {
it('boolean literal', async () => {
diff --git a/packages/schema/tests/generator/prisma-generator.test.ts b/packages/schema/tests/generator/prisma-generator.test.ts
index c1d835245..8ba127842 100644
--- a/packages/schema/tests/generator/prisma-generator.test.ts
+++ b/packages/schema/tests/generator/prisma-generator.test.ts
@@ -6,7 +6,7 @@ import path from 'path';
import tmp from 'tmp';
import { loadDocument } from '../../src/cli/cli-util';
import PrismaSchemaGenerator from '../../src/plugins/prisma/schema-generator';
-import { loadModel } from '@zenstackhq/testtools';
+import { loadModel } from '../utils';
describe('Prisma generator test', () => {
it('datasource coverage', async () => {
diff --git a/packages/schema/tests/generator/zmodel-generator.test.ts b/packages/schema/tests/generator/zmodel-generator.test.ts
index 9240ae741..91ddacca2 100644
--- a/packages/schema/tests/generator/zmodel-generator.test.ts
+++ b/packages/schema/tests/generator/zmodel-generator.test.ts
@@ -1,4 +1,4 @@
-import { loadModel } from '@zenstackhq/testtools';
+import { loadModel } from '../utils';
import ZModelCodeGenerator from '../../src/plugins/prisma/zmodel-code-generator';
import { DataModel, DataModelAttribute, DataModelFieldAttribute } from '@zenstackhq/language/ast';
diff --git a/packages/schema/tests/schema/abstract.test.ts b/packages/schema/tests/schema/abstract.test.ts
index 6681a1e58..621e7998a 100644
--- a/packages/schema/tests/schema/abstract.test.ts
+++ b/packages/schema/tests/schema/abstract.test.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
-import { loadModel } from '@zenstackhq/testtools';
+import { loadModel } from '../utils';
describe('Abstract Schema Tests', () => {
it('model loading', async () => {
diff --git a/packages/schema/tests/schema/cal-com.test.ts b/packages/schema/tests/schema/cal-com.test.ts
index 9bf58e64b..05da241b9 100644
--- a/packages/schema/tests/schema/cal-com.test.ts
+++ b/packages/schema/tests/schema/cal-com.test.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
-import { loadModel } from '@zenstackhq/testtools';
+import { loadModel } from '../utils';
describe('Cal.com Schema Tests', () => {
it('model loading', async () => {
diff --git a/packages/schema/tests/schema/parser.test.ts b/packages/schema/tests/schema/parser.test.ts
index 99a1753f3..9b4150cd5 100644
--- a/packages/schema/tests/schema/parser.test.ts
+++ b/packages/schema/tests/schema/parser.test.ts
@@ -20,7 +20,7 @@ import {
StringLiteral,
UnaryExpr,
} from '@zenstackhq/language/ast';
-import { loadModel } from '@zenstackhq/testtools';
+import { loadModel } from '../utils';
describe('Parsing Tests', () => {
it('data source', async () => {
diff --git a/packages/schema/tests/schema/sample-todo.test.ts b/packages/schema/tests/schema/sample-todo.test.ts
index 1f4eaefbe..40387604c 100644
--- a/packages/schema/tests/schema/sample-todo.test.ts
+++ b/packages/schema/tests/schema/sample-todo.test.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
-import { loadModel } from '@zenstackhq/testtools';
+import { loadModel } from '../utils';
describe('Sample Todo Schema Tests', () => {
it('model loading', async () => {
diff --git a/packages/schema/tests/schema/stdlib.test.ts b/packages/schema/tests/schema/stdlib.test.ts
index c34accd11..f4b1cc1fe 100644
--- a/packages/schema/tests/schema/stdlib.test.ts
+++ b/packages/schema/tests/schema/stdlib.test.ts
@@ -1,4 +1,4 @@
-import { SchemaLoadingError } from '@zenstackhq/testtools';
+import { SchemaLoadingError } from '../utils';
import { NodeFileSystem } from 'langium/node';
import path from 'path';
import { URI } from 'vscode-uri';
diff --git a/packages/schema/tests/schema/trigger-dev.test.ts b/packages/schema/tests/schema/trigger-dev.test.ts
index 599ec7a4f..c712ad25d 100644
--- a/packages/schema/tests/schema/trigger-dev.test.ts
+++ b/packages/schema/tests/schema/trigger-dev.test.ts
@@ -1,6 +1,6 @@
import * as fs from 'fs';
import path from 'path';
-import { loadModel } from '@zenstackhq/testtools';
+import { loadModel } from '../utils';
describe('Trigger.dev Schema Tests', () => {
it('model loading', async () => {
diff --git a/packages/schema/tests/schema/validation/attribute-validation.test.ts b/packages/schema/tests/schema/validation/attribute-validation.test.ts
index 51af8f460..5c638a841 100644
--- a/packages/schema/tests/schema/validation/attribute-validation.test.ts
+++ b/packages/schema/tests/schema/validation/attribute-validation.test.ts
@@ -1,6 +1,6 @@
///
-import { loadModel, loadModelWithError } from '@zenstackhq/testtools';
+import { loadModel, loadModelWithError } from '../../utils';
describe('Attribute tests', () => {
const prelude = `
diff --git a/packages/schema/tests/schema/validation/datamodel-validation.test.ts b/packages/schema/tests/schema/validation/datamodel-validation.test.ts
index bcfc4f54a..a2d68f2c0 100644
--- a/packages/schema/tests/schema/validation/datamodel-validation.test.ts
+++ b/packages/schema/tests/schema/validation/datamodel-validation.test.ts
@@ -1,4 +1,4 @@
-import { loadModel, loadModelWithError } from '@zenstackhq/testtools';
+import { loadModel, loadModelWithError } from '../../utils';
describe('Data Model Validation Tests', () => {
const prelude = `
diff --git a/packages/schema/tests/schema/validation/datasource-validation.test.ts b/packages/schema/tests/schema/validation/datasource-validation.test.ts
index a6596b7cd..19be1f076 100644
--- a/packages/schema/tests/schema/validation/datasource-validation.test.ts
+++ b/packages/schema/tests/schema/validation/datasource-validation.test.ts
@@ -1,4 +1,4 @@
-import { loadModel, loadModelWithError } from '@zenstackhq/testtools';
+import { loadModel, loadModelWithError } from '../../utils';
describe('Datasource Validation Tests', () => {
it('missing fields', async () => {
diff --git a/packages/schema/tests/schema/validation/enum-validation.test.ts b/packages/schema/tests/schema/validation/enum-validation.test.ts
index c20d386d5..fc31a0c92 100644
--- a/packages/schema/tests/schema/validation/enum-validation.test.ts
+++ b/packages/schema/tests/schema/validation/enum-validation.test.ts
@@ -1,4 +1,4 @@
-import { loadModelWithError } from '@zenstackhq/testtools';
+import { loadModelWithError } from '../../utils';
describe('Enum Validation Tests', () => {
const prelude = `
diff --git a/packages/schema/tests/schema/validation/schema-validation.test.ts b/packages/schema/tests/schema/validation/schema-validation.test.ts
index 9e90b28ec..c7b000338 100644
--- a/packages/schema/tests/schema/validation/schema-validation.test.ts
+++ b/packages/schema/tests/schema/validation/schema-validation.test.ts
@@ -1,4 +1,4 @@
-import { loadModelWithError } from '@zenstackhq/testtools';
+import { loadModelWithError } from '../../utils';
describe('Toplevel Schema Validation Tests', () => {
it('too many datasources', async () => {
diff --git a/packages/schema/tests/utils.ts b/packages/schema/tests/utils.ts
new file mode 100644
index 000000000..fade65628
--- /dev/null
+++ b/packages/schema/tests/utils.ts
@@ -0,0 +1,69 @@
+import { Model } from '@zenstackhq/sdk/ast';
+import * as fs from 'fs';
+import { NodeFileSystem } from 'langium/node';
+import * as path from 'path';
+import * as tmp from 'tmp';
+import { URI } from 'vscode-uri';
+import { createZModelServices } from '../src/language-server/zmodel-module';
+import { mergeBaseModel } from '../src/utils/ast-utils';
+
+export class SchemaLoadingError extends Error {
+ constructor(public readonly errors: string[]) {
+ super('Schema error:\n' + errors.join('\n'));
+ }
+}
+
+export async function loadModel(content: string, validate = true, verbose = true) {
+ const { name: docPath } = tmp.fileSync({ postfix: '.zmodel' });
+ fs.writeFileSync(docPath, content);
+ const { shared } = createZModelServices(NodeFileSystem);
+ const stdLib = shared.workspace.LangiumDocuments.getOrCreateDocument(
+ URI.file(path.resolve(__dirname, '../../schema/src/res/stdlib.zmodel'))
+ );
+ const doc = shared.workspace.LangiumDocuments.getOrCreateDocument(URI.file(docPath));
+
+ if (doc.parseResult.lexerErrors.length > 0) {
+ throw new SchemaLoadingError(doc.parseResult.lexerErrors.map((e) => e.message));
+ }
+
+ if (doc.parseResult.parserErrors.length > 0) {
+ throw new SchemaLoadingError(doc.parseResult.parserErrors.map((e) => e.message));
+ }
+
+ await shared.workspace.DocumentBuilder.build([stdLib, doc], {
+ validationChecks: validate ? 'all' : 'none',
+ });
+
+ const validationErrors = (doc.diagnostics ?? []).filter((e) => e.severity === 1);
+ if (validationErrors.length > 0) {
+ for (const validationError of validationErrors) {
+ if (verbose) {
+ const range = doc.textDocument.getText(validationError.range);
+ console.error(
+ `line ${validationError.range.start.line + 1}: ${validationError.message}${
+ range ? ' [' + range + ']' : ''
+ }`
+ );
+ }
+ }
+ throw new SchemaLoadingError(validationErrors.map((e) => e.message));
+ }
+
+ const model = (await doc.parseResult.value) as Model;
+
+ mergeBaseModel(model);
+
+ return model;
+}
+
+export async function loadModelWithError(content: string, verbose = false) {
+ try {
+ await loadModel(content, true, verbose);
+ } catch (err) {
+ if (!(err instanceof SchemaLoadingError)) {
+ throw err;
+ }
+ return (err as SchemaLoadingError).errors;
+ }
+ throw new Error('No error is thrown');
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ad62571c9..ef4f595d3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -124,7 +124,7 @@ importers:
version: 0.2.1
ts-jest:
specifier: ^29.0.5
- version: 29.0.5(@babel/core@7.22.5)(esbuild@0.18.13)(jest@29.5.0)(typescript@4.9.5)
+ version: 29.0.5(@babel/core@7.22.9)(esbuild@0.18.13)(jest@29.5.0)(typescript@4.9.5)
typescript:
specifier: ^4.9.5
version: 4.9.5
@@ -201,7 +201,7 @@ importers:
version: 2.0.3(react@18.2.0)
ts-jest:
specifier: ^29.0.5
- version: 29.0.5(@babel/core@7.22.9)(esbuild@0.18.13)(jest@29.5.0)(typescript@4.9.4)
+ version: 29.0.5(@babel/core@7.22.5)(esbuild@0.18.13)(jest@29.5.0)(typescript@4.9.4)
typescript:
specifier: ^4.9.4
version: 4.9.4
@@ -567,9 +567,6 @@ importers:
'@zenstackhq/runtime':
specifier: workspace:*
version: link:../runtime/dist
- '@zenstackhq/testtools':
- specifier: workspace:*
- version: link:../testtools/dist
concurrently:
specifier: ^7.4.0
version: 7.4.0
@@ -10634,7 +10631,7 @@ packages:
yargs-parser: 21.1.1
dev: true
- /ts-jest@29.0.5(@babel/core@7.22.5)(esbuild@0.18.13)(jest@29.5.0)(typescript@4.9.5):
+ /ts-jest@29.0.5(@babel/core@7.22.5)(esbuild@0.18.13)(jest@29.5.0)(typescript@4.9.4):
resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
@@ -10665,7 +10662,7 @@ packages:
lodash.memoize: 4.1.2
make-error: 1.3.6
semver: 7.5.3
- typescript: 4.9.5
+ typescript: 4.9.4
yargs-parser: 21.1.1
dev: true
@@ -10704,6 +10701,41 @@ packages:
yargs-parser: 21.1.1
dev: true
+ /ts-jest@29.0.5(@babel/core@7.22.9)(esbuild@0.18.13)(jest@29.5.0)(typescript@4.9.5):
+ resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ hasBin: true
+ peerDependencies:
+ '@babel/core': '>=7.0.0-beta.0 <8'
+ '@jest/types': ^29.0.0
+ babel-jest: ^29.0.0
+ esbuild: '*'
+ jest: ^29.0.0
+ typescript: '>=4.3'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ '@jest/types':
+ optional: true
+ babel-jest:
+ optional: true
+ esbuild:
+ optional: true
+ dependencies:
+ '@babel/core': 7.22.9
+ bs-logger: 0.2.6
+ esbuild: 0.18.13
+ fast-json-stable-stringify: 2.1.0
+ jest: 29.5.0(@types/node@18.0.0)(ts-node@10.9.1)
+ jest-util: 29.5.0
+ json5: 2.2.3
+ lodash.memoize: 4.1.2
+ make-error: 1.3.6
+ semver: 7.5.3
+ typescript: 4.9.5
+ yargs-parser: 21.1.1
+ dev: true
+
/ts-morph@16.0.0:
resolution: {integrity: sha512-jGNF0GVpFj0orFw55LTsQxVYEUOCWBAbR5Ls7fTYE5pQsbW18ssTb/6UXx/GYAEjS+DQTp8VoTw0vqYMiaaQuw==}
dependencies: