From 3c56b8d3b4a1328be1c4fd6a799c512072c95038 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 29 Jul 2025 15:44:36 +0800 Subject: [PATCH 1/3] chore: reorg test runs --- .../runtime/test/client-api/delegate.test.ts | 89 +++++-------------- .../runtime/test/client-api/relation.test.ts | 4 +- packages/runtime/test/utils.ts | 84 +++++++++++------ packages/testtools/src/schema.ts | 11 ++- tests/e2e/cal.com/cal-com.test.ts | 2 +- tests/e2e/formbricks/formbricks.test.ts | 2 +- tests/e2e/trigger.dev/trigger-dev.test.ts | 2 +- 7 files changed, 89 insertions(+), 105 deletions(-) diff --git a/packages/runtime/test/client-api/delegate.test.ts b/packages/runtime/test/client-api/delegate.test.ts index 51749af2..4608643f 100644 --- a/packages/runtime/test/client-api/delegate.test.ts +++ b/packages/runtime/test/client-api/delegate.test.ts @@ -1,4 +1,7 @@ +import path from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import type { ClientContract } from '../../src'; +import { schema, type SchemaType } from '../schemas/delegate/schema'; import { createTestClient } from '../utils'; const DB_NAME = `client-api-delegate-tests`; @@ -6,69 +9,18 @@ const DB_NAME = `client-api-delegate-tests`; describe.each([{ provider: 'sqlite' as const }, { provider: 'postgresql' as const }])( 'Delegate model tests for $provider', ({ provider }) => { - const POLYMORPHIC_SCHEMA = ` -model User { - id Int @id @default(autoincrement()) - email String? @unique - level Int @default(0) - assets Asset[] - ratedVideos RatedVideo[] @relation('direct') -} - -model Comment { - id Int @id @default(autoincrement()) - content String - asset Asset? @relation(fields: [assetId], references: [id], onDelete: Cascade) - assetId Int? -} - -model Asset { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - viewCount Int @default(0) - owner User? @relation(fields: [ownerId], references: [id], onDelete: Cascade) - ownerId Int? - comments Comment[] - assetType String - - @@delegate(assetType) -} - -model Video extends Asset { - duration Int - url String @unique - videoType String - - @@delegate(videoType) -} - -model RatedVideo extends Video { - rating Int - user User? @relation(name: 'direct', fields: [userId], references: [id], onDelete: Cascade) - userId Int? -} - -model Image extends Asset { - format String - gallery Gallery? @relation(fields: [galleryId], references: [id], onDelete: Cascade) - galleryId Int? -} - -model Gallery { - id Int @id @default(autoincrement()) - images Image[] -} -`; - - let client: any; + let client: ClientContract; beforeEach(async () => { - client = await createTestClient(POLYMORPHIC_SCHEMA, { - usePrismaPush: true, - provider, - dbName: provider === 'postgresql' ? DB_NAME : undefined, - }); + client = await createTestClient( + schema, + { + usePrismaPush: true, + provider, + dbName: provider === 'postgresql' ? DB_NAME : undefined, + }, + path.join(__dirname, '../schemas/delegate/schema.zmodel'), + ); }); afterEach(async () => { @@ -76,9 +28,10 @@ model Gallery { }); describe('Delegate create tests', () => { - it('works with create', async () => { + it('works with create11111', async () => { // delegate model cannot be created directly await expect( + // @ts-expect-error client.video.create({ data: { duration: 100, @@ -91,6 +44,7 @@ model Gallery { client.user.create({ data: { assets: { + // @ts-expect-error create: { assetType: 'Video' }, }, }, @@ -294,7 +248,7 @@ model Gallery { }); // omit fields - const r = await client.ratedVideo.findUnique({ + const r: any = await client.ratedVideo.findUnique({ where: { id: v.id }, omit: { viewCount: true, @@ -341,8 +295,10 @@ model Gallery { select: { id: true, assetType: true }, }, ratedVideos: { - url: true, - rating: true, + select: { + url: true, + rating: true, + }, }, }, }), @@ -568,7 +524,7 @@ model Gallery { client.video.findFirst({ where: { comments: { - all: { content: 'c2' }, + every: { content: 'c2' }, }, }, }), @@ -878,6 +834,7 @@ model Gallery { it('works with upsert', async () => { await expect( + // @ts-expect-error client.asset.upsert({ where: { id: 2 }, create: { diff --git a/packages/runtime/test/client-api/relation.test.ts b/packages/runtime/test/client-api/relation.test.ts index 04b5d36a..eeb7a5f3 100644 --- a/packages/runtime/test/client-api/relation.test.ts +++ b/packages/runtime/test/client-api/relation.test.ts @@ -242,7 +242,7 @@ describe.each([ }); describe.each([{ relationName: undefined }, { relationName: 'myM2M' }])( - 'Implicit many-to-many relation ($relationName)', + 'Implicit many-to-many relation (relation: $relationName)', ({ relationName }) => { beforeEach(async () => { client = await createTestClient( @@ -269,7 +269,7 @@ describe.each([ `, { provider, - dbName: provider === 'sqlite' ? 'file:./dev.db' : TEST_DB, + dbName: provider === 'postgresql' ? TEST_DB : undefined, usePrismaPush: true, }, ); diff --git a/packages/runtime/test/utils.ts b/packages/runtime/test/utils.ts index 7c2e02d4..c116c623 100644 --- a/packages/runtime/test/utils.ts +++ b/packages/runtime/test/utils.ts @@ -1,7 +1,7 @@ import { invariant } from '@zenstackhq/common-helpers'; import { loadDocument } from '@zenstackhq/language'; import { PrismaSchemaGenerator } from '@zenstackhq/sdk'; -import { generateTsSchema } from '@zenstackhq/testtools'; +import { createTestProject, generateTsSchema } from '@zenstackhq/testtools'; import SQLite from 'better-sqlite3'; import { execSync } from 'node:child_process'; import fs from 'node:fs'; @@ -67,6 +67,7 @@ export type CreateTestClientOptions = Omit( schema: Schema, options?: CreateTestClientOptions, + schemaFile?: string, ): Promise>; export async function createTestClient( schema: string, @@ -75,35 +76,70 @@ export async function createTestClient( export async function createTestClient( schema: Schema | string, options?: CreateTestClientOptions, + schemaFile?: string, ): Promise { let workDir: string | undefined; let _schema: Schema; + const provider = options?.provider ?? 'sqlite'; let dbName = options?.dbName; - const provider = options?.provider ?? 'sqlite'; - if (provider === 'sqlite' && options?.usePrismaPush && !dbName) { - dbName = 'file:./test.db'; + if (!dbName) { + if (provider === 'sqlite') { + dbName = './test.db'; + } else { + throw new Error(`dbName is required for ${provider} provider`); + } } + const dbUrl = + provider === 'sqlite' + ? `file:${dbName}` + : `postgres://${TEST_PG_CONFIG.user}:${TEST_PG_CONFIG.password}@${TEST_PG_CONFIG.host}:${TEST_PG_CONFIG.port}/${dbName}`; + if (typeof schema === 'string') { - const generated = await generateTsSchema(schema, provider, dbName, options?.extraSourceFiles); + const generated = await generateTsSchema(schema, provider, dbUrl, options?.extraSourceFiles); workDir = generated.workDir; - _schema = generated.schema as Schema; + // replace schema's provider + _schema = { + ...generated.schema, + provider: { + type: provider, + }, + } as Schema; } else { - _schema = schema; - if (options?.extraSourceFiles) { - throw new Error('`extraSourceFiles` is not supported when schema is a SchemaDef object'); + // replace schema's provider + _schema = { + ...schema, + provider: { + type: provider, + }, + }; + workDir = await createTestProject(); + if (schemaFile) { + let schemaContent = fs.readFileSync(schemaFile, 'utf-8'); + if (dbUrl) { + // replace `datasource db { }` section + schemaContent = schemaContent.replace( + /datasource\s+db\s*{[^}]*}/m, + `datasource db { + provider = '${provider}' + url = '${dbUrl}' +}`, + ); + } + fs.writeFileSync(path.join(workDir, 'schema.zmodel'), schemaContent); } } + console.log(`Work directory: ${workDir}`); + const { plugins, ...rest } = options ?? {}; const _options: ClientOptions = { ...rest, } as ClientOptions; if (options?.usePrismaPush) { - invariant(typeof schema === 'string', 'schema must be a string'); - invariant(workDir, 'workDir is required'); + invariant(typeof schema === 'string' || schemaFile, 'a schema file must be provided when using prisma db push'); const r = await loadDocument(path.resolve(workDir, 'schema.zmodel')); if (!r.success) { throw new Error(r.errors.join('\n')); @@ -111,10 +147,14 @@ export async function createTestClient( const prismaSchema = new PrismaSchemaGenerator(r.model); const prismaSchemaText = await prismaSchema.generate(); fs.writeFileSync(path.resolve(workDir, 'schema.prisma'), prismaSchemaText); - execSync('npx prisma db push --schema ./schema.prisma --skip-generate --force-reset', { - cwd: workDir!, - stdio: 'inherit', - }); + try { + execSync('npx prisma db push --schema ./schema.prisma --skip-generate --force-reset', { + cwd: workDir!, + stdio: 'inherit', + }); + } catch (e) { + console.log(e); + } } else { if (provider === 'postgresql') { invariant(dbName, 'dbName is required'); @@ -135,7 +175,7 @@ export async function createTestClient( } as unknown as ClientOptions['dialectConfig']; } else { _options.dialectConfig = { - database: new SQLite(options?.usePrismaPush ? getDbPath(path.join(workDir!, 'schema.prisma')) : ':memory:'), + database: new SQLite(path.join(workDir!, dbName)), } as unknown as ClientOptions['dialectConfig']; } @@ -153,15 +193,3 @@ export async function createTestClient( return client; } - -function getDbPath(prismaSchemaPath: string) { - const content = fs.readFileSync(prismaSchemaPath, 'utf-8'); - const found = content.match(/^\s*url\s*=(\s|")*([^"]+)(\s|")*$/m); - if (!found) { - throw new Error('No url found in prisma schema'); - } - const dbPath = found[2]!; - // convert 'file:./dev.db' to './dev.db' - const r = path.join(path.dirname(prismaSchemaPath), dbPath.replace(/^file:/, '')); - return r; -} diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index fb02835c..0acb0b87 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -7,13 +7,13 @@ import path from 'node:path'; import { match } from 'ts-pattern'; import { createTestProject } from './project'; -function makePrelude(provider: 'sqlite' | 'postgresql', dbName?: string) { +function makePrelude(provider: 'sqlite' | 'postgresql', dbUrl?: string) { return match(provider) .with('sqlite', () => { return ` datasource db { provider = 'sqlite' - url = '${dbName ?? ':memory:'}' + url = '${dbUrl ?? 'file:./test.db'}' } `; }) @@ -21,7 +21,7 @@ datasource db { return ` datasource db { provider = 'postgresql' - url = 'postgres://postgres:postgres@localhost:5432/${dbName}' + url = '${dbUrl ?? 'postgres://postgres:postgres@localhost:5432/db'}' } `; }) @@ -31,15 +31,14 @@ datasource db { export async function generateTsSchema( schemaText: string, provider: 'sqlite' | 'postgresql' = 'sqlite', - dbName?: string, + dbUrl?: string, extraSourceFiles?: Record, ) { const workDir = createTestProject(); - console.log(`Work directory: ${workDir}`); const zmodelPath = path.join(workDir, 'schema.zmodel'); const noPrelude = schemaText.includes('datasource '); - fs.writeFileSync(zmodelPath, `${noPrelude ? '' : makePrelude(provider, dbName)}\n\n${schemaText}`); + fs.writeFileSync(zmodelPath, `${noPrelude ? '' : makePrelude(provider, dbUrl)}\n\n${schemaText}`); const pluginModelFiles = glob.sync(path.resolve(__dirname, '../../runtime/src/plugins/**/plugin.zmodel')); diff --git a/tests/e2e/cal.com/cal-com.test.ts b/tests/e2e/cal.com/cal-com.test.ts index 84290d5f..0ca58a1b 100644 --- a/tests/e2e/cal.com/cal-com.test.ts +++ b/tests/e2e/cal.com/cal-com.test.ts @@ -6,7 +6,7 @@ import path from 'node:path'; describe('Cal.com e2e tests', () => { it('has a working schema', async () => { await expect( - generateTsSchema(fs.readFileSync(path.join(__dirname, 'schema.zmodel'), 'utf8'), 'postgresql', 'cal-com'), + generateTsSchema(fs.readFileSync(path.join(__dirname, 'schema.zmodel'), 'utf8'), 'postgresql'), ).resolves.toBeTruthy(); }); }); diff --git a/tests/e2e/formbricks/formbricks.test.ts b/tests/e2e/formbricks/formbricks.test.ts index 1e16f6dd..60766a45 100644 --- a/tests/e2e/formbricks/formbricks.test.ts +++ b/tests/e2e/formbricks/formbricks.test.ts @@ -6,7 +6,7 @@ import path from 'node:path'; describe('Formbricks e2e tests', () => { it('has a working schema', async () => { await expect( - generateTsSchema(fs.readFileSync(path.join(__dirname, 'schema.zmodel'), 'utf8'), 'postgresql', 'cal-com'), + generateTsSchema(fs.readFileSync(path.join(__dirname, 'schema.zmodel'), 'utf8'), 'postgresql'), ).resolves.toBeTruthy(); }); }); diff --git a/tests/e2e/trigger.dev/trigger-dev.test.ts b/tests/e2e/trigger.dev/trigger-dev.test.ts index 6c5b9e32..9de8d683 100644 --- a/tests/e2e/trigger.dev/trigger-dev.test.ts +++ b/tests/e2e/trigger.dev/trigger-dev.test.ts @@ -6,7 +6,7 @@ import { describe, expect, it } from 'vitest'; describe('Trigger.dev e2e tests', () => { it('has a working schema', async () => { await expect( - generateTsSchema(fs.readFileSync(path.join(__dirname, 'schema.zmodel'), 'utf8'), 'postgresql', 'cal-com'), + generateTsSchema(fs.readFileSync(path.join(__dirname, 'schema.zmodel'), 'utf8'), 'postgresql'), ).resolves.toBeTruthy(); }); }); From 80e57572f33ed8194841f6e9df5d8ad8ad4e45d0 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 29 Jul 2025 15:47:25 +0800 Subject: [PATCH 2/3] update --- packages/runtime/test/client-api/delegate.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime/test/client-api/delegate.test.ts b/packages/runtime/test/client-api/delegate.test.ts index 4608643f..a9ca705c 100644 --- a/packages/runtime/test/client-api/delegate.test.ts +++ b/packages/runtime/test/client-api/delegate.test.ts @@ -28,7 +28,7 @@ describe.each([{ provider: 'sqlite' as const }, { provider: 'postgresql' as cons }); describe('Delegate create tests', () => { - it('works with create11111', async () => { + it('works with create', async () => { // delegate model cannot be created directly await expect( // @ts-expect-error From af231510588a5d67d30d059dbfb44c4663db929c Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 29 Jul 2025 15:48:33 +0800 Subject: [PATCH 3/3] update --- packages/runtime/test/utils.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/runtime/test/utils.ts b/packages/runtime/test/utils.ts index c116c623..e7387e85 100644 --- a/packages/runtime/test/utils.ts +++ b/packages/runtime/test/utils.ts @@ -147,14 +147,10 @@ export async function createTestClient( const prismaSchema = new PrismaSchemaGenerator(r.model); const prismaSchemaText = await prismaSchema.generate(); fs.writeFileSync(path.resolve(workDir, 'schema.prisma'), prismaSchemaText); - try { - execSync('npx prisma db push --schema ./schema.prisma --skip-generate --force-reset', { - cwd: workDir!, - stdio: 'inherit', - }); - } catch (e) { - console.log(e); - } + execSync('npx prisma db push --schema ./schema.prisma --skip-generate --force-reset', { + cwd: workDir!, + stdio: 'inherit', + }); } else { if (provider === 'postgresql') { invariant(dbName, 'dbName is required');