diff --git a/packages/client/tests/functional/_utils/getTestSuiteInfo.ts b/packages/client/tests/functional/_utils/getTestSuiteInfo.ts index 4f39fe37b7c7..3050ae097248 100644 --- a/packages/client/tests/functional/_utils/getTestSuiteInfo.ts +++ b/packages/client/tests/functional/_utils/getTestSuiteInfo.ts @@ -17,6 +17,7 @@ export type NamedTestSuiteConfig = { relationMode?: `${RelationModes}` engineType?: `${ClientEngineType}` clientRuntime?: `${ClientRuntime}` + previewFeatures?: string[] } } diff --git a/packages/client/tests/functional/_utils/getTestSuitePlan.ts b/packages/client/tests/functional/_utils/getTestSuitePlan.ts index da1327a92eeb..54b516d16b00 100644 --- a/packages/client/tests/functional/_utils/getTestSuitePlan.ts +++ b/packages/client/tests/functional/_utils/getTestSuitePlan.ts @@ -40,6 +40,7 @@ export function getTestSuitePlan( expandedSuiteConfigs.forEach((config) => { config.matrixOptions.engineType ??= testCliMeta.engineType config.matrixOptions.clientRuntime ??= testCliMeta.runtime + config.matrixOptions.previewFeatures ??= testCliMeta.previewFeatures }) return expandedSuiteConfigs.map((namedConfig, configIndex) => ({ diff --git a/packages/client/tests/functional/extensions/result.ts b/packages/client/tests/functional/extensions/result.ts index 37970cf200e9..bf8a50a01b66 100644 --- a/packages/client/tests/functional/extensions/result.ts +++ b/packages/client/tests/functional/extensions/result.ts @@ -24,390 +24,400 @@ function prismaWithExtension() { }) } -testMatrix.setupTestSuite(() => { - beforeEach(async () => { - await prisma.post.deleteMany() - await prisma.user.deleteMany() - const { id: userId } = await prisma.user.create({ - data: { - email, - firstName: 'John', - lastName: 'Smith', - }, - }) +testMatrix.setupTestSuite( + () => { + beforeEach(async () => { + await prisma.post.deleteMany() + await prisma.user.deleteMany() + const { id: userId } = await prisma.user.create({ + data: { + email, + firstName: 'John', + lastName: 'Smith', + }, + }) - await prisma.post.create({ - data: { - user: { connect: { id: userId } }, - }, + await prisma.post.create({ + data: { + user: { connect: { id: userId } }, + }, + }) }) - }) - test('findFirst', async () => { - const xprisma = prismaWithExtension() + test('findFirst', async () => { + const xprisma = prismaWithExtension() - const user = await xprisma.user.findFirst({}) - expect(user?.fullName).toBe('John Smith') - expectTypeOf(user?.fullName).toEqualTypeOf() - }) + const user = await xprisma.user.findFirst({}) + expect(user?.fullName).toBe('John Smith') + expectTypeOf(user?.fullName).toEqualTypeOf() + }) - test('findFirst using $allModels', async () => { - const xprisma = prisma.$extends({ - result: { - $allModels: { - computed: { - compute: () => 123, + test('findFirst using $allModels', async () => { + const xprisma = prisma.$extends({ + result: { + $allModels: { + computed: { + compute: () => 123, + }, }, }, - }, - }) + }) - const user = await xprisma.user.findFirst({}) - expect(user?.computed).toBe(123) - expectTypeOf(user?.computed).toEqualTypeOf() - }) + const user = await xprisma.user.findFirst({}) + expect(user?.computed).toBe(123) + expectTypeOf(user?.computed).toEqualTypeOf() + }) - test('findUnique', async () => { - const xprisma = prismaWithExtension() + test('findUnique', async () => { + const xprisma = prismaWithExtension() - const user = await xprisma.user.findUnique({ where: { email } }) - expect(user?.fullName).toBe('John Smith') - expectTypeOf(user?.fullName).toEqualTypeOf() - }) + const user = await xprisma.user.findUnique({ where: { email } }) + expect(user?.fullName).toBe('John Smith') + expectTypeOf(user?.fullName).toEqualTypeOf() + }) - test('findMany', async () => { - const xprisma = prismaWithExtension() + test('findMany', async () => { + const xprisma = prismaWithExtension() - const user = await xprisma.user.findMany({}) - expect(user[0].fullName).toBe('John Smith') - expectTypeOf(user[0].fullName).toEqualTypeOf() - }) + const user = await xprisma.user.findMany({}) + expect(user[0].fullName).toBe('John Smith') + expectTypeOf(user[0].fullName).toEqualTypeOf() + }) - test('create', async () => { - const xprisma = prismaWithExtension() + test('create', async () => { + const xprisma = prismaWithExtension() - const user = await xprisma.user.create({ - data: { - firstName: 'Max', - lastName: 'Mustermann', - email: faker.internet.email(), - }, + const user = await xprisma.user.create({ + data: { + firstName: 'Max', + lastName: 'Mustermann', + email: faker.internet.email(), + }, + }) + expect(user.fullName).toBe('Max Mustermann') + expectTypeOf(user.fullName).toEqualTypeOf() }) - expect(user.fullName).toBe('Max Mustermann') - expectTypeOf(user.fullName).toEqualTypeOf() - }) - test('update', async () => { - const xprisma = prismaWithExtension() - const user = await xprisma.user.update({ - where: { email }, - data: { firstName: 'Jane' }, + test('update', async () => { + const xprisma = prismaWithExtension() + const user = await xprisma.user.update({ + where: { email }, + data: { firstName: 'Jane' }, + }) + + expect(user.fullName).toBe('Jane Smith') + expectTypeOf(user.fullName).toEqualTypeOf() }) - expect(user.fullName).toBe('Jane Smith') - expectTypeOf(user.fullName).toEqualTypeOf() - }) + test('upsert - update', async () => { + const xprisma = prismaWithExtension() + const user = await xprisma.user.upsert({ + where: { email }, + update: { firstName: 'Jane' }, + create: { email, firstName: 'Create', lastName: 'Shouldnothappen' }, + }) - test('upsert - update', async () => { - const xprisma = prismaWithExtension() - const user = await xprisma.user.upsert({ - where: { email }, - update: { firstName: 'Jane' }, - create: { email, firstName: 'Create', lastName: 'Shouldnothappen' }, + expect(user.fullName).toBe('Jane Smith') + expectTypeOf(user.fullName).toEqualTypeOf() }) - expect(user.fullName).toBe('Jane Smith') - expectTypeOf(user.fullName).toEqualTypeOf() - }) + test('upsert - create', async () => { + const nonExistingEmail = faker.internet.email() + const xprisma = prismaWithExtension() + const user = await xprisma.user.upsert({ + where: { email: nonExistingEmail }, + update: { firstName: 'Update', lastName: 'Shouldnothappen' }, + create: { email: nonExistingEmail, firstName: 'Jane', lastName: 'Smith' }, + }) - test('upsert - create', async () => { - const nonExistingEmail = faker.internet.email() - const xprisma = prismaWithExtension() - const user = await xprisma.user.upsert({ - where: { email: nonExistingEmail }, - update: { firstName: 'Update', lastName: 'Shouldnothappen' }, - create: { email: nonExistingEmail, firstName: 'Jane', lastName: 'Smith' }, + expect(user.fullName).toBe('Jane Smith') }) - expect(user.fullName).toBe('Jane Smith') - }) + test('when using select', async () => { + const xprisma = prismaWithExtension() - test('when using select', async () => { - const xprisma = prismaWithExtension() - - const user = await xprisma.user.findFirst({ - select: { - fullName: true, - }, + const user = await xprisma.user.findFirst({ + select: { + fullName: true, + }, + }) + expect(user?.fullName).toBe('John Smith') + expect(user).not.toHaveProperty('firstName') + expect(user).not.toHaveProperty('lastName') + expectTypeOf(user?.fullName).toEqualTypeOf() + expectTypeOf(user).not.toHaveProperty('firstName') + expectTypeOf(user).not.toHaveProperty('lastName') }) - expect(user?.fullName).toBe('John Smith') - expect(user).not.toHaveProperty('firstName') - expect(user).not.toHaveProperty('lastName') - expectTypeOf(user?.fullName).toEqualTypeOf() - expectTypeOf(user).not.toHaveProperty('firstName') - expectTypeOf(user).not.toHaveProperty('lastName') - }) - test('when using select and $allModels', async () => { - const xprisma = prisma.$extends({ - result: { - $allModels: { - computed: { - compute: () => 123, + test('when using select and $allModels', async () => { + const xprisma = prisma.$extends({ + result: { + $allModels: { + computed: { + compute: () => 123, + }, }, }, - }, - }) + }) - const user = await xprisma.user.findFirst({ - select: { - id: true, // TODO: since computed field has no dependencies, - // we need to query at least one non-computed field in order for query to succeed - computed: true, - }, + const user = await xprisma.user.findFirst({ + select: { + id: true, // TODO: since computed field has no dependencies, + // we need to query at least one non-computed field in order for query to succeed + computed: true, + }, + }) + expect(user?.computed).toBe(123) + expectTypeOf(user?.computed).toEqualTypeOf() }) - expect(user?.computed).toBe(123) - expectTypeOf(user?.computed).toEqualTypeOf() - }) - - test('relationships: with include', async () => { - const xprisma = prismaWithExtension() - const post = await xprisma.post.findFirst({ include: { user: true } }) - expect(post?.user.fullName).toBe('John Smith') - expectTypeOf(post?.user.fullName).toEqualTypeOf() - }) + test('relationships: with include', async () => { + const xprisma = prismaWithExtension() + const post = await xprisma.post.findFirst({ include: { user: true } }) - test('relationships: with select', async () => { - const xprisma = prismaWithExtension() - const post = await xprisma.post.findFirst({ select: { user: true } }) - - expect(post?.user.fullName).toBe('John Smith') - expectTypeOf(post?.user.fullName).toEqualTypeOf() - }) + expect(post?.user.fullName).toBe('John Smith') + expectTypeOf(post?.user.fullName).toEqualTypeOf() + }) - test('relationships: with deep select', async () => { - const xprisma = prismaWithExtension() - const post = await xprisma.post.findFirst({ select: { user: { select: { fullName: true } } } }) + test('relationships: with select', async () => { + const xprisma = prismaWithExtension() + const post = await xprisma.post.findFirst({ select: { user: true } }) - expect(post?.user.fullName).toBe('John Smith') - expectTypeOf(post?.user.fullName).toEqualTypeOf() - }) + expect(post?.user.fullName).toBe('John Smith') + expectTypeOf(post?.user.fullName).toEqualTypeOf() + }) - test('relationships: mixed include and select', async () => { - const xprisma = prismaWithExtension() - const post = await xprisma.post.findFirst({ include: { user: { select: { fullName: true } } } }) + test('relationships: with deep select', async () => { + const xprisma = prismaWithExtension() + const post = await xprisma.post.findFirst({ select: { user: { select: { fullName: true } } } }) - expect(post?.user.fullName).toBe('John Smith') - expectTypeOf(post?.user.fullName).toEqualTypeOf() - }) - - test('dependencies between computed fields', async () => { - const xprisma = prismaWithExtension().$extends({ - result: { - user: { - loudName: { - needs: { fullName: true }, - compute(user) { - expectTypeOf(user.fullName).toEqualTypeOf() - return user.fullName.toUpperCase() - }, - }, - }, - }, + expect(post?.user.fullName).toBe('John Smith') + expectTypeOf(post?.user.fullName).toEqualTypeOf() }) - const user = await xprisma.user.findFirst() - expect(user?.loudName).toBe('JOHN SMITH') - expectTypeOf(user?.loudName).toEqualTypeOf() - }) + test('relationships: mixed include and select', async () => { + const xprisma = prismaWithExtension() + const post = await xprisma.post.findFirst({ include: { user: { select: { fullName: true } } } }) - test('shadowing dependency', async () => { - const xprisma = prisma.$extends({ - result: { - user: { - firstName: { - needs: { firstName: true }, - compute(user) { - return user.firstName.toUpperCase() - }, - }, - }, - }, + expect(post?.user.fullName).toBe('John Smith') + expectTypeOf(post?.user.fullName).toEqualTypeOf() }) - const user = await xprisma.user.findFirst() - expect(user?.firstName).toBe('JOHN') - expectTypeOf(user?.firstName).toEqualTypeOf() - }) - - test('shadowing dependency multiple times', async () => { - const xprisma = prisma - .$extends({ + test('dependencies between computed fields', async () => { + const xprisma = prismaWithExtension().$extends({ result: { user: { - firstName: { - needs: { firstName: true }, + loudName: { + needs: { fullName: true }, compute(user) { - return user.firstName.toUpperCase() + expectTypeOf(user.fullName).toEqualTypeOf() + return user.fullName.toUpperCase() }, }, }, }, }) - .$extends({ + + const user = await xprisma.user.findFirst() + expect(user?.loudName).toBe('JOHN SMITH') + expectTypeOf(user?.loudName).toEqualTypeOf() + }) + + test('shadowing dependency', async () => { + const xprisma = prisma.$extends({ result: { user: { firstName: { needs: { firstName: true }, compute(user) { - return `${user.firstName}!!!` + return user.firstName.toUpperCase() }, }, }, }, }) - const user = await xprisma.user.findFirst() - expect(user?.firstName).toBe('JOHN!!!') - expectTypeOf(user?.firstName).toEqualTypeOf() - }) + const user = await xprisma.user.findFirst() + expect(user?.firstName).toBe('JOHN') + expectTypeOf(user?.firstName).toEqualTypeOf() + }) - test('empty extension does nothing', async () => { - const xprisma = prismaWithExtension() - .$extends({ - result: {}, - }) - .$extends({ - result: { - user: {}, - }, - }) + test('shadowing dependency multiple times', async () => { + const xprisma = prisma + .$extends({ + result: { + user: { + firstName: { + needs: { firstName: true }, + compute(user) { + return user.firstName.toUpperCase() + }, + }, + }, + }, + }) + .$extends({ + result: { + user: { + firstName: { + needs: { firstName: true }, + compute(user) { + return `${user.firstName}!!!` + }, + }, + }, + }, + }) - const user = await xprisma.user.findFirst({}) - expect(user?.fullName).toBe('John Smith') - expectTypeOf(user?.fullName).toEqualTypeOf() - }) + const user = await xprisma.user.findFirst() + expect(user?.firstName).toBe('JOHN!!!') + expectTypeOf(user?.firstName).toEqualTypeOf() + }) - test('with null result', async () => { - const xprisma = prismaWithExtension() + test('empty extension does nothing', async () => { + const xprisma = prismaWithExtension() + .$extends({ + result: {}, + }) + .$extends({ + result: { + user: {}, + }, + }) - const user = await xprisma.user.findUnique({ where: { email: 'nothere@example.com' } }) - expect(user).toBeNull() - expectTypeOf(user).toBeNullable() - }) + const user = await xprisma.user.findFirst({}) + expect(user?.fullName).toBe('John Smith') + expectTypeOf(user?.fullName).toEqualTypeOf() + }) - test('error in computed field', async () => { - const xprisma = prisma.$extends({ - name: 'Faulty extension', - result: { - user: { - fullName: { - needs: { firstName: true, lastName: true }, - compute() { - throw new Error('oops!') + test('with null result', async () => { + const xprisma = prismaWithExtension() + + const user = await xprisma.user.findUnique({ where: { email: 'nothere@example.com' } }) + expect(user).toBeNull() + expectTypeOf(user).toBeNullable() + }) + + test('error in computed field', async () => { + const xprisma = prisma.$extends({ + name: 'Faulty extension', + result: { + user: { + fullName: { + needs: { firstName: true, lastName: true }, + compute() { + throw new Error('oops!') + }, }, }, }, - }, - }) + }) - const user = await xprisma.user.findFirstOrThrow({}) - expect(() => user.fullName).toThrowErrorMatchingInlineSnapshot(`oops!`) - expectTypeOf(() => user.fullName).toEqualTypeOf<() => never>() - }) + const user = await xprisma.user.findFirstOrThrow({}) + expect(() => user.fullName).toThrowErrorMatchingInlineSnapshot(`oops!`) + expectTypeOf(() => user.fullName).toEqualTypeOf<() => never>() + }) - test('error in computed field with no name', async () => { - const xprisma = prisma.$extends({ - result: { - user: { - fullName: { - needs: { firstName: true, lastName: true }, - compute() { - throw new Error('oops!') + test('error in computed field with no name', async () => { + const xprisma = prisma.$extends({ + result: { + user: { + fullName: { + needs: { firstName: true, lastName: true }, + compute() { + throw new Error('oops!') + }, }, }, }, - }, - }) + }) - const user = await xprisma.user.findFirstOrThrow({}) - expect(() => user.fullName).toThrowErrorMatchingInlineSnapshot(`oops!`) - expectTypeOf(() => user.fullName).toEqualTypeOf<() => never>() - }) + const user = await xprisma.user.findFirstOrThrow({}) + expect(() => user.fullName).toThrowErrorMatchingInlineSnapshot(`oops!`) + expectTypeOf(() => user.fullName).toEqualTypeOf<() => never>() + }) - test('nested includes should include scalars and relations', async () => { - const xprisma = prisma.$extends({ - result: { - user: { - fullName: { - needs: { firstName: true, lastName: true }, - compute(user) { - return `${user.firstName} ${user.lastName}` + test('nested includes should include scalars and relations', async () => { + const xprisma = prisma.$extends({ + result: { + user: { + fullName: { + needs: { firstName: true, lastName: true }, + compute(user) { + return `${user.firstName} ${user.lastName}` + }, }, }, }, - }, - }) + }) - const user = await xprisma.user.findFirstOrThrow({ - include: { - posts: { - where: {}, // testing with where as it caused issues - include: { - user: { - include: { - posts: true, + const user = await xprisma.user.findFirstOrThrow({ + include: { + posts: { + where: {}, // testing with where as it caused issues + include: { + user: { + include: { + posts: true, + }, }, }, }, }, - }, - }) + }) - expectTypeOf(user.id).toEqualTypeOf() - expectTypeOf(user.posts[0].id).toEqualTypeOf() - expectTypeOf(user.posts[0].user.id).toEqualTypeOf() - expectTypeOf(user.posts[0].user.posts[0].id).toEqualTypeOf() - expectTypeOf(user.posts[0].user.posts[0]).not.toHaveProperty('user') - }) + expectTypeOf(user.id).toEqualTypeOf() + expectTypeOf(user.posts[0].id).toEqualTypeOf() + expectTypeOf(user.posts[0].user.id).toEqualTypeOf() + expectTypeOf(user.posts[0].user.posts[0].id).toEqualTypeOf() + expectTypeOf(user.posts[0].user.posts[0]).not.toHaveProperty('user') + }) - test('when any type is passed as an input default selection type is returned', () => { - ;async () => { - const xprisma = prisma.$extends({}) + test('when any type is passed as an input default selection type is returned', () => { + ;async () => { + const xprisma = prisma.$extends({}) - const data = await xprisma.user.findFirstOrThrow({} as any) - expectTypeOf(data).toEqualTypeOf() - } - }) + const data = await xprisma.user.findFirstOrThrow({} as any) + expectTypeOf(data).toEqualTypeOf() + } + }) - test('when args have both include and select and one of them is optional, result includes both', () => { - ;async () => { - const xprisma = prisma.$extends({}) + test('when args have both include and select and one of them is optional, result includes both', () => { + ;async () => { + const xprisma = prisma.$extends({}) - const userFindMany: PrismaNamespace.UserFindManyArgs = {} + const userFindMany: PrismaNamespace.UserFindManyArgs = {} - // testing for extended client - const usersXPrisma = await xprisma.user.findMany({ - ...userFindMany, - include: { - posts: true, - }, - }) + // testing for extended client + const usersXPrisma = await xprisma.user.findMany({ + ...userFindMany, + include: { + posts: true, + }, + }) - // regular client should be the same - const usersPrisma = await prisma.user.findMany({ - ...userFindMany, - include: { - posts: true, - }, - }) + // regular client should be the same + const usersPrisma = await prisma.user.findMany({ + ...userFindMany, + include: { + posts: true, + }, + }) - expectTypeOf(usersXPrisma[0].email).toEqualTypeOf() - expectTypeOf(usersXPrisma[0].posts).toEqualTypeOf() + expectTypeOf(usersXPrisma[0].email).toEqualTypeOf() + expectTypeOf(usersXPrisma[0].posts).toEqualTypeOf() - expectTypeOf(usersPrisma[0].email).toEqualTypeOf() - expectTypeOf(usersPrisma[0].posts).toEqualTypeOf() - } - }) -}) + expectTypeOf(usersPrisma[0].email).toEqualTypeOf() + expectTypeOf(usersPrisma[0].posts).toEqualTypeOf() + } + }) + }, + { + skip(when, suiteConfig) { + when( + suiteConfig.previewFeatures?.includes('relationJoins') === true, + 'https://github.com/prisma/prisma/issues/22971', + ) + }, + }, +)