ts-patch-mongoose
Version:
Patch history & events for mongoose models
372 lines (305 loc) • 12.5 kB
text/typescript
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
import mongoose, { model, Types } from 'mongoose'
import em from '../src/em'
import { patchHistoryPlugin } from '../src/index'
import { HistoryModel } from '../src/model'
import { isMongooseLessThan7 } from '../src/version'
import { USER_CREATED } from './constants/events'
import server from './mongo/server'
import { type User, UserSchema } from './schemas/User'
vi.mock('../src/em', () => ({ default: { emit: vi.fn((event: string, data: Record<string, unknown>) => console.log(event, data)) } }))
describe('plugin - event created & patch history disabled', () => {
const instance = server('plugin-event-created')
UserSchema.plugin(patchHistoryPlugin, {
eventCreated: USER_CREATED,
patchHistoryDisabled: true,
})
const UserModel = model<User>('User', UserSchema)
beforeAll(async () => {
await instance.create()
})
afterAll(async () => {
await instance.destroy()
})
beforeEach(async () => {
await mongoose.connection.collection('users').deleteMany({})
await mongoose.connection.collection('history').deleteMany({})
})
afterEach(() => {
vi.restoreAllMocks()
})
describe('normal cases', () => {
it('should save() and emit one create event', async () => {
const john = new UserModel({ name: 'John', role: 'user' })
await john.save()
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(1)
expect(em.emit).toHaveBeenCalledWith(USER_CREATED, {
doc: expect.objectContaining({
_id: john._id,
name: john.name,
role: john.role,
createdAt: john.createdAt,
updatedAt: john.updatedAt,
}),
})
// Check if the document is saved
const found = await UserModel.findOne({})
expect(found).not.toBeNull()
expect(found?.name).toBe('John')
expect(found?.role).toBe('user')
})
it('should create() and emit one create event', async () => {
const user = await UserModel.create({ name: 'John', role: 'user' })
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(1)
expect(em.emit).toHaveBeenCalledWith(USER_CREATED, {
doc: expect.objectContaining({
_id: user._id,
name: user.name,
role: user.role,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
}),
})
// Check if the document is saved
const found = await UserModel.findOne({})
expect(found).not.toBeNull()
expect(found?.name).toBe('John')
expect(found?.role).toBe('user')
})
it('should insertMany() and emit three create events', async () => {
const users = await UserModel.insertMany(
[
{ name: 'John', role: 'user' },
{ name: 'Alice', role: 'user' },
{ name: 'Bob', role: 'user' },
],
{ ordered: true },
)
const [john, alice, bob] = users
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(3)
expect(em.emit).toHaveBeenNthCalledWith(1, USER_CREATED, {
doc: expect.objectContaining({
_id: john._id,
name: john.name,
role: john.role,
createdAt: john.createdAt,
updatedAt: john.updatedAt,
}),
})
expect(em.emit).toHaveBeenNthCalledWith(2, USER_CREATED, {
doc: expect.objectContaining({
_id: alice._id,
name: alice.name,
role: alice.role,
createdAt: alice.createdAt,
updatedAt: alice.updatedAt,
}),
})
expect(em.emit).toHaveBeenNthCalledWith(3, USER_CREATED, {
doc: expect.objectContaining({
_id: bob._id,
name: bob.name,
role: bob.role,
createdAt: bob.createdAt,
updatedAt: bob.updatedAt,
}),
})
// Check if the documents are saved
const found = await UserModel.find({})
expect(found).toHaveLength(3)
const [foundJohn, foundAlice, foundBob] = found
expect(foundJohn.name).toBe('John')
expect(foundJohn.role).toBe('user')
expect(foundAlice.name).toBe('Alice')
expect(foundAlice.role).toBe('user')
expect(foundBob.name).toBe('Bob')
expect(foundBob.role).toBe('user')
})
})
describe('upsert cases', () => {
it('should update() + upsert and emit one create event', async () => {
if (isMongooseLessThan7) {
// @ts-expect-error update() not available in Mongoose v6 and below
await UserModel.update({ name: 'John' }, { name: 'John', role: 'admin' }, { upsert: true })
} else {
await UserModel.findOneAndUpdate({ name: 'John' }, { name: 'John', role: 'admin' }, { upsert: true })
}
const user = await UserModel.findOne({ name: 'John', role: 'admin' })
expect(user).not.toBeNull()
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(1)
expect(em.emit).toHaveBeenCalledWith(USER_CREATED, {
doc: expect.objectContaining({
_id: user?._id,
name: user?.name,
role: user?.role,
// Upsert does not set createdAt and updatedAt
}),
})
// Check if the document is saved
const found = await UserModel.findOne({})
expect(found).not.toBeNull()
expect(found?.name).toBe('John')
expect(found?.role).toBe('admin')
})
it('should updateOne() + upsert and emit one create event', async () => {
await UserModel.updateOne({ name: 'John' }, { name: 'John', role: 'admin' }, { upsert: true })
const user = await UserModel.findOne({ name: 'John', role: 'admin' })
expect(user).not.toBeNull()
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(1)
expect(em.emit).toHaveBeenCalledWith(USER_CREATED, {
doc: expect.objectContaining({
_id: user?._id,
name: user?.name,
role: user?.role,
// Upsert does not set createdAt and updatedAt
}),
})
// Check if the document is saved
const found = await UserModel.findOne({})
expect(found).not.toBeNull()
expect(found?.name).toBe('John')
expect(found?.role).toBe('admin')
})
it('should replaceOne() + upsert and emit one create event', async () => {
await UserModel.replaceOne({ name: 'John' }, { name: 'John', role: 'admin' }, { upsert: true })
const user = await UserModel.findOne({ name: 'John', role: 'admin' })
expect(user).not.toBeNull()
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(1)
expect(em.emit).toHaveBeenCalledWith(USER_CREATED, {
doc: expect.objectContaining({
_id: user?._id,
name: user?.name,
role: user?.role,
// Upsert does not set createdAt and updatedAt
}),
})
// Check if the document is saved
const found = await UserModel.findOne({})
expect(found).not.toBeNull()
expect(found?.name).toBe('John')
expect(found?.role).toBe('admin')
})
it('should updateMany() + upsert and emit one create event', async () => {
await UserModel.updateMany({ name: { $in: ['John', 'Alice', 'Bob'] } }, { name: 'Steve', role: 'admin' }, { upsert: true })
const users = await UserModel.findOne({ name: 'Steve', role: 'admin' })
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(1)
expect(em.emit).toHaveBeenNthCalledWith(1, USER_CREATED, {
doc: expect.objectContaining({
_id: users?._id,
name: users?.name,
role: users?.role,
// Upsert does not set createdAt and updatedAt
}),
})
// Check if the document is saved
const found = await UserModel.findById(users?._id)
expect(found).not.toBeNull()
expect(found?.name).toBe('Steve')
expect(found?.role).toBe('admin')
})
it('should findOneAndUpdate() + upsert and emit one create event', async () => {
await UserModel.findOneAndUpdate({ name: 'John' }, { name: 'John', role: 'admin' }, { upsert: true })
const user = await UserModel.findOne({ name: 'John', role: 'admin' })
expect(user).not.toBeNull()
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(1)
expect(em.emit).toHaveBeenCalledWith(USER_CREATED, {
doc: expect.objectContaining({
_id: user?._id,
name: user?.name,
role: user?.role,
// Upsert does not set createdAt and updatedAt
}),
})
// Check if the document is saved
const found = await UserModel.findOne({})
expect(found).not.toBeNull()
expect(found?.name).toBe('John')
expect(found?.role).toBe('admin')
})
it('should findOneAndReplace() + upsert and emit one create event', async () => {
await UserModel.findOneAndReplace({ name: 'John' }, { name: 'John', role: 'admin' }, { upsert: true })
const user = await UserModel.findOne({ name: 'John', role: 'admin' })
expect(user).not.toBeNull()
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(1)
expect(em.emit).toHaveBeenCalledWith(USER_CREATED, {
doc: expect.objectContaining({
_id: user?._id,
name: user?.name,
role: user?.role,
// Upsert does not set createdAt and updatedAt
}),
})
// Check if the document is saved
const found = await UserModel.findOne({})
expect(found).not.toBeNull()
expect(found?.name).toBe('John')
expect(found?.role).toBe('admin')
})
it('should findByIdAndUpdate() + upsert and emit one create event', async () => {
const _id = new Types.ObjectId()
await UserModel.findByIdAndUpdate(_id, { name: 'John', role: 'admin' }, { upsert: true })
const user = await UserModel.findOne({ name: 'John', role: 'admin' })
expect(user).not.toBeNull()
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(1)
expect(em.emit).toHaveBeenCalledWith(USER_CREATED, {
doc: expect.objectContaining({
_id: user?._id,
name: user?.name,
role: user?.role,
}),
})
// Check if the document is saved
const found = await UserModel.findOne({})
expect(found).not.toBeNull()
expect(found?.name).toBe('John')
expect(found?.role).toBe('admin')
})
it('should findOneAndUpdate() with $set + upsert and emit one create event', async () => {
const _id = new Types.ObjectId()
const john = await UserModel.create({ _id, name: 'John', role: 'admin' })
if (isMongooseLessThan7) {
// @ts-expect-error update() not available in Mongoose v6 and below
await UserModel.update({ name: 'Alex', role: 'user' }, { $set: { name: 'Alex', role: 'user' } }, { upsert: true, setDefaultsOnInsert: false, overwriteDiscriminatorKey: true }).exec()
} else {
await UserModel.findOneAndUpdate({ name: 'Alex', role: 'user' }, { $set: { name: 'Alex', role: 'user' } }, { upsert: true, setDefaultsOnInsert: false, overwriteDiscriminatorKey: true }).exec()
}
const alex = await UserModel.findOne({ name: 'Alex', role: 'user' })
expect(alex).not.toBeNull()
const history = await HistoryModel.find({})
expect(history).toHaveLength(0)
expect(em.emit).toHaveBeenCalledTimes(2)
expect(em.emit).toHaveBeenCalledWith(USER_CREATED, {
doc: expect.objectContaining({
name: john?.name,
role: john?.role,
}),
})
expect(em.emit).toHaveBeenCalledWith(USER_CREATED, {
doc: expect.objectContaining({
name: alex?.name,
role: alex?.role,
}),
})
})
})
})