Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions packages/testtools/src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { invariant } from '@zenstackhq/common-helpers';
import { loadDocument } from '@zenstackhq/language';
import { TsSchemaGenerator } from '@zenstackhq/sdk';
import type { SchemaDef } from '@zenstackhq/sdk/schema';
import { execSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { match } from 'ts-pattern';
import { expect } from 'vitest';
import { createTestProject } from './project';

function makePrelude(provider: 'sqlite' | 'postgresql', dbUrl?: string) {
Expand Down Expand Up @@ -86,3 +89,44 @@ export async function generateTsSchemaInPlace(schemaPath: string) {
await generator.generate(result.model, workDir);
return compileAndLoad(workDir);
}

export async function loadSchema(schema: string) {
if (!schema.includes('datasource ')) {
schema = `${makePrelude('sqlite')}\n\n${schema}`;
}

// create a temp file
const tempFile = path.join(os.tmpdir(), `zenstack-schema-${crypto.randomUUID()}.zmodel`);
Comment thread
ymc9 marked this conversation as resolved.
fs.writeFileSync(tempFile, schema);
const r = await loadDocument(tempFile);
expect(r).toSatisfy(
(r) => r.success,
`Failed to load schema: ${(r as any).errors?.map((e: any) => e.toString()).join(', ')}`,
);
invariant(r.success);
return r.model;
Comment thread
ymc9 marked this conversation as resolved.
}

export async function loadSchemaWithError(schema: string, error: string | RegExp) {
if (!schema.includes('datasource ')) {
schema = `${makePrelude('sqlite')}\n\n${schema}`;
}

// create a temp file
const tempFile = path.join(os.tmpdir(), `zenstack-schema-${crypto.randomUUID()}.zmodel`);
fs.writeFileSync(tempFile, schema);
const r = await loadDocument(tempFile);
expect(r.success).toBe(false);
Comment thread
ymc9 marked this conversation as resolved.
invariant(!r.success);
if (typeof error === 'string') {
expect(r).toSatisfy(
(r) => r.errors.some((e: any) => e.toString().toLowerCase().includes(error.toLowerCase())),
`Expected error message to include "${error}" but got: ${r.errors.map((e: any) => e.toString()).join(', ')}`,
);
} else {
expect(r).toSatisfy(
(r) => r.errors.some((e: any) => error.test(e)),
Comment thread
ymc9 marked this conversation as resolved.
`Expected error message to match "${error}" but got: ${r.errors.map((e: any) => e.toString()).join(', ')}`,
);
}
}
25 changes: 25 additions & 0 deletions tests/regression/test/v2-migrated/issue-177.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { loadSchemaWithError } from '@zenstackhq/testtools';
import { it } from 'vitest';

it('verifies issue 177', async () => {
await loadSchemaWithError(
`
model Foo {
id String @id @default(cuid())

bar Bar @relation(fields: [barId1, barId2], references: [id1, id2])
barId1 String?
barId2 String
}

model Bar {
id1 String @default(cuid())
id2 String @default(cuid())
foos Foo[]

@@id([id1, id2])
}
`,
'relation "bar" is not optional, but field "barId1" is optional',
);
});
15 changes: 15 additions & 0 deletions tests/regression/test/v2-migrated/issue-389.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { expect, it } from 'vitest';
import { createPolicyTestClient } from '@zenstackhq/testtools';

it('verifies issue 389', async () => {
const db = await createPolicyTestClient(`
model model {
id String @id @default(uuid())
value Int
@@allow('read', true)
@@allow('create', value > 0)
}
`);
await expect(db.model.create({ data: { value: 0 } })).toBeRejectedByPolicy();
await expect(db.model.create({ data: { value: 1 } })).toResolveTruthy();
});
63 changes: 63 additions & 0 deletions tests/regression/test/v2-migrated/issue-392.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { loadDocument } from '@zenstackhq/language';
import { it } from 'vitest';

it('verifies issue 392', async () => {
await loadDocument(
`
model M1 {
m2_id String @id
m2 M2 @relation(fields: [m2_id], references: [id])
}

model M2 {
id String @id
m1 M1?
}
`,
);

await loadDocument(
`
model M1 {
id String @id
m2_id String @unique
m2 M2 @relation(fields: [m2_id], references: [id])
}

model M2 {
id String @id
m1 M1?
}
`,
);

await loadDocument(
`
model M1 {
m2_id String
m2 M2 @relation(fields: [m2_id], references: [id])
@@id([m2_id])
}

model M2 {
id String @id
m1 M1?
}
`,
);

await loadDocument(
`
model M1 {
m2_id String
m2 M2 @relation(fields: [m2_id], references: [id])
@@unique([m2_id])
}

model M2 {
id String @id
m1 M1?
}
`,
);
});
20 changes: 20 additions & 0 deletions tests/regression/test/v2-migrated/issue-416.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { loadSchema } from '@zenstackhq/testtools';
import { it } from 'vitest';

it('verifies issue 416', async () => {
await loadSchema(
`
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model Example {
id Int @id
doubleQuote String @default("s\\"1")
singleQuote String @default('s\\'1')
json Json @default("{\\"theme\\": \\"light\\", \\"consoleDrawer\\": false}")
}
`,
);
});
29 changes: 29 additions & 0 deletions tests/regression/test/v2-migrated/issue-509.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { loadDocument } from '@zenstackhq/language';
import { it } from 'vitest';

it('verifies issue 509', async () => {
await loadDocument(
`
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}

model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?

deleted Boolean @default(false) @omit

@@allow('all', true)
@@deny('read', deleted)
}
`,
);
});
59 changes: 59 additions & 0 deletions tests/regression/test/v2-migrated/issue-609.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { createPolicyTestClient } from '@zenstackhq/testtools';
import { expect, it } from 'vitest';

it('verifies issue 609', async () => {
const db = await createPolicyTestClient(
`
model User {
id String @id @default(cuid())
comments Comment[]
}

model Comment {
id String @id @default(cuid())
parentCommentId String?
replies Comment[] @relation("CommentToComment")
parent Comment? @relation("CommentToComment", fields: [parentCommentId], references: [id])
comment String
author User @relation(fields: [authorId], references: [id])
authorId String

@@allow('read,create', true)
@@allow('update,delete', auth() == author)
}
`,
{ usePrismaPush: true },
);

const rawDb = db.$unuseAll();

await rawDb.user.create({
data: {
id: '1',
comments: {
create: {
id: '1',
comment: 'Comment 1',
},
},
},
});

await rawDb.user.create({
data: {
id: '2',
},
});

// connecting a child comment from a different user to a parent comment should succeed
const dbAuth = db.$setAuth({ id: '2' });
await expect(
dbAuth.comment.create({
data: {
comment: 'Comment 2',
author: { connect: { id: '2' } },
parent: { connect: { id: '1' } },
},
}),
).toResolveTruthy();
});
25 changes: 25 additions & 0 deletions tests/regression/test/v2-migrated/issue-632.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createTestClient } from '@zenstackhq/testtools';
import { it } from 'vitest';

it('verifies issue 632', async () => {
await createTestClient(
`
enum InventoryUnit {
DIGITAL
FL_OZ
GRAMS
MILLILITERS
OUNCES
UNIT
UNLIMITED
}

model TwoEnumsOneModelTest {
id String @id @default(cuid())
inventoryUnit InventoryUnit @default(UNIT)
inputUnit InventoryUnit @default(UNIT)
}
`,
{ provider: 'postgresql', usePrismaPush: true },
);
});
11 changes: 11 additions & 0 deletions tests/regression/test/v2-migrated/issue-646.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { loadSchema } from '@zenstackhq/testtools';
import { it } from 'vitest';

it('verifies issue 646', async () => {
await loadSchema(`
model Example {
id Int @id
epsilon Decimal @default(0.00000001)
}
`);
});
2 changes: 1 addition & 1 deletion turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"outputs": ["dist/**"]
},
"watch": {
"dependsOn": ["^build"],
"dependsOn": ["^watch"],
Comment thread
ymc9 marked this conversation as resolved.
Outdated
"inputs": ["src/**", "zenstack/*.zmodel"],
"outputs": []
},
Expand Down