UNPKG

mongoose-audit-trail

Version:

Manage Mongo Collection diff History and versions

1,102 lines (1,034 loc) 37.3 kB
const Promise = require("bluebird"); const expect = require("chai").expect; const mongoose = require("mongoose"); const diffPatch = require("jsondiffpatch").create(); const diffHistory = require(".."); const History = require("../auditModel").model; const semver = require("semver"); mongoose.Promise = Promise; const mongoVersion = parseInt(mongoose.version); // const testTransaction = false; /* disable as most people don't have cluster */ let session = null; console.log(`mongoVersion:${mongoose.version}`); if (mongoVersion < 5) { mongoose.connect("mongodb://localhost:27017/tekpub_test", { useMongoClient: true }); } else { /* * to test transaction need a mongoDBv4, and start with cluster, see below link. * http://thecodebarbarian.com/introducing-run-rs-zero-config-mongodb-runner.html * and remember to stop your original non cluster mongoDB * also need to use mongoose version 5.2.9 or later */ // const uri = (testTransaction) ? 'mongodb://localhost:27017,localhost:27018,localhost:27019/tekpub_test?replicaSet=rs' : 'mongodb://localhost:27017/tekpub_test'; const uri = "mongodb://localhost:27017/tekpub_test"; const uriRS = "mongodb://localhost:27017,localhost:27018,localhost:27019/tekpub_test?replicaSet=rs"; mongoose .connect(uriRS, { useNewUrlParser: true }) .then(() => { console.log("MongoDB connected"); mongoose.connection.db .admin() .serverInfo() .then(serverInfo => { const dbVersion = serverInfo.version; if (semver.gte(dbVersion, "4.0.0")) { mongoose .startSession() .then(_session => { try { _session.startTransaction(); _session.abortTransaction(); session = _session; console.log("session supported"); } catch (e) { console.log(`session not supported ${e}`); } }) .catch(e => { console.log(`session not supported ${e}`); session = null; }); } else { console.log("MongoDB version < 4.0.0 transaction not supported."); } }); }) .catch(e => { console.warn( `Unable to connect in replca mode - falling back normal - ${e}` ); mongoose .connect(uri, { useNewUrlParser: true }) .then(() => { console.log("MongoDB connected"); }) .catch(e1 => { console.error("mongoose-diff-history connection error:", e1); }); }); } const sampleSchema1 = new mongoose.Schema({ abc: { type: Date, default: Date.now() }, def: String, ghi: Number, ignored: String }); sampleSchema1.plugin(diffHistory.plugin, { omit: ["ignored"] }); const Sample1 = mongoose.model("samples", sampleSchema1); const sampleSchemaWithArray = new mongoose.Schema({ info: String, items: [], things: [] }); sampleSchemaWithArray.plugin(diffHistory.plugin); const SampleArray = mongoose.model("samplesArray", sampleSchemaWithArray); const pickSchema = new mongoose.Schema({ def: String, ghi: Number, pickOnly: String }); pickSchema.plugin(diffHistory.plugin, { pick: ["pickOnly"] }); const PickSchema = mongoose.model("picks", pickSchema); const mandatorySchema = new mongoose.Schema({ __user: String, __reason: String, someNumber: Number, someString: String }); mandatorySchema.plugin(diffHistory.plugin, { required: ["user", "reason"] }); const MandatorySchema = mongoose.model("mandatories", mandatorySchema); describe("diffHistory", function() { afterEach(function(done) { Promise.all([ mongoose.connection.collections["samples"].remove({}), mongoose.connection.collections["picks"].remove({}), mongoose.connection.collections["samplesarrays"].remove({}), mongoose.connection.collections["histories"].remove({}), mongoose.connection.collections["mandatories"].remove({}) ]) .then(() => done()) .catch(done); }); describe("plugin: getVersion", function() { let sample1, sampleV1, sampleV2, sampleV3, sampleV4; beforeEach(function(done) { sample1 = new Sample1({ def: "ipsum", ghi: 123 }); sample1 .save() .then(sample => { sampleV1 = sample.toObject(); sample.def = "laer"; sample.__user = "Mimani"; sample.__reason = "to test it"; return sample.save(); }) .then(sample => { sampleV2 = sample.toObject(); sample.ghi = 10; return sample.save(); }) .then(sample => { sampleV3 = sample.toObject(); sample.ghi = 100; return sample.save(); }) .then(sample => { sampleV4 = sample.toObject(); done(); }) .catch(done); }); it("should return correct version for version 0", function(done) { diffHistory .getVersion(Sample1, sample1._id, 0, { lean: true }) .then(oldSample => { expect(oldSample).to.be.an("object"); expect(oldSample).to.deep.equal(sampleV1); done(); }) .catch(done); }); it("should return correct version for version 1", function(done) { diffHistory .getVersion(Sample1, sample1._id, 1, { lean: true }) .then(oldSample => { expect(oldSample).to.be.an("object"); expect(oldSample).to.deep.equal(sampleV2); done(); }) .catch(done); }); it("should return correct version for version 2", function(done) { diffHistory .getVersion(Sample1, sample1._id, 2, { lean: true }) .then(oldSample => { expect(oldSample).to.be.an("object"); expect(oldSample).to.deep.equal(sampleV3); done(); }) .catch(done); }); it("should return correct version for version 3", function(done) { diffHistory .getVersion(Sample1, sample1._id, 3, { lean: true }) .then(oldSample => { expect(oldSample).to.be.an("object"); expect(oldSample).to.deep.equal(sampleV4); done(); }) .catch(done); }); //TODO: this is test case when version is greater than created in DB, as of now returning the latest object for this case it("should return correct version for version 4", function(done) { diffHistory .getVersion(Sample1, sample1._id, 3, { lean: true }) .then(oldSample => { expect(oldSample).to.be.an("object"); expect(oldSample).to.deep.equal(sampleV4); done(); }) .catch(done); }); it("should return correct version for version 0 using callback", function(done) { diffHistory.getVersion(Sample1, sample1._id, 0, (err, oldSample) => { expect(err).to.be.null; expect(oldSample).to.be.an("object"); expect(oldSample.toObject()).to.deep.equal(sampleV1); done(); }); }); it("should return correct version for version 0 using callback and opts", function(done) { diffHistory.getVersion( Sample1, sample1._id, 0, { lean: true }, (err, oldSample) => { expect(err).to.be.null; expect(oldSample).to.be.an("object"); expect(oldSample).to.deep.equal(sampleV1); done(); } ); }); it("should return an error when calling without id", function(done) { diffHistory.getVersion(Sample1, "", 0, (err, oldSample) => { expect(oldSample).to.be.null; expect(err).to.be.an("object"); expect(err.name).to.equal("CastError"); expect(err.path).to.equal("_id"); done(); }); }); }); describe("opt: omit", function() { let sample1, sampleV0, sampleV1, sampleV2; const ignoredFinal = "i2"; beforeEach(function(done) { sample1 = new Sample1({ def: "t0", ignored: "i0" }); sample1 .save() .then(sample => { sampleV0 = sample.toObject(); sample.def = "t1"; sample.ignored = "i1"; return sample.save(); }) .then(sample => { sampleV1 = sample.toObject(); sample.def = "t2"; sample.ignored = ignoredFinal; return sample.save(); }) .then(sample => { sampleV2 = sample.toObject(); done(); }) .catch(done); }); it("should return correct version for version 0, with unchanged omission", function(done) { diffHistory .getVersion(Sample1, sample1._id, 0, { lean: true }) .then(oldSample => { expect(oldSample).to.be.an("object"); expect(oldSample.def).to.equal(sampleV0.def); expect(oldSample.ignored).to.equal(ignoredFinal); done(); }) .catch(done); }); it("should return correct version for version 1, with unchanged omission", function(done) { diffHistory .getVersion(Sample1, sample1._id, 1, { lean: true }) .then(oldSample => { expect(oldSample).to.be.an("object"); expect(oldSample.def).to.equal(sampleV1.def); expect(oldSample.ignored).to.equal(ignoredFinal); done(); }) .catch(done); }); it("should return correct version for version 2, with unchanged omission", function(done) { diffHistory .getVersion(Sample1, sample1._id, 2, { lean: true }) .then(oldSample => { expect(oldSample).to.be.an("object"); expect(oldSample.def).to.equal(sampleV2.def); expect(oldSample.ignored).to.equal(ignoredFinal); done(); }) .catch(done); }); }); describe("opt: pick", function() { beforeEach(function(done) { let pickSample; pickSample = new PickSchema({ def: "t0", ghi: 55, pickOnly: "original" }); pickSample .save() .then(pickCollection => { pickCollection.__user = "Gibran"; pickCollection.__reason = "TestingPickOnly"; pickCollection.def = "tryingToChangeThisWithNoHistoryForIt"; pickCollection.ghi = 21223; pickCollection.pickOnly = "changeThisOneOnly"; return pickCollection.save(); }) .then(() => done()) .catch(done); }); it("should only create stories with the picked field", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(1); expect(histories[0].diff.pickOnly[0]).equal("original"); expect(histories[0].diff.pickOnly[1]).equal("changeThisOneOnly"); expect(histories[0].diff).to.not.contain.key("ghi"); expect(histories[0].diff).to.not.contain.key("def"); done(); }); }); }); describe("plugin: pre save", function() { let sample1, firstSample; beforeEach(function(done) { sample1 = new Sample1({ def: "ipsum", ghi: 123 }); sample1.__user = "Frank"; sample1.__reason = "to create it"; sample1 .save() .then(sample => { firstSample = sample.toObject(); sample.def = "laer"; sample.__user = "Mimani"; sample.__reason = "to test it"; return sample.save(); }) .then(() => done()) .catch(done); }); it("should create a diff object when collection is saved", function(done) { History.find({ collectionId: sample1._id }, function(err, histories) { expect(err).to.null; expect(histories.length).equal(1); expect(histories[0].diff.def[0]).equal("ipsum"); expect(histories[0].diff.def[1]).equal("laer"); expect(histories[0].user).equal("Mimani"); expect(histories[0].reason).equal("to test it"); expect(histories[0].createdAt).not.null; expect(histories[0].createdAt).not.undefined; expect(histories[0].updatedAt).not.null; expect(histories[0].updatedAt).not.undefined; expect(histories[0].collectionName).equal(Sample1.modelName); done(); }); }); it("should return histories", function(done) { diffHistory .getHistories(Sample1.modelName, sample1._id, []) .then(historyAudits => { expect(historyAudits.length).equal(1); expect(historyAudits[0].comment).equal("modified def"); done(); }) .catch(done); }); it("should return histories using callback", function(done) { diffHistory.getHistories( Sample1.modelName, sample1._id, [], (err, historyAudits) => { expect(err).to.be.null; expect(historyAudits.length).equal(1); expect(historyAudits[0].comment).equal("modified def"); done(); } ); }); it("should throw an error if trying to get histories without an id", function(done) { diffHistory.getHistories( Sample1.modelName, "", [], (err, historyAudits) => { expect(historyAudits).to.be.null; expect(err).to.be.an("object"); expect(err.name).to.equal("CastError"); expect(err.path).to.equal("collectionId"); done(); } ); }); }); describe("plugin: pre update", function() { let sample1, sample2; beforeEach(function(done) { sample1 = new Sample1({ def: "ipsum", ghi: 123 }); sample1 .save() .then(() => { sample2 = new Sample1({ def: "lorum", ghi: 456 }); return sample2.save(); }) .then(() => Sample1.update( {}, { ghi: 1212 }, { multi: true, __user: "Mimani", __reason: "Mimani updated" } ) ) .then(() => done()) .catch(done); }); it("should create a diff object when collections are updated via update", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(2); expect(histories[0].diff.ghi[0]).equal(123); expect(histories[0].diff.ghi[1]).equal(1212); expect(histories[0].user).equal("Mimani"); expect(histories[0].reason).equal("Mimani updated"); expect(histories[0].collectionName).equal(Sample1.modelName); expect(histories[1].diff.ghi[0]).equal(456); expect(histories[1].diff.ghi[1]).equal(1212); expect(histories[1].user).equal("Mimani"); expect(histories[1].reason).equal("Mimani updated"); expect(histories[1].collectionName).equal(Sample1.modelName); done(); }); }); it("should return histories", function(done) { diffHistory .getHistories(Sample1.modelName, sample1._id, ["ghi"]) .then(historyAudits => { expect(historyAudits.length).equal(1); expect(historyAudits[0].comment).equal( "modified ghi from 123 to 1212" ); done(); }) .catch(done); }); }); describe("plugin: preUpdate using $push for arrays", function() { let sampleArr; beforeEach(function(done) { sampleArr = new SampleArray({ items: [{ type: "one" }, { type: "two" }], things: [{ number: "one" }, { number: "two" }] }); sampleArr .save() .then(() => SampleArray.update( { _id: sampleArr._id }, { $push: { items: { type: "three" }, things: { number: "three" } }, $set: { info: "something" } }, { multi: true, __user: "Gibran", __reason: "TestingPushArray" } ) ) .then(() => done()) .catch(done); }); it("should create a diff object when collections are updated via update", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(1); expect(histories[0].diff.items["2"][0].type).equal("three"); expect(histories[0].diff.things["2"][0].number).equal("three"); expect(histories[0].diff.info[0]).equal("something"); expect(histories[0].diff.items._t).equal("a"); expect(histories[0].diff.things._t).equal("a"); expect(histories[0].user).equal("Gibran"); expect(histories[0].reason).equal("TestingPushArray"); expect(histories[0].collectionName).equal(SampleArray.modelName); done(); }); }); it("should update the array correctly", function(done) { SampleArray.find({}, function(err, arrayCollections) { expect(err).to.null; expect(arrayCollections[0].items[0].type).equal("one"); expect(arrayCollections[0].items[1].type).equal("two"); expect(arrayCollections[0].items[2].type).equal("three"); expect(arrayCollections[0].things[0].number).equal("one"); expect(arrayCollections[0].things[1].number).equal("two"); expect(arrayCollections[0].things[2].number).equal("three"); done(); }); }); }); describe("plugin: pre findOneAndUpdate", function() { let sample1; beforeEach(function(done) { sample1 = new Sample1({ def: "ipsum", ghi: 123 }); sample1 .save() .then(() => Sample1.findOneAndUpdate( { def: "ipsum" }, { $set: { ghi: 323, def: "hey hye" } }, { __user: "Mimani", __reason: "Mimani updated this also", upsert: true } ) ) .then(() => done()) .catch(done); }); it("should create a diff object when collections are updated via update (with upsert option)", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(1); expect(histories[0].diff.ghi[0]).equal(123); expect(histories[0].diff.ghi[1]).equal(323); expect(histories[0].diff.def[0]).equal("ipsum"); expect(histories[0].diff.def[1]).equal("hey hye"); expect(histories[0].reason).equal("Mimani updated this also"); expect(histories[0].collectionName).equal(Sample1.modelName); expect(histories[0].collectionName).equal(Sample1.modelName); done(); }); }); it("should return histories", function(done) { diffHistory .getHistories(Sample1.modelName, sample1._id, ["ghi"]) .then(historyAudits => { expect(historyAudits.length).equal(1); expect(historyAudits[0].comment).equal( "modified def, ghi from 123 to 323" ); done(); }) .catch(done); }); }); describe("plugin: pre updateOne", function() { let sample1; beforeEach(function(done) { sample1 = new Sample1({ def: "ipsum", ghi: 123 }); sample1 .save() .then(() => Sample1.updateOne( { def: "ipsum" }, { ghi: 323, def: "hey hye" }, { __user: "Marcel", __reason: "Marcel updated using updateOne" } ) ) .then(() => done()) .catch(done); }); it("should create a diff object when collections are updated via updateOne", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(1); expect(histories[0].diff.ghi[0]).equal(123); expect(histories[0].diff.ghi[1]).equal(323); expect(histories[0].diff.def[0]).equal("ipsum"); expect(histories[0].diff.def[1]).equal("hey hye"); expect(histories[0].reason).equal("Marcel updated using updateOne"); expect(histories[0].collectionName).equal(Sample1.modelName); expect(histories[0].collectionName).equal(Sample1.modelName); done(); }); }); it("should return histories", function(done) { diffHistory .getHistories(Sample1.modelName, sample1._id, ["ghi"]) .then(historyAudits => { expect(historyAudits.length).equal(1); expect(historyAudits[0].comment).equal( "modified def, ghi from 123 to 323" ); done(); }) .catch(done); }); }); describe("plugin: post remove", function() { let sample1, sample2; beforeEach(function(done) { sample1 = new Sample1({ def: "ipsum", ghi: 123 }); sample1 .save() .then(() => Sample1.findOneAndUpdate( { def: "ipsum" }, { ghi: 323, def: "hey hye" }, { __user: "Mimani", __reason: "Mimani updated this also", new: true } ) ) .then(updated => { sample2 = JSON.parse(JSON.stringify(updated)); updated.__user = { name: "Peter", role: "developer" }; updated.__reason = "As this was requested"; return updated.remove(); }) .then(() => done()) .catch(done); }); it("should create a diff object when collections are updated via update", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(2); expect(histories[0].diff.ghi[0]).equal(123); expect(histories[0].diff.ghi[1]).equal(323); expect(histories[0].diff.def[0]).equal("ipsum"); expect(histories[0].diff.def[1]).equal("hey hye"); expect(histories[0].reason).equal("Mimani updated this also"); expect(histories[0].collectionName).equal(Sample1.modelName); expect(histories[0].collectionName).equal(Sample1.modelName); expect(histories[1].diff).deep.equal(diffPatch.diff(sample2, {})); expect(histories[1].reason).equal("As this was requested"); expect(histories[1].user).deep.equal({ name: "Peter", role: "developer" }); expect(histories[1].collectionName).equal(Sample1.modelName); expect(histories[1].collectionName).equal(Sample1.modelName); done(); }); }); it("should return simple diffs", function(done) { diffHistory .getDiffs(Sample1.modelName, sample1._id) .then(diffs => { expect(diffs.length).to.equal(2); expect(diffs[0]).to.be.an("object"); expect(diffs[0].diff).to.be.an("object"); expect(diffs[0].diff.def).to.be.an("array"); expect(diffs[0].diff.def.length).to.equal(2); expect(diffs[0].diff.def[0]).to.equal("ipsum"); expect(diffs[0].diff.def[1]).to.equal("hey hye"); expect(diffs[0].collectionName).to.equal("samples"); done(); }) .catch(done); }); it("should return simple diffs with opts", function(done) { diffHistory .getDiffs(Sample1.modelName, sample1._id, { select: "diff user" }) .then(diffs => { expect(diffs.length).to.equal(2); expect(diffs[0]).to.be.an("object"); expect(diffs[0].diff).to.be.an("object"); expect(diffs[0].diff.def).to.be.an("array"); expect(diffs[0].diff.def.length).to.equal(2); expect(diffs[0].diff.def[0]).to.equal("ipsum"); expect(diffs[0].diff.def[1]).to.equal("hey hye"); expect(diffs[0].collectionName).to.be.an("undefined"); done(); }) .catch(done); }); it("should return simple diffs with callback and opts", function(done) { diffHistory.getDiffs( Sample1.modelName, sample1._id, { select: "diff user" }, (err, diffs) => { expect(err).to.be.null; expect(diffs.length).to.equal(2); expect(diffs[0]).to.be.an("object"); expect(diffs[0].diff).to.be.an("object"); expect(diffs[0].diff.def).to.be.an("array"); expect(diffs[0].diff.def.length).to.equal(2); expect(diffs[0].diff.def[0]).to.equal("ipsum"); expect(diffs[0].diff.def[1]).to.equal("hey hye"); expect(diffs[0].collectionName).to.be.an("undefined"); done(); } ); }); it("should return simple diffs with callback and no opts", function(done) { diffHistory.getDiffs(Sample1.modelName, sample1._id, (err, diffs) => { expect(err).to.be.null; expect(diffs.length).to.equal(2); expect(diffs[0]).to.be.an("object"); expect(diffs[0].diff).to.be.an("object"); expect(diffs[0].diff.def).to.be.an("array"); expect(diffs[0].diff.def.length).to.equal(2); expect(diffs[0].diff.def[0]).to.equal("ipsum"); expect(diffs[0].diff.def[1]).to.equal("hey hye"); expect(diffs[0].collectionName).to.be.equal("samples"); done(); }); }); it("should return histories", function(done) { diffHistory .getHistories(Sample1.modelName, sample1._id, ["ghi"]) .then(historyAudits => { expect(historyAudits.length).equal(2); expect(historyAudits[0].comment).equal( "modified def, ghi from 123 to 323" ); expect(historyAudits[1].comment).to.contain("ghi from 323 to 0"); expect(historyAudits[1].comment).to.contain("abc"); expect(historyAudits[1].comment).to.contain("def"); expect(historyAudits[1].changedAt).not.null; expect(historyAudits[1].updatedAt).not.null; done(); }) .catch(done); }); it("should return histories without expandableFields", function(done) { diffHistory .getHistories(Sample1.modelName, sample1._id) .then(historyAudits => { expect(historyAudits.length).equal(2); expect(historyAudits[0].comment).equal("modified ghi, def"); /* it seems the sequence for v4 mongoose and v5 mongoose is different -modified abc, _id, def, ghi, __v +modified abc, __v, ghi, def, _id */ if (mongoVersion < 5) { expect(historyAudits[1].comment).to.equal( "modified abc, __v, ghi, def, _id" ); } else { expect(historyAudits[1].comment).to.equal( "modified abc, _id, def, ghi, __v" ); } expect(historyAudits[1].changedAt).not.null; expect(historyAudits[1].updatedAt).not.null; done(); }) .catch(done); }); it("should return histories without expandableFields and with callback", function(done) { diffHistory.getHistories( Sample1.modelName, sample1._id, (err, historyAudits) => { expect(err).to.be.null; expect(historyAudits.length).equal(2); expect(historyAudits[0].comment).equal("modified ghi, def"); /* it seems the sequence for v4 mongoose and v5 mongoose is different -modified abc, _id, def, ghi, __v +modified abc, __v, ghi, def, _id */ if (mongoVersion < 5) { expect(historyAudits[1].comment).to.equal( "modified abc, __v, ghi, def, _id" ); } else { expect(historyAudits[1].comment).to.equal( "modified abc, _id, def, ghi, __v" ); } expect(historyAudits[1].changedAt).not.null; expect(historyAudits[1].updatedAt).not.null; done(); } ); }); it("should get version after object is removed", function(done) { diffHistory .getVersion(Sample1, sample1._id, 1) .then(oldObject => { expect(sample2).deep.equal(oldObject); done(); }) .catch(done); }); it("should get latest version after object is removed", function(done) { diffHistory .getVersion(Sample1, sample1._id, 2, { lean: true }) .then(oldObject => { expect({}).deep.equal(oldObject); done(); }) .catch(done); }); }); describe("plugin: Bug: Wrong version", function() { let sample1, sample2; beforeEach(function(done) { sample1 = new Sample1({ def: "ipsum", ghi: 123 }); sample1 .save() .then(() => Sample1.findOneAndUpdate( { def: "ipsum" }, { ghi: 323, def: "hey hye" }, { __user: "Mimani", __reason: "Mimani updated this also" } ) ) .then(() => Sample1.findOneAndUpdate( { def: "hey hye" }, { ghi: 1212, def: "hey hye" }, { __user: "Mimani", __reason: "Mimani updated this also" } ) ) .then(() => { sample2 = new Sample1({ def: "lorum", ghi: 345 }); return sample2.save(); }) .then(() => Sample1.findOneAndUpdate( { def: "lorum" }, { ghi: 1919 }, { __user: "Mimani", __reason: "Mimani updated this also" } ) ) .then(() => done()) .catch(done); }); it("should assign correct version to diff history", function(done) { History.findOne({ collectionId: sample2._id }, function(err, history) { expect(err).to.null; expect(history.version).equal(0); done(); }); }); }); describe("opt: requiredCheck", function() { beforeEach(function(done) { let mandatorySample; mandatorySample = new MandatorySchema({ someNumber: 55, someString: "string" }); mandatorySample .save() .then(mandatoryCollection => { mandatoryCollection.someString = "ThisWillNotWork"; mandatoryCollection.someNumber = 99932; return mandatoryCollection.save(); }) .then(() => done()) .catch(done); }); it("it should not create histories", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(0); done(); }); }); }); describe("opt: requiredValid fields", function() { beforeEach(function(done) { let mandatorySample; mandatorySample = new MandatorySchema({ someNumber: 55, someString: "string" }); mandatorySample .save() .then(mandatoryCollection => { mandatoryCollection.someString = "ThisUpdateIsValid"; mandatoryCollection.__user = "Gibran"; mandatoryCollection.__reason = "TestingRequired"; return mandatoryCollection.save(); }) .then(() => done()) .catch(done); }); it("it should create histories", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(1); expect(histories[0].diff.someString[0]).equal("string"); expect(histories[0].diff.someString[1]).equal("ThisUpdateIsValid"); expect(histories[0].user).equal("Gibran"); expect(histories[0].reason).equal("TestingRequired"); done(); }); }); }); /* transaction test should be at the bottom of the script so that have time for the session check*/ describe("transaction", function() { before(function() { // check if session is available, if not skip all test. if (!session) { this.skip(); } }); afterEach(function(done) { Promise.all([mongoose.connection.collections["samples"].remove({})]) .then(() => done()) .catch(done); }); describe("abort transaction with __session pass to history", function() { beforeEach(function(done) { session.startTransaction(); Sample1.create([{ def: "Test1", ghi: 1 }], { session: session }) .then(() => Sample1.create([{ def: "Test2", ghi: 2 }], { session: session }) ) .then(() => Sample1.findOneAndUpdate( { def: "Test1" }, { $set: { ghi: 3 } }, { __user: "Mimani", __reason: "Mimani updated this also", session: session, __session: session } ) ) .then(() => session.abortTransaction()) .then(() => done()) .catch(done); }); it("it should rollback all document with abortTransaction", function(done) { Sample1.find({}, function(err, doc) { expect(err).to.null; expect(doc.length).equal(0); done(); }); }); it("it should rollback histories with abortTransaction", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(0); done(); }); }); }); describe("test abort transaction without __session pass to history", function() { beforeEach(function(done) { session.startTransaction(); Sample1.create([{ def: "Test1", ghi: 1 }], { session: session }) .then(() => Sample1.create([{ def: "Test2", ghi: 2 }], { session: session }) ) .then(() => Sample1.findOneAndUpdate( { def: "Test1" }, { $set: { ghi: 3 } }, { __user: "Mimani", __reason: "Mimani updated this also", session: session /*, __session: session */ } ) ) .then(() => session.abortTransaction()) .then(() => done()) .catch(done); }); it("it should rollback all document with abortTransaction", function(done) { Sample1.find({}, function(err, doc) { expect(err).to.null; expect(doc.length).equal(0); done(); }); }); it("it should create histories even with abortTransaction called since no __session pass to history", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(1); expect(histories[0].diff.ghi[0]).equal(1); expect(histories[0].diff.ghi[1]).equal(3); expect(histories[0].reason).equal("Mimani updated this also"); expect(histories[0].collectionName).equal(Sample1.modelName); expect(histories[0].collectionName).equal(Sample1.modelName); done(); }); }); }); describe("test commit transaction with __session pass to history", function() { beforeEach(function(done) { session.startTransaction(); Sample1.create([{ def: "Test1", ghi: 1 }], { session: session }) .then(() => Sample1.create([{ def: "Test2", ghi: 2 }], { session: session }) ) .then(() => Sample1.findOneAndUpdate( { def: "Test1" }, { $set: { ghi: 3 } }, { __user: "Mimani", __reason: "Mimani updated this also", session: session, __session: session } ) ) .then(() => session.commitTransaction()) .then(() => done()) .catch(done); }); it("it should create document with commitTransaction", function(done) { Sample1.find({}, function(err, doc) { expect(err).to.null; expect(doc.length).equal(2); done(); }); }); it("it should create histories with commitTransaction", function(done) { History.find({}, function(err, histories) { expect(err).to.null; expect(histories.length).equal(1); expect(histories[0].diff.ghi[0]).equal(1); expect(histories[0].diff.ghi[1]).equal(3); expect(histories[0].reason).equal("Mimani updated this also"); expect(histories[0].collectionName).equal(Sample1.modelName); expect(histories[0].collectionName).equal(Sample1.modelName); done(); }); }); }); }); }); describe("diffHistory Error", function() { it("should throw an error when given an omit option not string or array", function() { const errSchema = new mongoose.Schema({ a: String }); expect(() => errSchema.plugin(diffHistory.plugin, { omit: true })).to.throw( TypeError, "opts.omit expects string or array, instead got 'boolean'" ); }); }); describe("diffHistory URI Option", function() { it("should connect to DB at optional URI", function() { const testSchema = new mongoose.Schema({ a: String }); testSchema.plugin(diffHistory.plugin, { uri: "mongodb://localhost/customUri" }); expect(mongoose.connections).to.be.an("array"); expect(mongoose.connections.length).to.equal(1); expect(mongoose.connections[0].name).to.equal("customUri"); }); });