UNPKG

ts-patch-mongoose

Version:
504 lines (438 loc) 16.6 kB
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_UPDATED } from './constants/events' import server from './mongo/server' import { type User, UserSchema } from './schemas/User' vi.mock('../src/em', () => ({ default: { emit: vi.fn() } })) describe('plugin - event updated & patch history disabled', () => { const instance = server('plugin-event-updated') UserSchema.plugin(patchHistoryPlugin, { eventUpdated: USER_UPDATED, patchHistoryDisabled: true, omit: ['createdAt', 'updatedAt'], }) 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(async () => { vi.resetAllMocks() }) it('should save() and emit one update event', async () => { await UserModel.create({ name: 'Bob', role: 'user' }) const user = new UserModel({ name: 'John', role: 'user' }) const created = await user.save() user.name = 'John Doe' const updated = await user.save() const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(1) expect(em.emit).toHaveBeenCalledWith(USER_UPDATED, { oldDoc: expect.objectContaining({ __v: 0, _id: created._id, name: 'John', role: 'user', createdAt: created.createdAt, updatedAt: created.createdAt, }), doc: expect.objectContaining({ __v: 0, _id: updated._id, name: 'John Doe', role: 'user', createdAt: created.createdAt, updatedAt: updated.updatedAt, }), patch: expect.arrayContaining([ { op: 'test', path: '/name', value: 'John' }, { op: 'replace', path: '/name', value: 'John Doe' }, ]), }) // Confirm that the document is updated const users = await UserModel.find({}) expect(users).toHaveLength(2) const [bob, john] = users expect(bob.name).toBe('Bob') expect(john.name).toBe('John Doe') }) it('should update() and emit three update event', async () => { await UserModel.create( [ { name: 'Alice', role: 'user' }, { name: 'Bob', role: 'user' }, { name: 'John', role: 'user' }, ], { ordered: true }, ) if (isMongooseLessThan7) { // @ts-expect-error not available in Mongoose 6 and below await UserModel.update({ role: 'user' }, { role: 'manager' }) } else { await UserModel.updateMany({ role: 'user' }, { role: 'manager' }) } const users = await UserModel.find({ role: 'manager' }) expect(users).toHaveLength(3) const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(3) // Confirm that the document is updated const updated = await UserModel.find({}).sort({ name: 1 }) expect(updated).toHaveLength(3) const [alice, bob, john] = updated expect(alice.role).toBe('manager') expect(bob.role).toBe('manager') expect(john.role).toBe('manager') }) it('should updateOne() and emit one update event', async () => { await UserModel.create( [ { name: 'Alice', role: 'user' }, { name: 'Bob', role: 'user' }, { name: 'John', role: 'user' }, ], { ordered: true }, ) await UserModel.updateOne({ name: 'Bob' }, { role: 'manager' }) const users = await UserModel.find({ role: 'manager' }) expect(users).toHaveLength(1) const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(1) // Confirm that the document is updated const updated = await UserModel.find({}).sort({ name: 1 }) expect(updated).toHaveLength(3) const [alice, bob, john] = updated expect(alice.role).toBe('user') expect(bob.role).toBe('manager') expect(john.role).toBe('user') }) it('should replaceOne() and emit two update event', async () => { await UserModel.create( [ { name: 'Alice', role: 'user' }, { name: 'Bob', role: 'user' }, { name: 'John', role: 'user' }, ], { ordered: true }, ) await UserModel.replaceOne({ name: 'Bob' }, { name: 'Bob Doe', role: 'manager' }) const users = await UserModel.find({ role: 'manager' }) expect(users).toHaveLength(1) const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(1) expect(em.emit).toHaveBeenCalledWith(USER_UPDATED, { oldDoc: expect.objectContaining({ __v: 0, _id: expect.any(Types.ObjectId), name: 'Bob', role: 'user', createdAt: expect.any(Date), updatedAt: expect.any(Date), }), doc: expect.objectContaining({ __v: 0, _id: expect.any(Types.ObjectId), name: 'Bob Doe', role: 'manager', createdAt: expect.any(Date), updatedAt: expect.any(Date), }), patch: expect.arrayContaining([ { op: 'test', path: '/name', value: 'Bob' }, { op: 'replace', path: '/name', value: 'Bob Doe' }, { op: 'test', path: '/role', value: 'user' }, { op: 'replace', path: '/role', value: 'manager' }, ]), }) // Confirm that the document is updated const updated = await UserModel.find({}).sort({ name: 1 }) expect(updated).toHaveLength(3) const [alice, bob, john] = updated expect(alice.role).toBe('user') expect(bob.role).toBe('manager') expect(john.role).toBe('user') }) it('should updateMany() and emit two update event', async () => { await UserModel.create( [ { name: 'Alice', role: 'user' }, { name: 'Bob', role: 'user' }, { name: 'John', role: 'user' }, ], { ordered: true }, ) await UserModel.updateMany({ role: 'user' }, { role: 'manager' }) const users = await UserModel.find({ role: 'manager' }) expect(users).toHaveLength(3) const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(3) // Confirm that the document is updated const updated = await UserModel.find({}).sort({ name: 1 }) expect(updated).toHaveLength(3) const [alice, bob, john] = updated expect(alice.role).toBe('manager') expect(bob.role).toBe('manager') expect(john.role).toBe('manager') }) it('should findOneAndUpdate() and emit one update event', async () => { await UserModel.create({ name: 'Bob', role: 'user' }) const created = await UserModel.create({ name: 'John', role: 'user' }) await UserModel.findOneAndUpdate({ _id: created._id }, { name: 'John Doe', role: 'manager' }) const updated = await UserModel.findById(created._id).exec() expect(updated).not.toBeNull() const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(1) expect(em.emit).toHaveBeenCalledWith(USER_UPDATED, { oldDoc: expect.objectContaining({ __v: 0, _id: created._id, name: created.name, role: created.role, createdAt: created.createdAt, }), doc: expect.objectContaining({ __v: 0, _id: updated?._id, name: updated?.name, role: updated?.role, createdAt: created.createdAt, }), patch: expect.arrayContaining([ { op: 'test', path: '/role', value: 'user' }, { op: 'replace', path: '/role', value: 'manager' }, { op: 'test', path: '/name', value: 'John' }, { op: 'replace', path: '/name', value: 'John Doe' }, ]), }) // Confirm that the document is updated expect(updated?.name).toBe('John Doe') expect(updated?.role).toBe('manager') }) it('should findOneAndReplace() and emit one update event', async () => { await UserModel.create({ name: 'Bob', role: 'user' }) const created = await UserModel.create({ name: 'John', role: 'user' }) await UserModel.findOneAndReplace({ _id: created._id }, { name: 'John Doe', role: 'manager' }) const updated = await UserModel.findById(created._id).exec() expect(updated).not.toBeNull() const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(1) expect(em.emit).toHaveBeenCalledWith(USER_UPDATED, { oldDoc: expect.objectContaining({ __v: 0, _id: created._id, name: created.name, role: created.role, createdAt: created.createdAt, updatedAt: created.createdAt, }), doc: expect.objectContaining({ __v: 0, _id: updated?._id, name: updated?.name, role: updated?.role, createdAt: updated?.createdAt, updatedAt: updated?.updatedAt, }), patch: expect.arrayContaining([ { op: 'test', path: '/name', value: 'John' }, { op: 'replace', path: '/name', value: 'John Doe' }, { op: 'test', path: '/role', value: 'user' }, { op: 'replace', path: '/role', value: 'manager' }, ]), }) // Confirm that the document is updated expect(updated?.name).toBe('John Doe') expect(updated?.role).toBe('manager') }) it('should findByIdAndUpdate() and emit one update event', async () => { const created = await UserModel.create({ name: 'Bob', role: 'user' }) await UserModel.findByIdAndUpdate(created._id, { name: 'John Doe', role: 'manager' }) const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(1) expect(em.emit).toHaveBeenCalledWith(USER_UPDATED, { oldDoc: expect.objectContaining({ __v: 0, _id: created._id, name: created.name, role: created.role, createdAt: created.createdAt, }), doc: expect.objectContaining({ __v: 0, _id: created._id, name: 'John Doe', role: 'manager', createdAt: created.createdAt, }), patch: expect.arrayContaining([ { op: 'test', path: '/name', value: 'Bob' }, { op: 'replace', path: '/name', value: 'John Doe' }, { op: 'test', path: '/role', value: 'user' }, { op: 'replace', path: '/role', value: 'manager' }, ]), }) // Confirm that the document is updated const updated = await UserModel.findById(created._id).exec() expect(updated?.name).toBe('John Doe') expect(updated?.role).toBe('manager') }) it('should update and emit one update event', async () => { await UserModel.create({ name: 'Bob', role: 'user' }) const created = await UserModel.create({ name: 'John', role: 'user' }) await UserModel.updateOne({ _id: created._id }, { name: 'John Doe', role: 'manager' }) const updated = await UserModel.findById(created._id).exec() expect(updated).not.toBeNull() const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(1) expect(em.emit).toHaveBeenCalledWith(USER_UPDATED, { oldDoc: expect.objectContaining({ __v: 0, _id: created._id, name: created.name, role: created.role, createdAt: created.createdAt, }), doc: expect.objectContaining({ __v: 0, _id: updated?._id, name: updated?.name, role: updated?.role, createdAt: created.createdAt, }), patch: expect.arrayContaining([ { op: 'test', path: '/role', value: 'user' }, { op: 'replace', path: '/role', value: 'manager' }, { op: 'test', path: '/name', value: 'John' }, { op: 'replace', path: '/name', value: 'John Doe' }, ]), }) // Confirm that the document is updated expect(updated?.name).toBe('John Doe') expect(updated?.role).toBe('manager') }) it('should updateMany and emit two update events', async () => { const created1 = await UserModel.create({ name: 'John', role: 'user' }) const created2 = await UserModel.create({ name: 'Bob', role: 'user' }) await UserModel.updateMany({}, { name: 'John Doe', role: 'manager' }) const updated1 = await UserModel.findById(created1._id).exec() expect(updated1).not.toBeNull() const updated2 = await UserModel.findById(created2._id).exec() expect(updated2).not.toBeNull() const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(2) expect(em.emit).toHaveBeenCalledWith(USER_UPDATED, { oldDoc: expect.objectContaining({ __v: 0, _id: created1._id, name: created1.name, role: created1.role, createdAt: created1.createdAt, }), doc: expect.objectContaining({ __v: 0, _id: updated1?._id, name: updated1?.name, role: updated1?.role, createdAt: created1.createdAt, }), patch: expect.arrayContaining([ { op: 'test', path: '/role', value: 'user' }, { op: 'replace', path: '/role', value: 'manager' }, { op: 'test', path: '/name', value: 'John' }, { op: 'replace', path: '/name', value: 'John Doe' }, ]), }) expect(em.emit).toHaveBeenCalledWith(USER_UPDATED, { oldDoc: expect.objectContaining({ __v: 0, _id: created2._id, name: created2.name, role: created2.role, createdAt: created2.createdAt, }), doc: expect.objectContaining({ __v: 0, _id: updated2?._id, name: updated2?.name, role: updated2?.role, createdAt: created2.createdAt, }), patch: expect.arrayContaining([ { op: 'test', path: '/role', value: 'user' }, { op: 'replace', path: '/role', value: 'manager' }, { op: 'test', path: '/name', value: 'Bob' }, { op: 'replace', path: '/name', value: 'John Doe' }, ]), }) // Confirm that the documents are updated expect(updated1?.name).toBe('John Doe') expect(updated1?.role).toBe('manager') expect(updated2?.name).toBe('John Doe') expect(updated2?.role).toBe('manager') }) it('should findOneAndUpdate $set and emit one update event', async () => { const created = await UserModel.create({ name: 'Bob', role: 'user' }) await UserModel.findOneAndUpdate({ _id: created._id }, { $set: { name: 'John Doe', role: 'manager' } }) const updated = await UserModel.findById(created._id).exec() expect(updated).not.toBeNull() expect(updated?.name).toBe('John Doe') expect(updated?.role).toBe('manager') const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(1) expect(em.emit).toHaveBeenCalledWith(USER_UPDATED, { oldDoc: expect.objectContaining({ __v: 0, _id: created._id, name: created.name, role: created.role, createdAt: created.createdAt, }), doc: expect.objectContaining({ __v: 0, _id: updated?._id, name: updated?.name, role: updated?.role, createdAt: created.createdAt, }), patch: expect.arrayContaining([ { op: 'test', path: '/name', value: 'Bob' }, { op: 'replace', path: '/name', value: 'John Doe' }, { op: 'test', path: '/role', value: 'user' }, { op: 'replace', path: '/role', value: 'manager' }, ]), }) // Confirm that the document is updated expect(updated?.name).toBe('John Doe') expect(updated?.role).toBe('manager') }) it('should ignoreHook option on updateMany', async () => { const john = await UserModel.create({ name: 'John', role: 'user' }) await UserModel.updateMany({ role: 'user' }, { role: 'admin' }, { ignoreHook: true }).exec() const history = await HistoryModel.find({}) expect(history).toHaveLength(0) expect(em.emit).toHaveBeenCalledTimes(0) // Confirm that the document is updated const updated = await UserModel.findById(john._id).exec() expect(updated?.role).toBe('admin') }) })