UNPKG

@anishghosh103/mongoose-transactions

Version:
489 lines (338 loc) 13.1 kB
import Transaction from '../src/main' import * as mongoose from 'mongoose' import MongooseDelete = require('mongoose-delete') // @ts-expect-error private variable mongoose.Promise = global.Promise mongoose.connection // .once('open', () => { }) .on('error', (err) => console.warn('Warning', err)) const personSchema = new mongoose.Schema({ age: Number, contact: { email: { alias: 'email', index: true, sparse: true, type: String, unique: true, }, }, name: String, }) personSchema.plugin(MongooseDelete, { overrideMethods: 'all' }) const carSchema = new mongoose.Schema({ age: Number, name: String, }) const Person = mongoose.model('Person', personSchema) const Car = mongoose.model('Car', carSchema) const transaction = new Transaction() async function dropCollections() { await Person.deleteMany({}) await Car.deleteMany({}) } describe('Transaction run ', () => { // Read more about fake timers: http://facebook.github.io/jest/docs/en/timer-mocks.html#content // jest.useFakeTimers(); beforeAll(async () => { await mongoose.connect( `mongodb://127.0.0.1:27017/mongoose-transactions` ) }) // afterAll(async () => { // await dropCollections(); // }); beforeEach(async () => { await dropCollections() transaction.clean() }) test('insert', async () => { const person = 'Person' const jonathanObject = { age: 18, name: 'Jonathan', } transaction.insert(person, jonathanObject) const final = await transaction.run().catch(console.error) const jonathan = await Person.findOne(jonathanObject).exec() expect(jonathan.name).toBe(jonathanObject.name) expect(jonathan.age).toBe(jonathanObject.age) expect(final).toBeInstanceOf(Array) expect(final.length).toBe(1) }) test('it should raise a duplicate key error', async () => { const person = 'Person' const jonathanObject = { age: 18, email: 'myemail@blabla.com', name: 'Jonathan', } const tonyObject = { age: 29, email: 'myemail@blabla.com', name: 'tony', } transaction.insert(person, jonathanObject) transaction.insert(person, tonyObject) try { const final = await transaction.run() expect(final).toBeFalsy() } catch (error) { expect(error).toBeTruthy() expect(error.error.code).toBe(11000) } }) test('update', async () => { const person = 'Person' const tonyObject = { age: 28, name: 'Tony', } const nicolaObject = { age: 32, name: 'Nicola', } const personId = transaction.insert(person, tonyObject) transaction.update(person, personId, nicolaObject) const final = await transaction.run() const nicola = await Person.findOne(nicolaObject).exec() expect(nicola.name).toBe(nicolaObject.name) expect(nicola.age).toBe(nicolaObject.age) expect(final).toBeInstanceOf(Array) expect(final.length).toBe(2) }) test('remove', async () => { const person = 'Person' const bobObject = { age: 45, name: 'Bob', } const aliceObject = { age: 23, name: 'Alice', } const personId = transaction.insert(person, bobObject) transaction.update(person, personId, aliceObject) transaction.remove(person, personId) const final = await transaction.run() const bob = await Person.findOne(bobObject).exec() const alice = await Person.findOne(aliceObject).exec() expect(final).toBeInstanceOf(Array) expect(final.length).toBe(3) expect(alice).toBeNull() expect(bob).toBeNull() }) test('removeOne', async () => { const person: string = 'Person' const bobObject: any = { age: 45, name: 'Bob', } const aliceObject: any = { age: 23, name: 'Alice', } const personId = transaction.insert(person, bobObject) transaction.update(person, personId, { $set: aliceObject }) transaction.removeOne(person, { name: 'Alice' }) const final = await transaction.run() const bob: any = await Person.findOne(bobObject).exec() const alice: any = await Person.findOne(aliceObject).exec() expect(final).toBeInstanceOf(Array) expect(final.length).toBe(3) expect(alice).toBeNull() expect(bob).toBeNull() }) test('remove (soft-delete)', async () => { const person: string = 'Person' const bobObject: any = { age: 45, name: 'Bob', } const personId = transaction.insert(person, bobObject) transaction.remove(person, personId, { withDeleted: true }) const final = await transaction.run() const bob: any = await Person.findOne(bobObject).exec() const bobWithDeleted: any = await (Person as any) .findOneWithDeleted(bobObject) .exec() expect(final).toBeInstanceOf(Array) expect(final.length).toBe(2) expect(bob).toBeNull() expect(bobWithDeleted).not.toBeNull() }) test('Fail remove', async () => { const person = 'Person' const bobObject = { age: 45, name: 'Bob', } const aliceObject = { age: 23, name: 'Alice', } const personId = transaction.insert(person, bobObject) transaction.update(person, personId, aliceObject) const failObjectId = new mongoose.Types.ObjectId() transaction.remove(person, failObjectId) expect(personId).not.toEqual(failObjectId) try { await transaction.run() } catch (error) { expect(error.executedTransactions).toEqual(2) expect(error.remainingTransactions).toEqual(1) expect(error.error.error.message).toBe('Entity not found') expect(error.data).toEqual({ _id: failObjectId }) } }) test('Fail remove with rollback', async () => { const person = 'Person' const bobObject = { age: 45, name: 'Bob', } const aliceObject = { age: 23, name: 'Alice', } const personId = transaction.insert(person, bobObject) transaction.update(person, personId, aliceObject) const failObjectId = new mongoose.Types.ObjectId() transaction.remove(person, failObjectId) expect(personId).not.toEqual(failObjectId) try { await transaction.run() } catch (error) { expect(error.executedTransactions).toEqual(2) expect(error.remainingTransactions).toEqual(1) expect(error.error.error.message).toBe('Entity not found') expect(error.data).toEqual({ _id: failObjectId }) const rollbackObj = await transaction .rollback() .catch(console.error) // First revert update of bob object to alice expect(rollbackObj[0].name).toBe(aliceObject.name) expect(rollbackObj[0].age).toBe(aliceObject.age) // Then revert the insert of bob object expect(rollbackObj[1].name).toBe(bobObject.name) expect(rollbackObj[1].age).toBe(bobObject.age) } }) test('Fail remove with rollback and clean, multiple update, run and insert', async () => { const person = 'Person' const bobObject = { age: 45, name: 'Bob', } const aliceObject = { age: 23, name: 'Alice', } const bobId = transaction.insert(person, bobObject) const insertRun = await transaction.run() const bobFind = await Person.findOne({ _id: bobId }).exec() expect(bobFind.name).toBe(bobObject.name) expect(bobFind.age).toBe(bobObject.age) expect(insertRun).toBeInstanceOf(Array) expect(insertRun.length).toBe(1) transaction.clean() const aliceId = transaction.insert(person, aliceObject) expect(bobId).not.toEqual(aliceId) // Invert bob and alice transaction.update(person, bobId, { name: 'Maria' }) transaction.update(person, aliceId, { name: 'Giuseppe' }) const failObjectId = new mongoose.Types.ObjectId() // ERROR REMOVE transaction.remove(person, failObjectId) expect(bobId).not.toEqual(failObjectId) expect(aliceId).not.toEqual(failObjectId) try { await transaction.run() } catch (error) { // expect(error).toBeNaN() expect(error.executedTransactions).toEqual(3) expect(error.remainingTransactions).toEqual(1) expect(error.error.error.message).toBe('Entity not found') expect(error.data).toEqual({ _id: failObjectId }) const rollbacks = await transaction.rollback().catch(console.error) // expect(rollbacks).toBeNaN() // First revert update of bob object to alice expect(rollbacks[0].name).toBe('Giuseppe') expect(rollbacks[0].age).toBe(aliceObject.age) // Then revert the insert of bob object expect(rollbacks[1].name).toBe('Maria') expect(rollbacks[1].age).toBe(bobObject.age) const bob = await Person.findOne({ _id: bobId }).exec() expect(bob.name).toBe(bobObject.name) expect(bob.age).toBe(bobObject.age) const alice = await Person.findOne(aliceObject).exec() expect(alice).toBeNull() } }) test('Fail update with rollback and clean, multiple update, run and remove', async () => { const person = 'Person' const bobObject = { age: 45, name: 'Bob', } const aliceObject = { age: 23, name: 'Alice', } const mariaObject = { age: 43, name: 'Maria', } const giuseppeObject = { age: 33, name: 'Giuseppe', } const bobId = transaction.insert(person, bobObject) const insertRun = await transaction.run() const bobFind = await Person.findOne({ _id: bobId }).exec() expect(bobFind.name).toBe(bobObject.name) expect(bobFind.age).toBe(bobObject.age) expect(insertRun).toBeInstanceOf(Array) expect(insertRun.length).toBe(1) transaction.clean() const aliceId = transaction.insert(person, aliceObject) expect(bobId).not.toEqual(aliceId) transaction.remove(person, bobId) transaction.remove(person, aliceId) const mariaId = transaction.insert(person, mariaObject) expect(mariaId).not.toEqual(bobId) expect(mariaId).not.toEqual(aliceId) // Update maria transaction.update(person, mariaId, giuseppeObject) // ERROR UPDATE transaction.update(person, aliceId, { name: 'Error' }) // unreachable transactions transaction.update(person, mariaId, { name: 'unreachable' }) transaction.insert(person, { name: 'unreachable' }) try { await transaction.run() } catch (error) { // expect(error).toBeNaN() expect(error.executedTransactions).toEqual(5) expect(error.remainingTransactions).toEqual(3) expect(error.error.error.message).toBe('Entity not found') expect(error.data.findObj._id).toEqual(aliceId) expect(error.data.data.name).toEqual('Error') const rollbacks = await transaction.rollback().catch(console.error) expect(rollbacks[0].name).toEqual(giuseppeObject.name) expect(rollbacks[0].age).toEqual(giuseppeObject.age) expect(rollbacks[1].name).toEqual(mariaObject.name) expect(rollbacks[1].age).toEqual(mariaObject.age) expect(rollbacks[2].name).toEqual(aliceObject.name) expect(rollbacks[2].age).toEqual(aliceObject.age) expect(rollbacks[3].name).toEqual(bobObject.name) expect(rollbacks[3].age).toEqual(bobObject.age) expect(rollbacks[4].name).toEqual(aliceObject.name) expect(rollbacks[4].age).toEqual(aliceObject.age) const results = await Person.find({}).lean().exec() expect(results.length).toBe(1) expect(results[0].name).toEqual(bobObject.name) expect(results[0].age).toEqual(bobObject.age) } }) })