tests_m-to-n-MongoDB.ts•9.72 kB
import { Providers } from '../_utils/providers'
import { checkIfEmpty } from '../_utils/relationMode/checkIfEmpty'
import testMatrix from './_matrix'
/* eslint-disable @typescript-eslint/no-unused-vars, jest/no-identical-title */
// @ts-ignore this is just for type checks
declare let prisma: import('./generated/prisma/client').PrismaClient
// @ts-ignore
const describeIf = (condition: boolean) => (condition ? describe : describe.skip)
// m:n relation (MongoDB database)
async function createXPostsWith2CategoriesMongoDB({ count, postModel }: { count: number; postModel: string }) {
  const prismaPromises: any = []
  for (let i = 0; i < count; i++) {
    // We want to start at 1
    const id = (i + 1).toString()
    const prismaPromise = prisma[postModel].create({
      data: {
        id: id,
        categories: {
          create: [
            {
              id: `${id}-cat-a`,
            },
            {
              id: `${id}-cat-b`,
            },
          ],
        },
      },
      include: {
        categories: true,
      },
    })
    prismaPromises.push(prismaPromise)
  }
  return await prisma.$transaction(prismaPromises)
}
// If no change
const expectedFindManyPostModelIfNoChange = [
  {
    id: '1',
    published: null,
    categoryIDs: ['1-cat-a', '1-cat-b'],
  },
  {
    id: '2',
    published: null,
    categoryIDs: ['2-cat-a', '2-cat-b'],
  },
]
const expectedFindManyCategoryModelIfNoChange = [
  {
    id: '1-cat-a',
    published: null,
    postIDs: ['1'],
  },
  {
    id: '1-cat-b',
    published: null,
    postIDs: ['1'],
  },
  {
    id: '2-cat-a',
    published: null,
    postIDs: ['2'],
  },
  {
    id: '2-cat-b',
    published: null,
    postIDs: ['2'],
  },
]
testMatrix.setupTestSuite(
  (suiteConfig, suiteMeta) => {
    const isMongoDB = suiteConfig.provider === Providers.MONGODB
    /**
     * m:n relationship
     */
    describeIf(isMongoDB)('m:n mandatory (explicit) - MongoDB', () => {
      const postModel = 'PostManyToMany'
      const categoryModel = 'CategoryManyToMany'
      beforeEach(async () => {
        const prismaPromises = [prisma[postModel].deleteMany(), prisma[categoryModel].deleteMany()]
        await prisma.$transaction(prismaPromises)
      })
      describe('[create]', () => {
        // Only this test and the one below pass
        test('[create] category alone should succeed', async () => {
          await prisma[categoryModel].create({
            data: {
              id: '1',
            },
          })
          expect(await prisma[categoryModel].findMany()).toEqual([
            {
              id: '1',
              published: null,
              postIDs: [],
            },
          ])
        })
        // Only this test and the one above pass
        test('[create] post alone should succeed', async () => {
          await prisma[postModel].create({
            data: {
              id: '1',
            },
          })
          expect(await prisma[postModel].findMany()).toEqual([
            {
              id: '1',
              published: null,
              categoryIDs: [],
            },
          ])
        })
        test.failing(
          '[create] create post [nested] [create] categories [nested] [create] category should succeed',
          async () => {
            await prisma[postModel].create({
              data: {
                id: '1',
                categories: {
                  create: [
                    {
                      id: '1',
                    },
                  ],
                },
              },
            })
            expect(await prisma[postModel].findMany({ orderBy: { id: 'asc' } })).toEqual([
              {
                id: '1',
                published: null,
                categoryIDs: ['1'],
              },
            ])
            expect(await prisma[categoryModel].findMany()).toEqual([
              {
                id: '1',
                published: null,
                postIDs: ['1'],
              },
            ])
          },
        )
      })
      describe('[update]', () => {
        beforeEach(async () => {
          await checkIfEmpty(categoryModel, postModel)
          await createXPostsWith2CategoriesMongoDB({
            count: 2,
            postModel,
          })
        })
        // Note: it's not possible on MongoDB to mutate _id, it is immutable
        test.failing('[update] id (_id) should throw at runtime because id field is read-only/immutable', async () => {
          await expect(
            prisma[postModel].update({
              where: {
                id: '1',
              },
              data: {
                // This would show a type error
                id: 'new id',
              },
            }),
            // Runtime error
          ).rejects.toThrow('Unknown arg `id` in data.id for type PostManyToManyUpdateInput. Available args:')
          expect(await prisma[postModel].findMany({ orderBy: { id: 'asc' } })).toEqual(
            expectedFindManyPostModelIfNoChange,
          )
          expect(
            await prisma[categoryModel].findMany({
              orderBy: { id: 'asc' },
            }),
          ).toEqual(expectedFindManyCategoryModelIfNoChange)
        })
        test.failing('[update] (post) optional boolean field should succeed', async () => {
          await prisma[postModel].update({
            where: {
              id: '1',
            },
            data: {
              published: true,
            },
          })
          expect(await prisma[postModel].findMany({ orderBy: { id: 'asc' } })).toEqual([
            {
              id: '1',
              // The update
              published: true,
              categoryIDs: ['1-cat-a', '1-cat-b'],
            },
            {
              id: '2',
              published: null,
              categoryIDs: ['2-cat-a', '2-cat-b'],
            },
          ])
          expect(
            await prisma[categoryModel].findMany({
              orderBy: { id: 'asc' },
            }),
          ).toEqual(expectedFindManyCategoryModelIfNoChange)
        })
        test.failing('[update] (category): optional boolean field should succeed', async () => {
          await prisma[categoryModel].update({
            where: {
              id: '1-cat-a',
            },
            data: {
              published: true,
            },
          })
          expect(await prisma[postModel].findMany({ orderBy: { id: 'asc' } })).toEqual(
            expectedFindManyPostModelIfNoChange,
          )
          expect(
            await prisma[categoryModel].findMany({
              orderBy: { id: 'asc' },
            }),
          ).toEqual([
            {
              id: '1-cat-a',
              // The update
              published: true,
              postIDs: ['1'],
            },
            {
              id: '1-cat-b',
              published: null,
              postIDs: ['1'],
            },
            {
              id: '2-cat-a',
              published: null,
              postIDs: ['2'],
            },
            {
              id: '2-cat-b',
              published: null,
              postIDs: ['2'],
            },
          ])
        })
      })
      describe('[delete]', () => {
        beforeEach(async () => {
          await checkIfEmpty(categoryModel, postModel)
          await createXPostsWith2CategoriesMongoDB({
            count: 2,
            postModel,
          })
        })
        // Note :Referential actions on two-way embedded many-to-many relations are not supported (schema validation error when added)
        // Which means everything has the same following behaviour:
        describe(`onDelete:`, () => {
          test.failing('[delete] post should succeed', async () => {
            await prisma[postModel].delete({
              where: { id: '1' },
            })
            expect(await prisma[postModel].findMany({ orderBy: { id: 'asc' } })).toEqual([
              {
                id: '2',
                published: null,
                categoryIDs: ['2-cat-a', '2-cat-b'],
              },
            ])
            expect(
              await prisma[categoryModel].findMany({
                orderBy: { id: 'asc' },
              }),
            ).toEqual(expectedFindManyCategoryModelIfNoChange)
          })
          test.failing('[delete] category should succeed', async () => {
            await prisma[categoryModel].delete({
              where: { id: '1-cat-a' },
            })
            expect(await prisma[postModel].findMany({ orderBy: { id: 'asc' } })).toEqual(
              expectedFindManyPostModelIfNoChange,
            )
            expect(
              await prisma[categoryModel].findMany({
                orderBy: { id: 'asc' },
              }),
            ).toEqual([
              {
                id: '1-cat-b',
                published: null,
                postIDs: ['1'],
              },
              {
                id: '2-cat-a',
                published: null,
                postIDs: ['2'],
              },
              {
                id: '2-cat-b',
                published: null,
                postIDs: ['2'],
              },
            ])
          })
        })
      })
    })
  },
  // Use `optOut` to opt out from testing the default selected providers
  // otherwise the suite will require all providers to be specified.
  {
    optOut: {
      from: [
        Providers.MONGODB,
        Providers.SQLSERVER,
        Providers.MYSQL,
        Providers.POSTGRESQL,
        Providers.COCKROACHDB,
        Providers.SQLITE,
      ],
      reason: 'Only testing xyz provider(s) so opting out of xxx',
    },
  },
)