@prisma-extensions/soft-delete
Version:
Soft Delete can be used to hide records instead of deleting them, making them recoverable later.
448 lines (359 loc) • 11.7 kB
text/typescript
import { faker } from '@faker-js/faker'
import { PrismaClient, type Post, type User } from '@prisma/client'
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest'
import { applyExtension } from './soft-delete'
const prisma = new PrismaClient()
describe('Soft Delete', () => {
afterAll(async () => {
await prisma.post.deleteMany()
await prisma.user.deleteMany()
await prisma.$disconnect()
})
describe('All Models', () => {
const xprisma = prisma.$extends(applyExtension())
afterEach(async () => {
await xprisma.post.deleteMany()
await xprisma.user.deleteMany()
})
describe('Mutations', () => {
test('.softDelete', async () => {
const user = await xprisma.user.create({
data: {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
},
})
const deleteOp = await xprisma.user.softDelete({
where: { id: user.id },
})
expect(deleteOp).toEqual(
expect.objectContaining({
...user,
deletedAt: expect.any(Date),
updatedAt: expect.any(Date),
}),
)
})
test('.softDeleteMany', async () => {
const ids: number[] = []
const createUser = async () => {
const post = await xprisma.user.create({
data: {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
},
})
ids.push(post.id)
}
await Promise.allSettled([createUser(), createUser(), createUser()])
const deleteOp = await xprisma.user.softDeleteMany()
expect(deleteOp.count).toEqual(3)
const findOp = await xprisma.user.findMany({
where: { id: { in: ids } },
})
expect(findOp.length).toEqual(0)
})
test('.restore', async () => {
const user = await xprisma.user.create({
data: {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
deletedAt: new Date(),
},
})
const restoredUser = await xprisma.user.softRestore({
where: { id: user.id },
})
expect(restoredUser.deletedAt).toBeNull()
})
test('.restoreMany', async () => {
const deletedAt = new Date()
const ids: number[] = []
const createUser = async () => {
const user = await xprisma.user.create({
data: {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
deletedAt,
},
})
ids.push(user.id)
}
await Promise.allSettled([createUser(), createUser(), createUser()])
const deleteOp = await xprisma.user.softRestoreMany()
expect(deleteOp.count).toEqual(3)
const findOp = await xprisma.user.findMany({
where: { id: { in: ids } },
})
expect(findOp.map((p) => p.id).sort()).toEqual(ids.sort())
})
})
describe('Queries', () => {
let post: Post
beforeAll(async () => {
const author = await prisma.user.create({
data: {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
},
})
post = await xprisma.post.create({
data: {
authorId: author.id,
content: faker.lorem.paragraphs(3),
title: faker.lorem.sentence(),
deletedAt: new Date(),
commentCount: 5,
},
})
})
test('.aggregate', async () => {
const { _avg } = await xprisma.post.aggregate({
_avg: {
commentCount: true,
},
})
expect(_avg.commentCount).toBeNull()
})
test('.count', async () => {
const count = await xprisma.post.count()
expect(count).toBe(0)
})
test('.findFirst', async () => {
const res = await xprisma.post.findFirst({ where: { id: post.id } })
expect(res).toBeNull()
})
test.todo('.findFirstOrThrow', async () => {
// Prisma Client does not invoke the extended functions
await expect(
prisma.post.findFirstOrThrow({
where: { id: post.id },
}),
).rejects.toThrowError()
})
test.todo('.findMany', async () => {
// Prisma Client does not invoke the extended functions
const res = await prisma.post.findMany({
where: { id: { in: post.id } },
})
expect(res).toStrictEqual([])
})
test('.groupBy', async () => {
const res = await xprisma.post.groupBy({
by: ['authorId'],
_count: {
authorId: true,
},
})
expect(res).toStrictEqual([])
})
})
})
describe('Selected Models', () => {
let author: User
const xprisma = prisma.$extends(applyExtension({ models: ['post'] }))
beforeAll(async () => {
author = await prisma.user.create({
data: {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
},
})
})
describe('Mutations', () => {
afterEach(async () => {
await xprisma.post.deleteMany()
})
describe('Enabled', () => {
test('.softDelete', async () => {
const post = await xprisma.post.create({
data: {
authorId: author.id,
content: faker.lorem.paragraphs(3),
title: faker.lorem.sentence(),
},
})
const deleteOp = await xprisma.post.softDelete({
where: { id: post.id },
})
expect(deleteOp).toEqual(
expect.objectContaining({
...post,
deletedAt: expect.any(Date),
updatedAt: expect.any(Date),
}),
)
const findOp = await xprisma.post.findFirst({
where: { id: post.id },
})
expect(findOp).toBeNull()
})
test('.softDeleteMany', async () => {
const ids: number[] = []
const createPost = async () => {
const post = await xprisma.post.create({
data: {
authorId: author.id,
content: faker.lorem.paragraphs(3),
title: faker.lorem.sentence(),
},
})
ids.push(post.id)
}
await Promise.allSettled([createPost(), createPost(), createPost()])
const deleteOp = await xprisma.post.softDeleteMany()
expect(deleteOp.count).toEqual(3)
const findOp = await xprisma.post.findMany({
where: { id: { in: ids } },
})
expect(findOp.length).toEqual(0)
})
test('.restore', async () => {
const post = await xprisma.post.create({
data: {
authorId: author.id,
content: faker.lorem.paragraphs(3),
deletedAt: new Date(),
title: faker.lorem.sentence(),
},
})
const restoredPost = await xprisma.post.softRestore({
where: { id: post.id },
})
expect(restoredPost.deletedAt).toBeNull()
})
test('.restoreMany', async () => {
const deletedAt = new Date()
const ids: number[] = []
const createPost = async () => {
const post = await xprisma.post.create({
data: {
authorId: author.id,
content: faker.lorem.paragraphs(3),
deletedAt,
title: faker.lorem.sentence(),
},
})
ids.push(post.id)
}
await Promise.allSettled([createPost(), createPost(), createPost()])
const deleteOp = await xprisma.post.softRestoreMany()
expect(deleteOp.count).toEqual(3)
const findOp = await xprisma.post.findMany({
where: { id: { in: ids } },
})
expect(findOp.map((p) => p.id).sort()).toEqual(ids.sort())
})
})
})
describe('Queries', () => {
describe('Modified ', () => {
let post: Post
beforeAll(async () => {
post = await xprisma.post.create({
data: {
authorId: author.id,
content: faker.lorem.paragraphs(3),
title: faker.lorem.sentence(),
deletedAt: new Date(),
commentCount: 5,
},
})
})
test('.aggregate', async () => {
const { _avg } = await xprisma.post.aggregate({
_avg: {
commentCount: true,
},
})
expect(_avg.commentCount).toBeNull()
})
test('.count', async () => {
const count = await xprisma.post.count()
expect(count).toBe(0)
})
test('.findFirst', async () => {
const res = await xprisma.post.findFirst({ where: { id: post.id } })
expect(res).toBeNull()
})
test.todo('.findFirstOrThrow', async () => {
// Prisma Client does not invoke the extended functions
await expect(
prisma.post.findFirstOrThrow({
where: { id: post.id },
}),
).rejects.toThrowError()
})
test.todo('.findMany', async () => {
// Prisma Client does not invoke the extended functions
const res = await prisma.post.findMany({
where: { id: { in: post.id } },
})
expect(res).toStrictEqual([])
})
test('.groupBy', async () => {
const res = await xprisma.post.groupBy({
by: ['authorId'],
_count: {
authorId: true,
},
})
expect(res).toStrictEqual([])
})
})
describe('Unmodified', () => {
test('.aggregate', async () => {
const { _avg } = await xprisma.user.aggregate({
_avg: {
id: true,
},
})
expect(_avg.id).toEqual(author.id)
})
test('.count', async () => {
const count = await xprisma.user.count()
expect(count).toBe(1)
})
test('.findFirst', async () => {
const res = await xprisma.user.findFirst({ where: { id: author.id } })
expect(res).toStrictEqual(author)
})
test('.findFirstOrThrow - resolves', async () => {
const res = await prisma.user.findFirstOrThrow({
where: { id: author.id },
})
expect(res).toStrictEqual(author)
})
test('.findFirstOrThrow - throws', async () => {
await expect(
prisma.user.findFirstOrThrow({
where: { firstName: 'no-name' },
}),
).rejects.toThrowError()
})
test('.findMany', async () => {
const res = await prisma.user.findMany({
where: { id: { in: author.id } },
})
expect(res).toStrictEqual([author])
})
test('.groupBy', async () => {
const res = await xprisma.user.groupBy({
by: ['id'],
_count: {
id: true,
},
})
expect(res).toStrictEqual([
{
_count: {
id: 1,
},
id: author.id,
},
])
})
})
})
})
})