From c1ad53e3e105b4c7ff23a985c9ee6519b354f6aa Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Sun, 12 Oct 2025 22:12:25 -0700 Subject: [PATCH] fix: relation include orderBy validation issue, more test migrations --- packages/language/src/utils.ts | 7 +- .../src/client/crud/validator/index.ts | 4 +- packages/testtools/src/schema.ts | 2 +- .../test/v2-migrated/issue-1647.test.ts | 51 +++++++++ .../test/v2-migrated/issue-1648.test.ts | 42 +++++++ .../test/v2-migrated/issue-1674.test.ts | 80 ++++++++++++++ .../test/v2-migrated/issue-1681.test.ts | 29 +++++ .../test/v2-migrated/issue-1693.test.ts | 18 +++ .../test/v2-migrated/issue-1695.test.ts | 21 ++++ .../test/v2-migrated/issue-1698.test.ts | 72 ++++++++++++ .../test/v2-migrated/issue-1734.test.ts | 104 ++++++++++++++++++ .../test/v2-migrated/issue-1745.test.ts | 94 ++++++++++++++++ .../test/v2-migrated/issue-1755.test.ts | 58 ++++++++++ .../test/v2-migrated/issue-1758.test.ts | 27 +++++ .../test/v2-migrated/issue-1763.test.ts | 42 +++++++ .../test/v2-migrated/issue-1786.test.ts | 46 ++++++++ .../test/v2-migrated/issue-1835.test.ts | 23 ++++ .../test/v2-migrated/issue-1849.test.ts | 27 +++++ .../test/v2-migrated/issue-1857.test.ts | 39 +++++++ .../test/v2-migrated/issue-1870.test.ts | 14 +++ .../test/v2-migrated/issue-1894.test.ts | 46 ++++++++ 21 files changed, 841 insertions(+), 5 deletions(-) create mode 100644 tests/regression/test/v2-migrated/issue-1647.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1648.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1674.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1681.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1693.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1695.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1698.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1734.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1745.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1755.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1758.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1763.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1786.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1835.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1849.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1857.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1870.test.ts create mode 100644 tests/regression/test/v2-migrated/issue-1894.test.ts diff --git a/packages/language/src/utils.ts b/packages/language/src/utils.ts index 19220f07..762187b3 100644 --- a/packages/language/src/utils.ts +++ b/packages/language/src/utils.ts @@ -166,10 +166,11 @@ export function getRecursiveBases( return result; } seen.add(decl); - decl.mixins.forEach((mixin) => { - // avoid using mixin.ref since this function can be called before linking + const bases = [...decl.mixins, ...(isDataModel(decl) && decl.baseModel ? [decl.baseModel] : [])]; + bases.forEach((base) => { + // avoid using .ref since this function can be called before linking const baseDecl = decl.$container.declarations.find( - (d): d is TypeDef => isTypeDef(d) && d.name === mixin.$refText, + (d): d is TypeDef | DataModel => isTypeDef(d) || (isDataModel(d) && d.name === base.$refText), ); if (baseDecl) { if (!includeDelegate && isDelegateModel(baseDecl)) { diff --git a/packages/runtime/src/client/crud/validator/index.ts b/packages/runtime/src/client/crud/validator/index.ts index 11d93350..fd3be7ac 100644 --- a/packages/runtime/src/client/crud/validator/index.ts +++ b/packages/runtime/src/client/crud/validator/index.ts @@ -677,7 +677,9 @@ export class InputValidator { ...(fieldDef.array ? { // to-many relations can be ordered, skipped, taken, and cursor-located - orderBy: z.lazy(() => this.makeOrderBySchema(fieldDef.type, true, false)).optional(), + orderBy: z + .lazy(() => this.orArray(this.makeOrderBySchema(fieldDef.type, true, false), true)) + .optional(), skip: this.makeSkipSchema().optional(), take: this.makeTakeSchema().optional(), cursor: this.makeCursorSchema(fieldDef.type).optional(), diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index 7662fba0..516d445a 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -54,7 +54,7 @@ export async function generateTsSchema( if (extraSourceFiles) { for (const [fileName, content] of Object.entries(extraSourceFiles)) { - const filePath = path.resolve(workDir, `${fileName}.ts`); + const filePath = path.resolve(workDir, !fileName.endsWith('.ts') ? `${fileName}.ts` : fileName); fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, content); } diff --git a/tests/regression/test/v2-migrated/issue-1647.test.ts b/tests/regression/test/v2-migrated/issue-1647.test.ts new file mode 100644 index 00000000..faebabed --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1647.test.ts @@ -0,0 +1,51 @@ +import { loadSchema } from '@zenstackhq/testtools'; +import { describe, it } from 'vitest'; + +// TODO: multi-schema support +describe.skip('Regression for issue 1647', () => { + it('inherits @@schema by default', async () => { + await loadSchema( + ` + model Asset { + id Int @id + type String + @@delegate(type) + @@schema('public') + } + + model Post extends Asset { + title String + } + `, + ); + }); + + it('respects sub model @@schema overrides', async () => { + await loadSchema( + ` + datasource db { + provider = 'postgresql' + url = env('DATABASE_URL') + schemas = ['public', 'post'] + } + + generator client { + provider = 'prisma-client-js' + previewFeatures = ['multiSchema'] + } + + model Asset { + id Int @id + type String + @@delegate(type) + @@schema('public') + } + + model Post extends Asset { + title String + @@schema('post') + } + `, + ); + }); +}); diff --git a/tests/regression/test/v2-migrated/issue-1648.test.ts b/tests/regression/test/v2-migrated/issue-1648.test.ts new file mode 100644 index 00000000..7fe06686 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1648.test.ts @@ -0,0 +1,42 @@ +import { createPolicyTestClient } from '@zenstackhq/testtools'; +import { expect, it } from 'vitest'; + +it('verifies issue 1648', async () => { + const db = await createPolicyTestClient( + ` +model User { + id Int @id @default(autoincrement()) + profile Profile? + posts Post[] +} + +model Profile { + id Int @id @default(autoincrement()) + someText String + user User @relation(fields: [userId], references: [id]) + userId Int @unique +} + +model Post { + id Int @id @default(autoincrement()) + title String + + userId Int + user User @relation(fields: [userId], references: [id]) + + // this will always be true, even if the someText field is "canUpdate" + @@deny("post-update", user.profile.someText != "canUpdate") + + @@allow("all", true) +} + `, + ); + + await db.$unuseAll().user.create({ data: { id: 1, profile: { create: { someText: 'canUpdate' } } } }); + await db.$unuseAll().user.create({ data: { id: 2, profile: { create: { someText: 'nothing' } } } }); + await db.$unuseAll().post.create({ data: { id: 1, title: 'Post1', userId: 1 } }); + await db.$unuseAll().post.create({ data: { id: 2, title: 'Post2', userId: 2 } }); + + await expect(db.post.update({ where: { id: 1 }, data: { title: 'Post1-1' } })).toResolveTruthy(); + await expect(db.post.update({ where: { id: 2 }, data: { title: 'Post2-2' } })).toBeRejectedByPolicy(); +}); diff --git a/tests/regression/test/v2-migrated/issue-1674.test.ts b/tests/regression/test/v2-migrated/issue-1674.test.ts new file mode 100644 index 00000000..759f7d82 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1674.test.ts @@ -0,0 +1,80 @@ +import { createPolicyTestClient } from '@zenstackhq/testtools'; +import { expect, it } from 'vitest'; + +it('verifies issue 1674', async () => { + const db = await createPolicyTestClient( + ` +model User { + id String @id @default(cuid()) + email String @unique @email @length(6, 32) + posts Post[] + + // everybody can signup + @@allow('create', true) + + // full access by self + @@allow('all', auth() == this) +} + +model Blog { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + post Post? @relation(fields: [postId], references: [id], onDelete: Cascade) + postId String? +} + +model Post { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + title String @length(1, 256) + content String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId String + + blogs Blog[] + + type String + + @@delegate(type) +} + +model PostA extends Post { +} + +model PostB extends Post { +} + `, + ); + + const user = await db.$unuseAll().user.create({ + data: { email: 'abc@def.com' }, + }); + + const blog = await db.$unuseAll().blog.create({ + data: {}, + }); + + const authDb = db.$setAuth(user); + await expect( + authDb.postA.create({ + data: { + content: 'content', + title: 'title', + blogs: { + connect: { + id: blog.id, + }, + }, + author: { + connect: { + id: user.id, + }, + }, + }, + }), + ).toBeRejectedByPolicy(); +}); diff --git a/tests/regression/test/v2-migrated/issue-1681.test.ts b/tests/regression/test/v2-migrated/issue-1681.test.ts new file mode 100644 index 00000000..5fb20312 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1681.test.ts @@ -0,0 +1,29 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { expect, it } from 'vitest'; + +it('verifies issue 1681', async () => { + const db = await createTestClient( + ` +model User { + id Int @id @default(autoincrement()) + posts Post[] + @@allow('all', true) +} + +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId Int @default(auth().id) + @@allow('all', true) +} + `, + ); + + const authDb = db.$setAuth({ id: 1 }); + const user = await db.user.create({ data: {} }); + await expect(authDb.post.createMany({ data: [{ title: 'Post1' }] })).resolves.toMatchObject({ count: 1 }); + + const r = await authDb.post.createManyAndReturn({ data: [{ title: 'Post2' }] }); + expect(r[0].authorId).toBe(user.id); +}); diff --git a/tests/regression/test/v2-migrated/issue-1693.test.ts b/tests/regression/test/v2-migrated/issue-1693.test.ts new file mode 100644 index 00000000..04c4075b --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1693.test.ts @@ -0,0 +1,18 @@ +import { loadSchema } from '@zenstackhq/testtools'; +import { it } from 'vitest'; + +it('verifies issue 1693', async () => { + await loadSchema( + ` +model Animal { + id String @id @default(uuid()) + animalType String @default("") + @@delegate(animalType) +} + +model Dog extends Animal { + name String +} + `, + ); +}); diff --git a/tests/regression/test/v2-migrated/issue-1695.test.ts b/tests/regression/test/v2-migrated/issue-1695.test.ts new file mode 100644 index 00000000..a713f1af --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1695.test.ts @@ -0,0 +1,21 @@ +import { loadSchema } from '@zenstackhq/testtools'; +import { it } from 'vitest'; + +it('verifies issue 1695', async () => { + await loadSchema( + ` +type SoftDelete { + deleted Int @default(0) +} + +model MyModel with SoftDelete { + id String @id @default(cuid()) + name String + + @@deny('update', deleted != 0) + @@deny('post-update', deleted != 0) + @@deny('read', this.deleted != 0) +} + `, + ); +}); diff --git a/tests/regression/test/v2-migrated/issue-1698.test.ts b/tests/regression/test/v2-migrated/issue-1698.test.ts new file mode 100644 index 00000000..c93dfa03 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1698.test.ts @@ -0,0 +1,72 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { expect, it } from 'vitest'; + +it('verifies issue 1698', async () => { + const db = await createTestClient( + ` +model House { + id Int @id @default(autoincrement()) + doorTypeId Int + door Door @relation(fields: [doorTypeId], references: [id]) + houseType String + @@delegate(houseType) +} + +model PrivateHouse extends House { + size Int +} + +model Skyscraper extends House { + height Int +} + +model Door { + id Int @id @default(autoincrement()) + color String + doorType String + houses House[] + @@delegate(doorType) +} + +model IronDoor extends Door { + strength Int +} + +model WoodenDoor extends Door { + texture String +} + `, + ); + + const door1 = await db.ironDoor.create({ + data: { strength: 100, color: 'blue' }, + }); + console.log(door1); + + const door2 = await db.woodenDoor.create({ + data: { texture: 'pine', color: 'red' }, + }); + console.log(door2); + + const house1 = await db.privateHouse.create({ + data: { size: 5000, door: { connect: { id: door1.id } } }, + }); + console.log(house1); + + const house2 = await db.skyscraper.create({ + data: { height: 3000, door: { connect: { id: door2.id } } }, + }); + console.log(house2); + + const r1 = await db.privateHouse.findFirst({ include: { door: true } }); + console.log(r1); + expect(r1).toMatchObject({ + door: { color: 'blue', strength: 100 }, + }); + + const r2 = (await db.skyscraper.findMany({ include: { door: true } }))[0]; + console.log(r2); + expect(r2).toMatchObject({ + door: { color: 'red', texture: 'pine' }, + }); +}); diff --git a/tests/regression/test/v2-migrated/issue-1734.test.ts b/tests/regression/test/v2-migrated/issue-1734.test.ts new file mode 100644 index 00000000..15061653 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1734.test.ts @@ -0,0 +1,104 @@ +import { createPolicyTestClient } from '@zenstackhq/testtools'; +import { expect, it } from 'vitest'; + +// TODO: field-level policy support +it.skip('verifies issue 1734', async () => { + const db = await createPolicyTestClient( + ` +type Base { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Profile with Base { + displayName String + type String + + @@allow('read', true) + @@delegate(type) +} + +model User extends Profile { + username String @unique + access Access[] + organization Organization[] +} + +model Access with Base { + user User @relation(fields: [userId], references: [id]) + userId String + + organization Organization @relation(fields: [organizationId], references: [id]) + organizationId String + + manage Boolean @default(false) + + superadmin Boolean @default(false) + + @@unique([userId,organizationId]) +} + +model Organization extends Profile { + owner User @relation(fields: [ownerId], references: [id]) + ownerId String @default(auth().id) + published Boolean @default(false) @allow('read', access?[user == auth()]) + access Access[] +} + + `, + ); + + const user = await db.$unuseAll().user.create({ + data: { + username: 'test', + displayName: 'test', + }, + }); + + const organization = await db.$unuseAll().organization.create({ + data: { + displayName: 'test', + owner: { + connect: { + id: user.id, + }, + }, + access: { + create: { + user: { + connect: { + id: user.id, + }, + }, + manage: true, + superadmin: true, + }, + }, + }, + }); + + const foundUser = await db.profile.findFirst({ + where: { + id: user.id, + }, + }); + expect(foundUser).toMatchObject(user); + + const foundOrg = await db.profile.findFirst({ + where: { + id: organization.id, + }, + }); + // published field not readable + expect(foundOrg).toMatchObject({ id: organization.id, displayName: 'test', type: 'Organization' }); + expect(foundOrg.published).toBeUndefined(); + + const foundOrg1 = await db.$setAuth({ id: user.id }).profile.findFirst({ + where: { + id: organization.id, + }, + }); + // published field readable + expect(foundOrg1.published).not.toBeUndefined(); +}); diff --git a/tests/regression/test/v2-migrated/issue-1745.test.ts b/tests/regression/test/v2-migrated/issue-1745.test.ts new file mode 100644 index 00000000..9140ad84 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1745.test.ts @@ -0,0 +1,94 @@ +import { loadSchema } from '@zenstackhq/testtools'; +import { it } from 'vitest'; + +it('verifies issue 1745', async () => { + await loadSchema( + ` +datasource db { + provider = 'postgresql' + url = env('DATABASE_URL') +} + +enum BuyerType { + STORE + RESTAURANT + WHOLESALER +} + +enum ChainStore { + ALL + CHAINSTORE_1 + CHAINSTORE_2 + CHAINSTORE_3 +} + +type Id { + id String @id @default(cuid()) +} + +type Base with Id { + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Ad with Base { + serial Int @unique @default(autoincrement()) + buyerTypes BuyerType[] + chainStores ChainStore[] + listPrice Float + isSold Boolean @default(false) + + supplier Supplier @relation(fields: [supplierId], references: [id]) + supplierId String @default(auth().companyId) + + // @@allow('all', auth().company.companyType == 'Buyer' && has(buyerTypes, auth().company.buyerType)) + // @@allow('all', auth().company.companyType == 'Supplier' && auth().companyId == supplierId) + // @@allow('all', auth().isAdmin) +} + +model Company with Base { + name String @unique + organizationNumber String @unique + users User[] + buyerType BuyerType + + companyType String + @@delegate(companyType) + + @@allow('read, update', auth().companyId == id) + @@allow('all', auth().isAdmin) +} + +model Buyer extends Company { + storeName String + type String + chainStore ChainStore @default(ALL) + + @@allow('read, update', auth().company.companyType == 'Buyer' && auth().companyId == id) + @@allow('all', auth().isAdmin) +} + +model Supplier extends Company { + ads Ad[] + + @@allow('all', auth().company.companyType == 'Supplier' && auth().companyId == id) + @@allow('all', auth().isAdmin) +} + +model User with Base { + firstName String + lastName String + email String @unique + username String @unique + isAdmin Boolean @default(false) + + company Company? @relation(fields: [companyId], references: [id]) + companyId String? + + @@allow('read', auth().id == id) + @@allow('read', auth().companyId == companyId) + @@allow('all', auth().isAdmin) +} + `, + ); +}); diff --git a/tests/regression/test/v2-migrated/issue-1755.test.ts b/tests/regression/test/v2-migrated/issue-1755.test.ts new file mode 100644 index 00000000..47ac92a1 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1755.test.ts @@ -0,0 +1,58 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { expect, it } from 'vitest'; + +it('verifies issue 1755', async () => { + const db = await createTestClient( + ` +model User { + id Int @id @default(autoincrement()) + contents Content[] +} + +model Content { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + user User @relation(fields: [userId], references: [id]) + userId Int + contentType String + @@delegate(contentType) +} + +model Post extends Content { + title String +} + +model Video extends Content { + name String + duration Int +} + `, + ); + + const user = await db.user.create({ data: {} }); + const now = Date.now(); + await db.post.create({ + data: { title: 'post1', createdAt: new Date(now - 1000), user: { connect: { id: user.id } } }, + }); + await db.post.create({ + data: { title: 'post2', createdAt: new Date(now), user: { connect: { id: user.id } } }, + }); + + // scalar orderBy + await expect(db.post.findFirst({ orderBy: { createdAt: 'desc' } })).resolves.toMatchObject({ + title: 'post2', + }); + + // array orderBy + await expect(db.post.findFirst({ orderBy: [{ createdAt: 'desc' }] })).resolves.toMatchObject({ + title: 'post2', + }); + + // nested orderBy + await expect( + db.user.findFirst({ include: { contents: { orderBy: [{ createdAt: 'desc' }] } } }), + ).resolves.toMatchObject({ + id: user.id, + contents: [{ title: 'post2' }, { title: 'post1' }], + }); +}); diff --git a/tests/regression/test/v2-migrated/issue-1758.test.ts b/tests/regression/test/v2-migrated/issue-1758.test.ts new file mode 100644 index 00000000..d62282e6 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1758.test.ts @@ -0,0 +1,27 @@ +import { loadSchemaWithError } from '@zenstackhq/testtools'; +import { it } from 'vitest'; + +it('verifies issue 1758', async () => { + await loadSchemaWithError( + ` +model Organization { + id String @id @default(cuid()) + contents Content[] @relation("OrganizationContents") +} + +model Content { + id String @id @default(cuid()) + contentType String + organization Organization @relation("OrganizationContents", fields: [organizationId], references: [id]) + organizationId String + @@delegate(contentType) +} + +model Store extends Content { + name String + @@unique([organizationId, name]) +} + `, + 'Cannot use fields inherited from a polymorphic base model in `@@unique`', + ); +}); diff --git a/tests/regression/test/v2-migrated/issue-1763.test.ts b/tests/regression/test/v2-migrated/issue-1763.test.ts new file mode 100644 index 00000000..ec3d5748 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1763.test.ts @@ -0,0 +1,42 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { it } from 'vitest'; + +it('verifies issue 1763', async () => { + await createTestClient( + ` +model Post { + id Int @id @default(autoincrement()) + name String + + type String + @@delegate(type) + + // full access by author + @@allow('all', true) +} + +model ConcretePost extends Post { + age Int +} + `, + + { + extraSourceFiles: { + main: ` +import { ZenStackClient } from '@zenstackhq/runtime'; +import { schema } from './schema'; + +async function test() { + const db = new ZenStackClient(schema, {} as any); + await db.concretePost.create({ + data: { + id: 5, + name: 'a name', + age: 20, + }, + }); +}`, + }, + }, + ); +}); diff --git a/tests/regression/test/v2-migrated/issue-1786.test.ts b/tests/regression/test/v2-migrated/issue-1786.test.ts new file mode 100644 index 00000000..f67a98a7 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1786.test.ts @@ -0,0 +1,46 @@ +import { loadSchema } from '@zenstackhq/testtools'; +import { it } from 'vitest'; + +it('verifies issue 1786', async () => { + await loadSchema( + ` + model User { + id String @id @default(cuid()) + email String @unique @email @length(6, 32) + contents Content[] + + // everybody can signup + @@allow('create', true) + + // full access by self + @@allow('all', auth() == this) + } + + type BaseContent { + published Boolean @default(false) + + @@index([published]) + } + + model Content with BaseContent { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + owner User @relation(fields: [ownerId], references: [id]) + ownerId String + contentType String + + @@delegate(contentType) + } + + model Post extends Content { + title String + } + + model Video extends Content { + name String + duration Int + } + `, + ); +}); diff --git a/tests/regression/test/v2-migrated/issue-1835.test.ts b/tests/regression/test/v2-migrated/issue-1835.test.ts new file mode 100644 index 00000000..2cc6f088 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1835.test.ts @@ -0,0 +1,23 @@ +import { loadSchema } from '@zenstackhq/testtools'; +import { it } from 'vitest'; + +it('verifies issue 1835', async () => { + await loadSchema( + ` +enum Enum { + SOME_VALUE + ANOTHER_VALUE +} + +model Model { + id String @id @default(cuid()) + value Enum + @@ignore +} + +model AnotherModel { + id String @id @default(cuid()) +} +`, + ); +}); diff --git a/tests/regression/test/v2-migrated/issue-1849.test.ts b/tests/regression/test/v2-migrated/issue-1849.test.ts new file mode 100644 index 00000000..cf99d6f3 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1849.test.ts @@ -0,0 +1,27 @@ +import { loadSchema } from '@zenstackhq/testtools'; +import { it } from 'vitest'; + +it('verifies issue 1849', async () => { + await loadSchema( + ` +import './enum' + +datasource db { + provider = 'sqlite' + url = 'file:./dev.db' +} + +model Post { + id Int @id + status Status @default(PUBLISHED) +}`, + { + enum: ` +enum Status { + PENDING + PUBLISHED +} +`, + }, + ); +}); diff --git a/tests/regression/test/v2-migrated/issue-1857.test.ts b/tests/regression/test/v2-migrated/issue-1857.test.ts new file mode 100644 index 00000000..2df875b2 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1857.test.ts @@ -0,0 +1,39 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { it } from 'vitest'; + +it('verifies issue 1857', async () => { + await createTestClient( + ` + type JSONContent { + type String + text String? + } + + model Post { + id String @id @default(uuid()) + content JSONContent @json + @@allow('all', true) + } + `, + { + extraSourceFiles: { + main: ` + import { ZenStackClient } from '@zenstackhq/runtime'; + import { schema } from './schema'; + + async function main() { + const db = new ZenStackClient(schema, {} as any); + await db.post.create({ + data: { + content: { type: 'foo', text: null } + } + }); + } + `, + }, + }, + ); + + // TODO: zod schema support + // zodSchemas.models.JSONContentSchema.parse({ type: 'foo', text: null }); +}); diff --git a/tests/regression/test/v2-migrated/issue-1870.test.ts b/tests/regression/test/v2-migrated/issue-1870.test.ts new file mode 100644 index 00000000..15c8b668 --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1870.test.ts @@ -0,0 +1,14 @@ +import { loadSchema } from '@zenstackhq/testtools'; +import { it } from 'vitest'; + +it('verifies issue 1870', async () => { + await loadSchema( + ` +model Polygon { + id Int @id @default(autoincrement()) + geometry Unsupported("geometry(MultiPolygon, 4326)") + @@index([geometry], name: "parcel_polygon_idx", type: Gist) +} +`, + ); +}); diff --git a/tests/regression/test/v2-migrated/issue-1894.test.ts b/tests/regression/test/v2-migrated/issue-1894.test.ts new file mode 100644 index 00000000..92f4710e --- /dev/null +++ b/tests/regression/test/v2-migrated/issue-1894.test.ts @@ -0,0 +1,46 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { expect, it } from 'vitest'; + +it('verifies issue 1894', async () => { + const db = await createTestClient( + ` +model A { + id Int @id @default(autoincrement()) + b B[] +} + +model B { + id Int @id @default(autoincrement()) + a A @relation(fields: [aId], references: [id]) + aId Int + + type String + @@delegate(type) +} + +model C extends B { + f String? +} + `, + { + extraSourceFiles: { + main: ` + import { ZenStackClient } from '@zenstackhq/runtime'; + import { schema } from './schema'; + + async function main() { + const db = new ZenStackClient(schema, {} as any); + await db.a.create({ data: { id: 0 } }); + await db.c.create({ data: { a: { connect: { id: 0 } } } }); + } + + main(); + +`, + }, + }, + ); + + await db.a.create({ data: { id: 0 } }); + await expect(db.c.create({ data: { a: { connect: { id: 0 } } } })).toResolveTruthy(); +});