UNPKG

mongoose-transactions

Version:
484 lines 23 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var main_1 = require("../src/main"); var mongoose = require("mongoose"); // @ts-expect-error private variable mongoose.Promise = global.Promise; mongoose.connection // .once('open', () => { }) .on('error', function (err) { return console.warn('Warning', err); }); var personSchema = new mongoose.Schema({ age: Number, contact: { email: { alias: 'email', index: true, sparse: true, type: String, unique: true, }, }, name: String, }); var carSchema = new mongoose.Schema({ age: Number, name: String, }); var Person = mongoose.model('Person', personSchema); var Car = mongoose.model('Car', carSchema); var transaction = new main_1.default(); function dropCollections() { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, Person.deleteMany({})]; case 1: _a.sent(); return [4 /*yield*/, Car.deleteMany({})]; case 2: _a.sent(); return [2 /*return*/]; } }); }); } describe('Transaction run ', function () { // Read more about fake timers: http://facebook.github.io/jest/docs/en/timer-mocks.html#content // jest.useFakeTimers(); beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, mongoose.connect('mongodb://localhost/mongoose-transactions')]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }); // afterAll(async () => { // await dropCollections(); // }); beforeEach(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, dropCollections()]; case 1: _a.sent(); transaction.clean(); return [2 /*return*/]; } }); }); }); test('insert', function () { return __awaiter(void 0, void 0, void 0, function () { var person, jonathanObject, final, jonathan; return __generator(this, function (_a) { switch (_a.label) { case 0: person = 'Person'; jonathanObject = { age: 18, name: 'Jonathan', }; transaction.insert(person, personSchema, jonathanObject); return [4 /*yield*/, transaction.run().catch(console.error)]; case 1: final = _a.sent(); return [4 /*yield*/, Person.findOne(jonathanObject).exec()]; case 2: jonathan = _a.sent(); expect(jonathan.name).toBe(jonathanObject.name); expect(jonathan.age).toBe(jonathanObject.age); expect(final).toBeInstanceOf(Array); expect(final.length).toBe(1); return [2 /*return*/]; } }); }); }); test('it should raise a duplicate key error', function () { return __awaiter(void 0, void 0, void 0, function () { var person, jonathanObject, tonyObject, final, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: person = 'Person'; jonathanObject = { age: 18, email: 'myemail@blabla.com', name: 'Jonathan', }; tonyObject = { age: 29, email: 'myemail@blabla.com', name: 'tony', }; transaction.insert(person, personSchema, jonathanObject); transaction.insert(person, personSchema, tonyObject); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, transaction.run()]; case 2: final = _a.sent(); expect(final).toBeFalsy(); return [3 /*break*/, 4]; case 3: error_1 = _a.sent(); expect(error_1).toBeTruthy(); expect(error_1.error.code).toBe(11000); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }); test('update', function () { return __awaiter(void 0, void 0, void 0, function () { var person, tonyObject, nicolaObject, personId, final, nicola; return __generator(this, function (_a) { switch (_a.label) { case 0: person = 'Person'; tonyObject = { age: 28, name: 'Tony', }; nicolaObject = { age: 32, name: 'Nicola', }; personId = transaction.insert(person, personSchema, tonyObject); transaction.update(person, personSchema, personId, nicolaObject); return [4 /*yield*/, transaction.run()]; case 1: final = _a.sent(); return [4 /*yield*/, Person.findOne(nicolaObject).exec()]; case 2: nicola = _a.sent(); expect(nicola.name).toBe(nicolaObject.name); expect(nicola.age).toBe(nicolaObject.age); expect(final).toBeInstanceOf(Array); expect(final.length).toBe(2); return [2 /*return*/]; } }); }); }); test('remove', function () { return __awaiter(void 0, void 0, void 0, function () { var person, bobObject, aliceObject, personId, final, bob, alice; return __generator(this, function (_a) { switch (_a.label) { case 0: person = 'Person'; bobObject = { age: 45, name: 'Bob', }; aliceObject = { age: 23, name: 'Alice', }; personId = transaction.insert(person, personSchema, bobObject); transaction.update(person, personSchema, personId, aliceObject); transaction.remove(person, personSchema, personId); return [4 /*yield*/, transaction.run()]; case 1: final = _a.sent(); return [4 /*yield*/, Person.findOne(bobObject).exec()]; case 2: bob = _a.sent(); return [4 /*yield*/, Person.findOne(aliceObject).exec()]; case 3: alice = _a.sent(); expect(final).toBeInstanceOf(Array); expect(final.length).toBe(3); expect(alice).toBeNull(); expect(bob).toBeNull(); return [2 /*return*/]; } }); }); }); test('Fail remove', function () { return __awaiter(void 0, void 0, void 0, function () { var person, bobObject, aliceObject, personId, failObjectId, error_2; return __generator(this, function (_a) { switch (_a.label) { case 0: person = 'Person'; bobObject = { age: 45, name: 'Bob', }; aliceObject = { age: 23, name: 'Alice', }; personId = transaction.insert(person, personSchema, bobObject); transaction.update(person, personSchema, personId, aliceObject); failObjectId = new mongoose.Types.ObjectId(); transaction.remove(person, personSchema, failObjectId); expect(personId).not.toEqual(failObjectId); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, transaction.run()]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_2 = _a.sent(); expect(error_2.executedTransactions).toEqual(2); expect(error_2.remainingTransactions).toEqual(1); expect(error_2.error.error.message).toBe('Entity not found'); expect(error_2.data).toEqual(failObjectId); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }); test('Fail remove with rollback', function () { return __awaiter(void 0, void 0, void 0, function () { var person, bobObject, aliceObject, personId, failObjectId, error_3, rollbackObj; return __generator(this, function (_a) { switch (_a.label) { case 0: person = 'Person'; bobObject = { age: 45, name: 'Bob', }; aliceObject = { age: 23, name: 'Alice', }; personId = transaction.insert(person, personSchema, bobObject); transaction.update(person, personSchema, personId, aliceObject); failObjectId = new mongoose.Types.ObjectId(); transaction.remove(person, personSchema, failObjectId); expect(personId).not.toEqual(failObjectId); _a.label = 1; case 1: _a.trys.push([1, 3, , 5]); return [4 /*yield*/, transaction.run()]; case 2: _a.sent(); return [3 /*break*/, 5]; case 3: error_3 = _a.sent(); expect(error_3.executedTransactions).toEqual(2); expect(error_3.remainingTransactions).toEqual(1); expect(error_3.error.error.message).toBe('Entity not found'); expect(error_3.data).toEqual(failObjectId); return [4 /*yield*/, transaction .rollback() .catch(console.error) // First revert update of bob object to alice ]; case 4: rollbackObj = _a.sent(); // 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); return [3 /*break*/, 5]; case 5: return [2 /*return*/]; } }); }); }); test('Fail remove with rollback and clean, multiple update, run and insert', function () { return __awaiter(void 0, void 0, void 0, function () { var person, bobObject, aliceObject, bobId, insertRun, bobFind, aliceId, failObjectId, error_4, rollbacks, bob, alice; return __generator(this, function (_a) { switch (_a.label) { case 0: person = 'Person'; bobObject = { age: 45, name: 'Bob', }; aliceObject = { age: 23, name: 'Alice', }; bobId = transaction.insert(person, personSchema, bobObject); return [4 /*yield*/, transaction.run()]; case 1: insertRun = _a.sent(); return [4 /*yield*/, Person.findOne({ _id: bobId }).exec()]; case 2: bobFind = _a.sent(); expect(bobFind.name).toBe(bobObject.name); expect(bobFind.age).toBe(bobObject.age); expect(insertRun).toBeInstanceOf(Array); expect(insertRun.length).toBe(1); transaction.clean(); aliceId = transaction.insert(person, personSchema, aliceObject); expect(bobId).not.toEqual(aliceId); // Invert bob and alice transaction.update(person, personSchema, bobId, { name: 'Maria' }); transaction.update(person, personSchema, aliceId, { name: 'Giuseppe' }); failObjectId = new mongoose.Types.ObjectId(); // ERROR REMOVE transaction.remove(person, personSchema, failObjectId); expect(bobId).not.toEqual(failObjectId); expect(aliceId).not.toEqual(failObjectId); _a.label = 3; case 3: _a.trys.push([3, 5, , 9]); return [4 /*yield*/, transaction.run()]; case 4: _a.sent(); return [3 /*break*/, 9]; case 5: error_4 = _a.sent(); // expect(error).toBeNaN() expect(error_4.executedTransactions).toEqual(3); expect(error_4.remainingTransactions).toEqual(1); expect(error_4.error.error.message).toBe('Entity not found'); expect(error_4.data).toEqual(failObjectId); return [4 /*yield*/, transaction.rollback().catch(console.error) // expect(rollbacks).toBeNaN() // First revert update of bob object to alice ]; case 6: rollbacks = _a.sent(); // 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); return [4 /*yield*/, Person.findOne({ _id: bobId }).exec()]; case 7: bob = _a.sent(); expect(bob.name).toBe(bobObject.name); expect(bob.age).toBe(bobObject.age); return [4 /*yield*/, Person.findOne(aliceObject).exec()]; case 8: alice = _a.sent(); expect(alice).toBeNull(); return [3 /*break*/, 9]; case 9: return [2 /*return*/]; } }); }); }); test('Fail update with rollback and clean, multiple update, run and remove', function () { return __awaiter(void 0, void 0, void 0, function () { var person, bobObject, aliceObject, mariaObject, giuseppeObject, bobId, insertRun, bobFind, aliceId, mariaId, error_5, rollbacks, results; return __generator(this, function (_a) { switch (_a.label) { case 0: person = 'Person'; bobObject = { age: 45, name: 'Bob', }; aliceObject = { age: 23, name: 'Alice', }; mariaObject = { age: 43, name: 'Maria', }; giuseppeObject = { age: 33, name: 'Giuseppe', }; bobId = transaction.insert(person, personSchema, bobObject); return [4 /*yield*/, transaction.run()]; case 1: insertRun = _a.sent(); return [4 /*yield*/, Person.findOne({ _id: bobId }).exec()]; case 2: bobFind = _a.sent(); expect(bobFind.name).toBe(bobObject.name); expect(bobFind.age).toBe(bobObject.age); expect(insertRun).toBeInstanceOf(Array); expect(insertRun.length).toBe(1); transaction.clean(); aliceId = transaction.insert(person, personSchema, aliceObject); expect(bobId).not.toEqual(aliceId); transaction.remove(person, personSchema, bobId); transaction.remove(person, personSchema, aliceId); mariaId = transaction.insert(person, personSchema, mariaObject); expect(mariaId).not.toEqual(bobId); expect(mariaId).not.toEqual(aliceId); // Update maria transaction.update(person, personSchema, mariaId, giuseppeObject); // ERROR UPDATE transaction.update(person, personSchema, aliceId, { name: 'Error' }); // unreachable transactions transaction.update(person, personSchema, mariaId, { name: 'unreachable' }); transaction.insert(person, personSchema, { name: 'unreachable' }); _a.label = 3; case 3: _a.trys.push([3, 5, , 8]); return [4 /*yield*/, transaction.run()]; case 4: _a.sent(); return [3 /*break*/, 8]; case 5: error_5 = _a.sent(); // expect(error).toBeNaN() expect(error_5.executedTransactions).toEqual(5); expect(error_5.remainingTransactions).toEqual(3); expect(error_5.error.error.message).toBe('Entity not found'); expect(error_5.data.id).toEqual(aliceId); expect(error_5.data.data.name).toEqual('Error'); return [4 /*yield*/, transaction.rollback().catch(console.error)]; case 6: rollbacks = _a.sent(); 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); return [4 /*yield*/, Person.find({}).lean().exec()]; case 7: results = _a.sent(); expect(results.length).toBe(1); expect(results[0].name).toEqual(bobObject.name); expect(results[0].age).toEqual(bobObject.age); return [3 /*break*/, 8]; case 8: return [2 /*return*/]; } }); }); }); }); //# sourceMappingURL=main.spec.js.map