supported-queries.ts•17.5 kB
import { faker } from '@faker-js/faker'
import { match } from 'ts-pattern'
import { Providers } from '../_utils/providers'
import { waitFor } from '../_utils/tests/waitFor'
import { NewPrismaClient } from '../_utils/types'
import { providersNotSupportingRelationJoins, providersSupportingRelationJoins, RelationLoadStrategy } from './_common'
import testMatrix from './_matrix'
// @ts-ignore
import type { Prisma as PrismaNamespace, PrismaClient } from './generated/prisma/client'
let prisma: PrismaClient<PrismaNamespace.PrismaClientOptions, 'query'>
declare const newPrismaClient: NewPrismaClient<PrismaClient, typeof PrismaClient>
testMatrix.setupTestSuite(
(suiteConfig, _suiteMeta, _clientMeta, cliMeta) => {
const relationJoinsEnabled = cliMeta.previewFeatures.includes('relationJoins')
// We can't use `Object.keys(Prisma.RelationLoadStrategy).includes(suiteConfig.strategy)` here to let
// the DMMF tell us because the `Prisma` object is not injected at this point yet.
const strategySupportedForProvider = match(suiteConfig.strategy)
.with('join', () => providersSupportingRelationJoins.includes(suiteConfig.provider))
.with('query', () => true)
.exhaustive()
describeIf(relationJoinsEnabled && strategySupportedForProvider)(
'relationLoadStrategy in supported queries',
() => {
const relationLoadStrategy = suiteConfig.strategy as PrismaNamespace.RelationLoadStrategy
const postId = faker.database.mongodbObjectId()
const logs: PrismaNamespace.QueryEvent[] = []
async function expectQueryCountAtLeast(count: Record<RelationLoadStrategy, number>) {
await waitFor(() => {
let expected = count[suiteConfig.strategy]
if (suiteConfig.driverAdapter !== undefined) {
// Driver adapters do not issue BEGIN through the query engine.
expected -= 1
}
expect(logs.length).toBeGreaterThanOrEqual(expected)
})
}
const lateralJoinQueryMatcher = expect.stringMatching('LEFT JOIN LATERAL')
const relationJoinQueryMatcher = match(suiteConfig.provider)
.with(Providers.MYSQL, () => {
// For MySQL, JSON_OBJECT is the only available indicator. We use correlated subqueries
// rather than lateral joins, and 1:1 queries don't even have JSON aggregations or the
// `__prisma_data__` column.
return expect.stringMatching('JSON_OBJECT')
})
.otherwise(() => lateralJoinQueryMatcher)
const relationJoinLogsMatcher = expect.arrayContaining([
expect.objectContaining({ query: relationJoinQueryMatcher }),
])
function expectRelationJoinToBeUsed() {
expect(logs).toEqual(relationJoinLogsMatcher)
}
function expectRelationJoinNotToBeUsed() {
expect(logs).not.toEqual(relationJoinLogsMatcher)
}
function expectRelationJoinToBeUsedIfRequested() {
if (suiteConfig.strategy === 'join') {
expectRelationJoinToBeUsed()
} else {
expectRelationJoinNotToBeUsed()
}
}
beforeAll(async () => {
prisma = newPrismaClient({
log: [
{
emit: 'event',
level: 'query',
},
],
})
prisma.$on('query', (event) => {
logs.push(event)
})
await prisma.$connect()
})
beforeEach(async () => {
await prisma.comment.deleteMany()
await prisma.post.deleteMany()
await prisma.user.deleteMany()
await prisma.user.create({
data: {
login: 'author',
posts: {
create: {
id: postId,
title: 'first post',
content: 'insightful content',
},
},
},
})
await prisma.user.create({
data: {
login: 'commenter',
comments: {
create: {
post: {
connect: { id: postId },
},
body: 'a comment',
},
},
},
})
logs.length = 0
})
test('findMany', async () => {
await expect(
prisma.user.findMany({
relationLoadStrategy,
select: {
login: true,
posts: {
select: {
title: true,
comments: {
select: {
author: {
select: { login: true },
},
body: true,
},
},
},
},
},
}),
).resolves.toMatchInlineSnapshot(`
[
{
"login": "author",
"posts": [
{
"comments": [
{
"author": {
"login": "commenter",
},
"body": "a comment",
},
],
"title": "first post",
},
],
},
{
"login": "commenter",
"posts": [],
},
]
`)
await expectQueryCountAtLeast({
join: 1,
query: 4,
})
expectRelationJoinToBeUsedIfRequested()
})
test('findFirst', async () => {
await expect(
prisma.user.findFirst({
relationLoadStrategy,
where: {
login: 'author',
},
select: {
login: true,
posts: {
select: {
title: true,
comments: {
select: {
author: {
select: {
login: true,
},
},
body: true,
},
},
},
},
},
}),
).resolves.toMatchInlineSnapshot(`
{
"login": "author",
"posts": [
{
"comments": [
{
"author": {
"login": "commenter",
},
"body": "a comment",
},
],
"title": "first post",
},
],
}
`)
await expectQueryCountAtLeast({
join: 1,
query: 4,
})
expectRelationJoinToBeUsedIfRequested()
})
test('findFirstOrThrow', async () => {
await expect(
prisma.user.findFirstOrThrow({
relationLoadStrategy,
where: {
login: 'author',
},
select: {
login: true,
posts: {
select: {
title: true,
comments: {
select: {
author: {
select: {
login: true,
},
},
body: true,
},
},
},
},
},
}),
).resolves.toMatchInlineSnapshot(`
{
"login": "author",
"posts": [
{
"comments": [
{
"author": {
"login": "commenter",
},
"body": "a comment",
},
],
"title": "first post",
},
],
}
`)
await expectQueryCountAtLeast({
join: 1,
query: 4,
})
expectRelationJoinToBeUsedIfRequested()
})
test('findUnique', async () => {
await expect(
prisma.user.findUnique({
relationLoadStrategy,
where: {
login: 'author',
},
select: {
login: true,
posts: {
select: {
title: true,
comments: {
select: {
author: {
select: {
login: true,
},
},
body: true,
},
},
},
},
},
}),
).resolves.toMatchInlineSnapshot(`
{
"login": "author",
"posts": [
{
"comments": [
{
"author": {
"login": "commenter",
},
"body": "a comment",
},
],
"title": "first post",
},
],
}
`)
await expectQueryCountAtLeast({
join: 1,
query: 4,
})
expectRelationJoinToBeUsedIfRequested()
})
test('findUniqueOrThrow', async () => {
await expect(
prisma.user.findUniqueOrThrow({
relationLoadStrategy,
where: {
login: 'author',
},
select: {
login: true,
posts: {
select: {
title: true,
comments: {
select: {
author: {
select: {
login: true,
},
},
body: true,
},
},
},
},
},
}),
).resolves.toMatchInlineSnapshot(`
{
"login": "author",
"posts": [
{
"comments": [
{
"author": {
"login": "commenter",
},
"body": "a comment",
},
],
"title": "first post",
},
],
}
`)
await expectQueryCountAtLeast({
join: 1,
query: 4,
})
expectRelationJoinToBeUsedIfRequested()
})
test('create', async () => {
await expect(
prisma.user.create({
relationLoadStrategy,
data: {
login: 'reader',
comments: {
create: {
post: {
connect: { id: postId },
},
body: 'most insightful indeed!',
},
},
},
select: {
login: true,
comments: {
select: {
post: {
select: {
title: true,
},
},
body: true,
},
},
},
}),
).resolves.toMatchInlineSnapshot(`
{
"comments": [
{
"body": "most insightful indeed!",
"post": {
"title": "first post",
},
},
],
"login": "reader",
}
`)
await expectQueryCountAtLeast({
join: 6,
query: suiteConfig.provider === Providers.MONGODB ? 6 : 8,
})
expectRelationJoinToBeUsedIfRequested()
})
test('update', async () => {
await expect(
prisma.user.update({
relationLoadStrategy,
where: {
login: 'author',
},
data: {
login: 'distinguished author',
},
select: {
login: true,
posts: {
select: {
title: true,
comments: {
select: {
body: true,
},
},
},
},
},
}),
).resolves.toMatchInlineSnapshot(`
{
"login": "distinguished author",
"posts": [
{
"comments": [
{
"body": "a comment",
},
],
"title": "first post",
},
],
}
`)
await expectQueryCountAtLeast({
join: 4,
query: 6,
})
expectRelationJoinToBeUsedIfRequested()
})
test('delete', async () => {
await expect(
prisma.user.delete({
relationLoadStrategy,
where: {
login: 'author',
},
select: {
login: true,
posts: {
select: {
title: true,
comments: {
select: {
body: true,
},
},
},
},
},
}),
).resolves.toMatchInlineSnapshot(`
{
"login": "author",
"posts": [
{
"comments": [
{
"body": "a comment",
},
],
"title": "first post",
},
],
}
`)
await expectQueryCountAtLeast({
join: 4,
query: 6,
})
expectRelationJoinToBeUsedIfRequested()
})
test('upsert', async () => {
await expect(
prisma.user.upsert({
relationLoadStrategy,
where: {
login: 'commenter',
},
create: {
login: 'commenter',
},
update: {
login: 'ardent commenter',
},
select: {
login: true,
comments: {
select: {
post: {
select: {
title: true,
},
},
body: true,
},
},
},
}),
).resolves.toMatchInlineSnapshot(`
{
"comments": [
{
"body": "a comment",
"post": {
"title": "first post",
},
},
],
"login": "ardent commenter",
}
`)
await expectQueryCountAtLeast({
join: 5,
query: suiteConfig.provider === Providers.MONGODB ? 6 : 7,
})
expectRelationJoinToBeUsedIfRequested()
})
test('create with no relation selection', async () => {
await expect(
prisma.user.create({
// Doesn't influence the generated queries in this case as we only select scalars.
relationLoadStrategy,
data: {
login: 'reader',
comments: {
create: {
post: {
connect: { id: postId },
},
body: 'most insightful indeed!',
},
},
},
select: {
login: true,
},
}),
).resolves.toMatchInlineSnapshot(`
{
"login": "reader",
}
`)
// Query count is equal in this test since no relations are loaded.
await expectQueryCountAtLeast({
join: 6,
query: suiteConfig.provider === Providers.MONGODB ? 4 : 6,
})
expectRelationJoinNotToBeUsed()
})
},
)
},
{
optOut: {
from: providersNotSupportingRelationJoins,
reason: "Doesn't support relation joins",
},
},
)