btrz-mongo-service
Version:
Betterez specific Mongo data-access service
414 lines (358 loc) • 11.5 kB
JavaScript
describe("Mongo Data Service", () => {
const sinon = require("sinon");
const chai = require("chai");
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
const {expect} = chai;
const {MongoDataService} = require("../index");
const {SimpleDao} = require("btrz-simple-dao");
class TestModel {
static factory(data) {
const model = new TestModel();
model.something = data.something;
return model;
}
static update(model, dto) {
model.something = dto.something;
model.updatedAt = "now";
return model;
}
}
let sandbox = null;
let dao = null;
let config = null;
let service = null;
let id = null;
beforeEach(() => {
sandbox = sinon.createSandbox();
dao = {
for: sandbox.spy(function () { return this; }),
find: sandbox.spy(function () { return this; }),
findAggregate: function () { return this; },
findById: sandbox.stub().resolves(),
count: sandbox.stub().resolves(0),
save: sandbox.stub().resolves(),
removeById: sandbox.stub().resolves(),
toArray: sandbox.stub().resolves(),
objectId: sandbox.spy(_id => SimpleDao.objectId(_id))
};
config = {};
service = new MongoDataService(dao, config);
id = SimpleDao.objectId().toString();
});
it("should be called MongoDataService", () => {
expect(MongoDataService.name).to.equal("MongoDataService");
});
describe("constructor", () => {
function sut() {
return new MongoDataService(dao, config);
}
it("should fail if dao is not in dependencies", () => {
dao = null;
expect(sut).to.throw("dao is mandatory for MongoDataService");
});
it("should fail if dao in dependencies is not a valid dao (has for method)", () => {
dao.for = null;
expect(sut).to.throw("dao is mandatory for MongoDataService");
});
it("should fail if config is not in dependencies", () => {
config = null;
expect(sut).to.throw("config is mandatory for MongoDataService");
});
it("should create an instance of MongoDataService", () => {
expect(sut()).to.be.an.instanceof(MongoDataService);
});
});
describe("#getById()", () => {
function sut() {
return service.getById(TestModel, id);
}
let dbDocument = null;
beforeEach(() => {
dbDocument = {something: "important"};
dao.findById = sandbox.stub().resolves(dbDocument);
});
it("should fail if ID is missing", () => {
id = null;
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
type: "INVALID",
code: "INVALID_TESTMODEL_ID",
message: "TestModel ID is invalid.",
});
});
it("should fail if ID is an invalid Mongo objectId", () => {
id = "something";
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
type: "INVALID",
code: "INVALID_TESTMODEL_ID",
message: "TestModel ID is invalid.",
});
});
it("should call the dao findById with the Model and id", async () => {
await sut();
expect(dao.for.calledOnce).to.equal(true);
expect(dao.for.firstCall.args[0]).to.be.eql(TestModel);
expect(dao.findById.calledOnce).to.equal(true);
expect(dao.findById.firstCall.args[0]).to.equal(id);
});
it("should return the document found from the database", async () => {
const found = await sut();
expect(found).to.deep.equal(dbDocument);
});
});
describe("#delete()", () => {
function sut() {
return service.delete(TestModel, id);
}
it("should fail if ID is missing", () => {
id = null;
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
type: "INVALID",
code: "INVALID_TESTMODEL_ID",
message: "TestModel ID is invalid.",
});
});
it("should fail if ID is an invalid Mongo objectId", () => {
id = "something";
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
type: "INVALID",
code: "INVALID_TESTMODEL_ID",
message: "TestModel ID is invalid.",
});
});
it("should call the dao findById with the Model and id", async () => {
await sut();
expect(dao.for.calledOnce).to.equal(true);
expect(dao.for.firstCall.args[0]).to.be.eql(TestModel);
expect(dao.removeById.calledOnce).to.equal(true);
expect(dao.removeById.firstCall.args[0]).to.equal(id);
});
});
describe("#getExisting()", () => {
function sut() {
return service.getExisting(TestModel, id);
}
let dbDocument = null;
beforeEach(() => {
dbDocument = {something: "important"};
dao.findById = sandbox.spy(() => Promise.resolve(dbDocument));
});
it("should fail if ID is missing", () => {
id = null;
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
type: "INVALID",
code: "INVALID_TESTMODEL_ID",
message: "TestModel ID is invalid.",
});
});
it("should fail if ID is an invalid Mongo objectId", () => {
id = "something";
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
type: "INVALID",
code: "INVALID_TESTMODEL_ID",
message: "TestModel ID is invalid.",
});
});
it("should call the dao findById with the Model and id", async () => {
await sut();
expect(dao.for.calledOnce).to.equal(true);
expect(dao.for.firstCall.args[0]).to.be.eql(TestModel);
expect(dao.findById.calledOnce).to.equal(true);
expect(dao.findById.firstCall.args[0]).to.equal(id);
});
it("should return the document found from the database", async () => {
const found = await sut();
expect(found).to.deep.equal(dbDocument);
});
it("should fail if document was not found", () => {
dbDocument = null;
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
type: "NOT_FOUND",
code: "NOT_FOUND",
message: "TestModel not found",
});
});
});
describe("#update()", () => {
function sut() {
return service.update(TestModel, id, data);
}
let existingDocument = null;
let data = null;
beforeEach(() => {
existingDocument = {something: "important"};
data = {something: "new"};
dao.findById = sandbox.spy(() => Promise.resolve(existingDocument));
});
it("should fail if ID is missing", () => {
id = null;
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
type: "INVALID",
code: "INVALID_TESTMODEL_ID",
message: "TestModel ID is invalid.",
});
});
it("should fail if ID is an invalid Mongo objectId", () => {
id = "something";
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
type: "INVALID",
code: "INVALID_TESTMODEL_ID",
message: "TestModel ID is invalid.",
});
});
it("should fail if update data is missing", () => {
data = null;
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
message: "The data is required for update"
});
});
it("should call the dao findById with the Model and id", async () => {
await sut();
expect(dao.for.calledOnce).to.equal(true);
expect(dao.for.firstCall.args[0]).to.be.eql(TestModel);
expect(dao.findById.calledOnce).to.equal(true);
expect(dao.findById.firstCall.args[0]).to.equal(id);
});
it("should fail if document was not found", () => {
existingDocument = null;
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({
type: "NOT_FOUND",
code: "NOT_FOUND",
message: "TestModel not found",
});
});
it("should update and save the document found", async () => {
await sut();
expect(dao.save.calledOnce).to.equal(true);
expect(dao.save.firstCall.args[0]).to.deep.equal({
something: "new",
updatedAt: "now"
});
});
});
describe("#getAggregateCount()", () => {
function sut() {
return service.getAggregateCount(query);
}
class ResultsCursorMock {
constructor () {
this.counter = 0;
}
on(event, handler) {
if (event === "data") {
this.onDataHandler = handler;
}
this.counter++;
if (this.counter >= 3) {
this.doIt();
}
return this;
}
doIt() {
this.onDataHandler({"total_documents": 5});
}
}
let query = null;
let cursor = null;
beforeEach(() => {
cursor = new ResultsCursorMock();
dao.toCursor = sandbox.spy(() => Promise.resolve(cursor));
query = [
{$match: {accountId: "account-id"}},
{ $group: { _id: null, "total_documents": { $sum: 1 } } }
];
});
it("should return a promise with an integer", () => {
return expect(sut()).to.eventually.equal(5);
});
it("should return a rejected promise if cursor fails", () => {
cursor.on = function (event, handler) {
if (event === "error") {
handler(new Error("mongo cursor error"));
}
return this;
};
return expect(sut())
.to.eventually.be.rejectedWith(Error)
.to.include({message: "mongo cursor error"});
});
it("should resolve to zero if cursor ends with no results", () => {
cursor.on = function (event, handler) {
if (event === "end") {
handler();
}
return this;
};
return expect(sut()).to.eventually.equal(0);
});
});
describe("#getCountedList", () => {
let model = null;
let accountId = null;
let filters = null;
let queryBuilder = null;
let listResult = null;
let countResult = null;
class QueryBuilder {
build(_accountId, _filters) {
return {accountId};
}
static buildQueryOptions(_filters, _pageSize) {
return {};
}
}
beforeEach(() => {
accountId = "123123123";
filters = {},
queryBuilder = new QueryBuilder();
model = new TestModel();
listResult = [{_id: "1"}, {_id: "2"}];
countResult = listResult.length;
dao.toArray = sandbox.spy(() => Promise.resolve(listResult));
dao.count = sandbox.spy(() => Promise.resolve(countResult));
});
function sut() {
return service.getCountedList(model, accountId, filters, queryBuilder);
}
it("should return an object with a list and count", () => {
return sut()
.then((result) => {
expect(result).to.deep.equal({
list: listResult,
count: countResult
});
});
})
it("should call count and find with same query and options", () => {
return sut()
.then(() => {
expect(dao.find.callCount).to.equal(1);
expect(dao.find.firstCall.args).to.deep.equal([{accountId}, {}]);
expect(dao.count.callCount).to.equal(1);
expect(dao.count.firstCall.args).to.deep.equal([{accountId}]);
});
});
});
});