UNPKG

iridium

Version:

A custom lightweight ORM for MongoDB designed for power-users

423 lines 20 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Iridium = require("../iridium"); const MongoDB = require("mongodb"); const Bluebird = require("bluebird"); const chai = require("chai"); class Test extends Iridium.Instance { test() { return true; } get ansqr() { return this.answer * this.answer; } } Test.collection = "test"; Test.schema = { _id: false, answer: Number, lots: { $required: false, $type: [Number] }, less: { $required: false, $propertyType: Number } }; class TestDB extends Iridium.Core { constructor() { super("mongodb://localhost/test"); this.Test = new Iridium.Model(this, Test); } } describe("Instance", () => { let core = new TestDB(); before(() => core.connect()); after(() => core.close()); beforeEach(() => core.Test.remove()); it("should default to isNew", () => { let instance = new core.Test.Instance({ answer: 42 }); chai.expect(instance).to.have.property("_isNew", true); }); it("should default to !isPartial", () => { let instance = new core.Test.Instance({ answer: 42 }); chai.expect(instance).to.have.property("_isPartial", false); }); it("should expose the latest document values", () => { let instance = core.Test.helpers.wrapDocument({ _id: "aaaaaa", answer: 2 }); chai.expect(instance).to.exist; chai.expect(instance.answer).to.be.equal(2); chai.expect(instance._id).to.be.equal("aaaaaa"); }); describe("methods", () => { it("should expose save()", () => { chai.expect(core.Test.helpers.wrapDocument({ _id: "1", answer: 2 }).save).to.exist.and.be.a("function"); }); it("should expose update()", () => { chai.expect(core.Test.helpers.wrapDocument({ _id: "1", answer: 2 }).update).to.exist.and.be.a("function"); }); it("should expose refresh()", () => { chai.expect(core.Test.helpers.wrapDocument({ _id: "1", answer: 2 }).refresh).to.exist.and.be.a("function"); }); it("should expose delete()", () => { chai.expect(core.Test.helpers.wrapDocument({ _id: "1", answer: 2 }).delete).to.exist.and.be.a("function"); }); it("should expose remove()", () => { chai.expect(core.Test.helpers.wrapDocument({ _id: "1", answer: 2 }).remove).to.exist.and.be.a("function"); }); it("should override toJSON()", () => { chai.expect(core.Test.helpers.wrapDocument({ _id: "1", answer: 2 }).toJSON()).to.eql({ _id: "1", answer: 2 }); }); it("should override toString()", () => { chai.expect(core.Test.helpers.wrapDocument({ _id: "1", answer: 2 }).toString()).to.eql(JSON.stringify({ _id: "1", answer: 2 }, null, 2)); }); }); describe("properties", () => { it("should expose document", () => { chai.expect(core.Test.helpers.wrapDocument({ _id: "1", answer: 2 }).document).to.eql({ _id: "1", answer: 2 }); }); }); it("should expose additional getters and setters", () => { let instance = core.Test.helpers.wrapDocument({ _id: "aaaaaa", answer: 2 }); chai.expect(instance).to.exist; chai.expect(instance.ansqr).to.exist.and.be.equal(4); }); it("should expose additional methods", () => { let instance = core.Test.helpers.wrapDocument({ _id: "aaaaaa", answer: 2 }); chai.expect(instance).to.exist; chai.expect(instance.test).to.exist.and.be.a("function"); }); describe("should handle _id in a special manner", () => { beforeEach(() => core.Test.remove().then(() => core.Test.insert({ answer: 42 }))); afterEach(() => core.Test.remove()); it("get should transform ObjectIDs into hex strings", () => { return core.Test.get().then(instance => { chai.expect(instance.document._id._bsontype).to.equal("ObjectID"); chai.expect(instance._id).to.be.a("string").with.length(24); }); }); it("set should transform hex strings into ObjectIDs by default", () => { return core.Test.get().then(instance => { instance._id = "aaaaaaaaaaaaaaaaaaaaaaaa"; chai.expect(new MongoDB.ObjectID(instance.document._id).toHexString()).to.equal("aaaaaaaaaaaaaaaaaaaaaaaa"); }); }); }); describe("save()", () => { beforeEach(() => core.Test.remove()); it("should avoid making calls to the database if no changes were made to the instance", () => { let update = core.Test.collection.updateOne; core.Test.collection.updateOne = (...args) => { chai.assert.fail(); return update.apply(core.Test.collection, args); }; return core.Test.insert({ answer: 1 }).then(() => core.Test.get()).then(instance => instance.save()).then(() => { core.Test.collection.updateOne = update; }); }); it("should insert the instance if it is not present in the database", () => { let instance = new core.Test.Instance({ answer: 1 }); chai.expect(instance._isNew).to.be.true; return chai.expect(instance.save().then(() => chai.expect(core.Test.get(instance._id)).to.eventually.have.property("answer", instance.answer))).to.eventually.be.ok; }); it("should set isNew to false after inserting a new instance", () => { let instance = new core.Test.Instance({ answer: 1 }); chai.expect(instance._isNew).to.be.true; return instance.save().then(() => chai.expect(instance._isNew).to.be.false); }); it("should allow a new instance to be saved on insertion, modified and saved again", () => { let instance = new core.Test.Instance({ answer: 1 }); return chai.expect(instance.save().then(() => { instance.answer = 2; return instance.save(); })).to.eventually.not.be.rejected; }); it("should automatically generate the update query if one was not provided", () => { return core.Test.insert({ answer: 1 }).then(() => chai.expect(core.Test.get().then((instance) => { instance.answer = 42; return instance.save().then(() => core.Test.get(instance._id)); })).to.eventually.have.property("answer", 42)); }); it("should allow you to specify a custom update query", () => { return core.Test.insert({ answer: 1 }) .then(() => core.Test.get()) .then((instance) => chai.expect(instance.save({ $set: { answer: 10 } })).to.eventually.have.property("answer", 10)); }); it("should allow you to specify a custom update query and conditions for the update", () => { return core.Test.insert({ answer: 1 }) .then(() => core.Test.get()) .then((instance) => chai.expect(instance.save({ answer: { $lt: 5 } }, { $set: { answer: 10 } })).to.eventually.have.property("answer", 10)); }); it("should return a promise for the instance", () => { return core.Test.insert({ answer: 1 }) .then(() => core.Test.get()) .then((instance) => chai.expect(instance.save()).to.eventually.equal(instance)); }); it("should allow the use of a callback instead of promises", (done) => { core.Test.insert({ answer: 1 }) .then(() => core.Test.get()) .then((instance) => { instance.save((err, result) => { if (err) return done(err); chai.expect(result).to.equal(instance); return done(); }); }); }); }); describe("update()", () => { beforeEach(() => core.Test.remove().then(() => core.Test.insert({ answer: 1 }))); it("should not replace the instance", () => { return core.Test.get().then((instance) => chai.expect(instance.update()).to.eventually.equal(instance)); }); it("should update the instance's properties", () => { return chai.expect(core.Test.get().then((instance) => { return core.Test.update({ _id: instance._id }, { $set: { answer: 10 } }).then(() => instance.update()); })).to.eventually.have.property("answer", 10); }); it("should set _isNew to true if the instance was removed from the database", () => { return core.Test.get().then(instance => { core.Test.remove().then(() => instance.update()).then(() => chai.expect(instance._isNew).to.be.true); }); }); it("should return a promise for the instance", () => { return core.Test.get().then((instance) => { core.Test.update({ _id: instance._id }, { $set: { answer: 10 } }).then(() => chai.expect(instance.update()).to.eventually.equal(instance)); }); }); it("should allow the use of a callback instead of promises", (done) => { core.Test.get().then((instance) => { instance.update((err, result) => { if (err) return done(err); chai.expect(result).to.equal(instance); return done(); }); }); }); }); describe("refresh()", () => { beforeEach(() => core.Test.remove().then(() => core.Test.insert({ answer: 1 }))); it("should not replace the instance", () => { return core.Test.get().then((instance) => chai.expect(instance.update()).to.eventually.equal(instance)); }); it("should update the instance's properties", () => { return chai.expect(core.Test.get().then((instance) => { return core.Test.update({ _id: instance._id }, { $set: { answer: 10 } }).then(() => instance.refresh()); })).to.eventually.have.property("answer", 10); }); it("should set _isNew to true if the instance was removed from the database", () => { return core.Test.get().then(instance => { core.Test.remove().then(() => instance.refresh()).then(() => chai.expect(instance._isNew).to.be.true); }); }); it("should return a promise for the instance", () => { return core.Test.get().then((instance) => { core.Test.update({ _id: instance._id }, { $set: { answer: 10 } }).then(() => chai.expect(instance.refresh()).to.eventually.equal(instance)); }); }); it("should allow the use of a callback instead of promises", (done) => { core.Test.get().then((instance) => { instance.refresh((err, result) => { if (err) return done(err); chai.expect(result).to.equal(instance); return done(); }); }); }); }); describe("remove()", () => { beforeEach(() => core.Test.remove().then(() => core.Test.insert({ answer: 1 }))); it("should remove the document from the database", () => { return chai.expect(core.Test.get().then((instance) => instance.remove()).then(() => core.Test.get())).to.eventually.be.null; }); it("should set the instance's isNew property to true", () => { return chai.expect(core.Test.get().then((instance) => instance.remove())).to.eventually.have.property("_isNew", true); }); it("should return a promise for the instance", () => { return core.Test.get().then((instance) => chai.expect(instance.remove()).to.eventually.equal(instance)); }); it("shouldn't mind if the object has already been removed", () => { return core.Test.get().then(instance => { return chai.expect(core.Test.remove().then(() => instance.remove())).to.eventually.not.be.rejected; }); }); it("should be a no-op if the object is marked as _isNew", () => { return core.Test.get().then(instance => { let newInstance = new core.Test.Instance(instance.document); return newInstance.remove(); }).then(() => chai.expect(core.Test.count()).to.eventually.equal(1)); }); it("should allow the use of a callback instead of promises", (done) => { core.Test.get().then((instance) => { instance.remove((err, result) => { if (err) return done(err); chai.expect(result).to.equal(instance); return done(); }); }); }); }); describe("delete()", () => { beforeEach(() => core.Test.remove().then(() => core.Test.insert({ answer: 1 }))); it("should remove the document from the database", () => { return chai.expect(core.Test.get().then((instance) => instance.delete()).then(() => core.Test.get())).to.eventually.be.null; }); it("should set the instance's isNew property to true", () => { return chai.expect(core.Test.get().then((instance) => instance.delete())).to.eventually.have.property("_isNew", true); }); it("should return a promise for the instance", () => { return core.Test.get().then((instance) => chai.expect(instance.delete()).to.eventually.equal(instance)); }); it("shouldn't mind if the object has already been removed", () => { return core.Test.get().then(instance => { return chai.expect(core.Test.remove().then(() => instance.delete())).to.eventually.not.be.rejected; }); }); it("should be a no-op if the object is marked as _isNew", () => { return core.Test.get().then(instance => { let newInstance = new core.Test.Instance(instance.document); return newInstance.delete(); }).then(() => chai.expect(core.Test.count()).to.eventually.equal(1)); }); it("should allow the use of a callback instead of promises", (done) => { core.Test.get().then((instance) => { instance.delete((err, result) => { if (err) return done(err); chai.expect(result).to.equal(instance); return done(); }); }); }); }); describe("first()", () => { beforeEach(() => core.Test.remove().then(() => core.Test.insert({ answer: 1, lots: [1, 2, 3, 4], less: { "a": 1, "b": 2 } }))); it("should return the first object which matches the predicate over an array", () => { return chai.expect(core.Test.get().then(instance => instance.first(instance.lots, lot => lot == 2))).to.eventually.equal(2); }); it("should return the first object which matches the predicate over an object", () => { return chai.expect(core.Test.get().then(instance => instance.first(instance.less, (value, key) => key == "a"))).to.eventually.equal(1); }); it("should return null if no item was found", () => { return chai.expect(core.Test.get().then(instance => instance.first(instance.lots, lot => lot > 100))).to.eventually.be.null; }); }); describe("select()", () => { beforeEach(() => core.Test.remove().then(() => core.Test.insert({ answer: 1, lots: [1, 2, 3, 4], less: { "a": 1, "b": 2 } }))); it("should return the objects which match the predicate over an array", () => { return chai.expect(core.Test.get().then(instance => instance.select(instance.lots, lot => lot > 2))).to.eventually.eql([3, 4]); }); it("should return the properties which match the predicate over an object", () => { return chai.expect(core.Test.get().then(instance => instance.select(instance.less, (value, key) => key == "a"))).to.eventually.eql({ "a": 1 }); }); it("should return an empty array if no items matched over an array", () => { return chai.expect(core.Test.get().then(instance => instance.select(instance.lots, lot => lot > 100))).to.eventually.be.eql([]); }); it("should return an empty object if no items matched over an object", () => { return chai.expect(core.Test.get().then(instance => instance.select(instance.less, lot => lot > 100))).to.eventually.be.eql({}); }); }); describe("modifications", () => { beforeEach(() => core.Test.remove().then(() => core.Test.insert({ answer: 1, lots: [1, 2, 3, 4], less: { "a": 1, "b": 2 } }))); it("should correctly diff simple property changes", () => { return core.Test.get().then(instance => { instance.answer = 2; return instance.save(); }).then(instance => { chai.expect(instance).to.have.property("answer", 2); }); }); it("should correctly diff deep property changes", () => { return core.Test.get().then(instance => { instance.less["a"] = 2; return instance.save(); }).then(instance => { chai.expect(instance).to.have.property("less").eql({ a: 2, b: 2 }); }); }); it("should correctly diff array operations", () => { return core.Test.get().then(instance => { instance.lots.push(5); return instance.save(); }).then(instance => { chai.expect(instance).to.have.property("lots").eql([1, 2, 3, 4, 5]); }); }); }); describe("after a save", () => { var instance; before(() => core.Test.remove().then(() => core.Test.insert({ answer: 1, lots: [1, 2, 3, 4], less: { "a": 1, "b": 2 } })).then(i => { i.answer = 3; return i.save(); }).then(i => instance = i)); it("should return the instance", () => { chai.expect(instance).to.exist; }); it("should correctly diff simple property changes", () => { return Bluebird.resolve(instance).then(i => { i.answer = 2; return i.save(); }).then(i => { chai.expect(i).to.exist; chai.expect(i).to.have.property("answer", 2); }); }); it("should correctly diff deep property changes", () => { return Bluebird.resolve(instance).then(i => { i.less["a"] = 2; return i.save(); }).then(i => { chai.expect(i).to.exist; chai.expect(i).to.have.property("less").eql({ a: 2, b: 2 }); }); }); it("should correctly diff array operations", () => { return Bluebird.resolve(instance).then(i => { i.lots.push(5); return i.save(); }).then(i => { chai.expect(i).to.exist; chai.expect(i).to.have.property("lots").eql([1, 2, 3, 4, 5]); }); }); }); }); //# sourceMappingURL=Instance.js.map