overleaf/services/docstore/test/unit/coffee/HttpControllerTests.js

445 lines
13 KiB
JavaScript
Raw Normal View History

/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const SandboxedModule = require('sandboxed-module');
const {
assert
} = require("chai");
const sinon = require('sinon');
const chai = require('chai');
chai.should();
const {
expect
} = chai;
const modulePath = require('path').join(__dirname, '../../../app/js/HttpController');
const {
ObjectId
} = require("mongojs");
describe("HttpController", function() {
beforeEach(function() {
this.HttpController = SandboxedModule.require(modulePath, { requires: {
"./DocManager": (this.DocManager = {}),
"./DocArchiveManager": (this.DocArchiveManager = {}),
"logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }),
"./HealthChecker": {}
}
});
this.res = { send: sinon.stub(), json: sinon.stub(), setHeader:sinon.stub() };
this.res.status = sinon.stub().returns(this.res);
this.req = { query:{}};
this.next = sinon.stub();
this.project_id = "mock-project-id";
this.doc_id = "mock-doc-id";
this.doc = {
_id: this.doc_id,
lines: ["mock", "lines", " here", "", "", " spaces "],
version: 42,
rev: 5
};
return this.deletedDoc = {
deleted:true,
_id: this.doc_id,
lines: ["mock", "lines", " here", "", "", " spaces "],
version: 42,
rev: 5
};});
describe("getDoc", function() {
describe("without deleted docs", function() {
beforeEach(function() {
this.req.params = {
project_id: this.project_id,
doc_id: this.doc_id
};
this.DocManager.getFullDoc = sinon.stub().callsArgWith(2, null, this.doc);
return this.HttpController.getDoc(this.req, this.res, this.next);
});
it("should get the document with the version (including deleted)", function() {
return this.DocManager.getFullDoc
.calledWith(this.project_id, this.doc_id)
.should.equal(true);
});
return it("should return the doc as JSON", function() {
return this.res.json
.calledWith({
_id: this.doc_id,
lines: this.doc.lines,
rev: this.doc.rev,
version: this.doc.version
})
.should.equal(true);
});
});
return describe("which is deleted", function() {
beforeEach(function() {
this.req.params = {
project_id: this.project_id,
doc_id: this.doc_id
};
return this.DocManager.getFullDoc = sinon.stub().callsArgWith(2, null, this.deletedDoc);
});
it("should get the doc from the doc manager", function() {
this.HttpController.getDoc(this.req, this.res, this.next);
return this.DocManager.getFullDoc.calledWith(this.project_id, this.doc_id).should.equal(true);
});
it("should return 404 if the query string delete is not set ", function() {
this.HttpController.getDoc(this.req, this.res, this.next);
return this.res.send.calledWith(404).should.equal(true);
});
return it("should return the doc as JSON if include_deleted is set to true", function() {
this.req.query.include_deleted = "true";
this.HttpController.getDoc(this.req, this.res, this.next);
return this.res.json
.calledWith({
_id: this.doc_id,
lines: this.doc.lines,
rev: this.doc.rev,
deleted: true,
version: this.doc.version
})
.should.equal(true);
});
});
});
describe("getRawDoc", function() {
beforeEach(function() {
this.req.params = {
project_id: this.project_id,
doc_id: this.doc_id
};
this.DocManager.getDocLines = sinon.stub().callsArgWith(2, null, this.doc);
return this.HttpController.getRawDoc(this.req, this.res, this.next);
});
it("should get the document without the version", function() {
return this.DocManager.getDocLines
.calledWith(this.project_id, this.doc_id)
.should.equal(true);
});
it("should set the content type header", function() {
return this.res.setHeader.calledWith('content-type', 'text/plain').should.equal(true);
});
return it("should send the raw version of the doc", function() {
return assert.deepEqual(this.res.send.args[0][0], `${this.doc.lines[0]}\n${this.doc.lines[1]}\n${this.doc.lines[2]}\n${this.doc.lines[3]}\n${this.doc.lines[4]}\n${this.doc.lines[5]}`);
});
});
describe("getAllDocs", function() {
describe("normally", function() {
beforeEach(function() {
this.req.params =
{project_id: this.project_id};
this.docs = [{
_id: ObjectId(),
lines: ["mock", "lines", "one"],
rev: 2
}, {
_id: ObjectId(),
lines: ["mock", "lines", "two"],
rev: 4
}];
this.DocManager.getAllNonDeletedDocs = sinon.stub().callsArgWith(2, null, this.docs);
return this.HttpController.getAllDocs(this.req, this.res, this.next);
});
it("should get all the (non-deleted) docs", function() {
return this.DocManager.getAllNonDeletedDocs
.calledWith(this.project_id, {lines: true, rev: true})
.should.equal(true);
});
return it("should return the doc as JSON", function() {
return this.res.json
.calledWith([{
_id: this.docs[0]._id.toString(),
lines: this.docs[0].lines,
rev: this.docs[0].rev
}, {
_id: this.docs[1]._id.toString(),
lines: this.docs[1].lines,
rev: this.docs[1].rev
}])
.should.equal(true);
});
});
return describe("with a null doc", function() {
beforeEach(function() {
this.req.params =
{project_id: this.project_id};
this.docs = [{
_id: ObjectId(),
lines: ["mock", "lines", "one"],
rev: 2
},
null,
{
_id: ObjectId(),
lines: ["mock", "lines", "two"],
rev: 4
}];
this.DocManager.getAllNonDeletedDocs = sinon.stub().callsArgWith(2, null, this.docs);
return this.HttpController.getAllDocs(this.req, this.res, this.next);
});
it("should return the non null docs as JSON", function() {
return this.res.json
.calledWith([{
_id: this.docs[0]._id.toString(),
lines: this.docs[0].lines,
rev: this.docs[0].rev
}, {
_id: this.docs[2]._id.toString(),
lines: this.docs[2].lines,
rev: this.docs[2].rev
}])
.should.equal(true);
});
return it("should log out an error", function() {
return this.logger.error
.calledWith({
err: sinon.match.has('message', "null doc"),
project_id: this.project_id
},
"encountered null doc"
)
.should.equal(true);
});
});
});
describe("getAllRanges", () => describe("normally", function() {
beforeEach(function() {
this.req.params =
{project_id: this.project_id};
this.docs = [{
_id: ObjectId(),
ranges: {"mock_ranges": "one"}
}, {
_id: ObjectId(),
ranges: {"mock_ranges": "two"}
}];
this.DocManager.getAllNonDeletedDocs = sinon.stub().callsArgWith(2, null, this.docs);
return this.HttpController.getAllRanges(this.req, this.res, this.next);
});
it("should get all the (non-deleted) doc ranges", function() {
return this.DocManager.getAllNonDeletedDocs
.calledWith(this.project_id, {ranges: true})
.should.equal(true);
});
return it("should return the doc as JSON", function() {
return this.res.json
.calledWith([{
_id: this.docs[0]._id.toString(),
ranges: this.docs[0].ranges
}, {
_id: this.docs[1]._id.toString(),
ranges: this.docs[1].ranges
}])
.should.equal(true);
});
}));
describe("updateDoc", function() {
beforeEach(function() {
return this.req.params = {
project_id: this.project_id,
doc_id: this.doc_id
};
});
describe("when the doc lines exist and were updated", function() {
beforeEach(function() {
this.req.body = {
lines: (this.lines = ["hello", "world"]),
version: (this.version = 42),
ranges: (this.ranges = { changes: "mock" })
};
this.DocManager.updateDoc = sinon.stub().yields(null, true, (this.rev = 5));
return this.HttpController.updateDoc(this.req, this.res, this.next);
});
it("should update the document", function() {
return this.DocManager.updateDoc
.calledWith(this.project_id, this.doc_id, this.lines, this.version, this.ranges)
.should.equal(true);
});
return it("should return a modified status", function() {
return this.res.json
.calledWith({modified: true, rev: this.rev})
.should.equal(true);
});
});
describe("when the doc lines exist and were not updated", function() {
beforeEach(function() {
this.req.body = {
lines: (this.lines = ["hello", "world"]),
version: (this.version = 42),
ranges: {}
};
this.DocManager.updateDoc = sinon.stub().yields(null, false, (this.rev = 5));
return this.HttpController.updateDoc(this.req, this.res, this.next);
});
return it("should return a modified status", function() {
return this.res.json
.calledWith({modified: false, rev: this.rev})
.should.equal(true);
});
});
describe("when the doc lines are not provided", function() {
beforeEach(function() {
this.req.body = { version: 42, ranges: {} };
this.DocManager.updateDoc = sinon.stub().yields(null, false);
return this.HttpController.updateDoc(this.req, this.res, this.next);
});
it("should not update the document", function() {
return this.DocManager.updateDoc.called.should.equal(false);
});
return it("should return a 400 (bad request) response", function() {
return this.res.send
.calledWith(400)
.should.equal(true);
});
});
describe("when the doc version are not provided", function() {
beforeEach(function() {
this.req.body = { version: 42, lines: ["hello world"] };
this.DocManager.updateDoc = sinon.stub().yields(null, false);
return this.HttpController.updateDoc(this.req, this.res, this.next);
});
it("should not update the document", function() {
return this.DocManager.updateDoc.called.should.equal(false);
});
return it("should return a 400 (bad request) response", function() {
return this.res.send
.calledWith(400)
.should.equal(true);
});
});
describe("when the doc ranges is not provided", function() {
beforeEach(function() {
this.req.body = { lines : [ "foo" ], version: 42 };
this.DocManager.updateDoc = sinon.stub().yields(null, false);
return this.HttpController.updateDoc(this.req, this.res, this.next);
});
it("should not update the document", function() {
return this.DocManager.updateDoc.called.should.equal(false);
});
return it("should return a 400 (bad request) response", function() {
return this.res.send
.calledWith(400)
.should.equal(true);
});
});
return describe("when the doc body is too large", function() {
beforeEach(function() {
this.req.body = {
lines: (this.lines = Array(2049).fill('a'.repeat(1024))),
version: (this.version = 42),
ranges: (this.ranges = { changes: "mock" })
};
return this.HttpController.updateDoc(this.req, this.res, this.next);
});
it("should return a 413 (too large) response", function() {
return sinon.assert.calledWith(this.res.status, 413);
});
return it("should report that the document body is too large", function() {
return sinon.assert.calledWith(this.res.send, "document body too large");
});
});
});
describe("deleteDoc", function() {
beforeEach(function() {
this.req.params = {
project_id: this.project_id,
doc_id: this.doc_id
};
this.DocManager.deleteDoc = sinon.stub().callsArg(2);
return this.HttpController.deleteDoc(this.req, this.res, this.next);
});
it("should delete the document", function() {
return this.DocManager.deleteDoc
.calledWith(this.project_id, this.doc_id)
.should.equal(true);
});
return it("should return a 204 (No Content)", function() {
return this.res.send
.calledWith(204)
.should.equal(true);
});
});
describe("archiveAllDocs", function() {
beforeEach(function() {
this.req.params =
{project_id: this.project_id};
this.DocArchiveManager.archiveAllDocs = sinon.stub().callsArg(1);
return this.HttpController.archiveAllDocs(this.req, this.res, this.next);
});
it("should archive the project", function() {
return this.DocArchiveManager.archiveAllDocs
.calledWith(this.project_id)
.should.equal(true);
});
return it("should return a 204 (No Content)", function() {
return this.res.send
2015-06-02 18:24:45 -04:00
.calledWith(204)
.should.equal(true);
});
});
return describe("destroyAllDocs", function() {
beforeEach(function() {
this.req.params =
{project_id: this.project_id};
this.DocArchiveManager.destroyAllDocs = sinon.stub().callsArg(1);
return this.HttpController.destroyAllDocs(this.req, this.res, this.next);
});
it("should destroy the docs", function() {
return sinon.assert.calledWith(this.DocArchiveManager.destroyAllDocs, this.project_id);
});
return it("should return 204", function() {
return sinon.assert.calledWith(this.res.send, 204);
});
});
});