From 088fd29391335b9b7323e4e2d49362dcc09901e0 Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Sun, 16 Feb 2020 14:02:55 +0000 Subject: [PATCH] prettier: convert test/unit decaffeinated files to Prettier format --- .../test/unit/js/DocArchiveManagerTests.js | 911 +++++++----- .../docstore/test/unit/js/DocManagerTests.js | 1325 ++++++++++------- .../test/unit/js/HttpControllerTests.js | 821 +++++----- .../test/unit/js/MongoManagerTests.js | 450 +++--- .../test/unit/js/RangeManagerTests.js | 414 ++--- 5 files changed, 2233 insertions(+), 1688 deletions(-) diff --git a/services/docstore/test/unit/js/DocArchiveManagerTests.js b/services/docstore/test/unit/js/DocArchiveManagerTests.js index 97cfa73140..06bd6db8aa 100644 --- a/services/docstore/test/unit/js/DocArchiveManagerTests.js +++ b/services/docstore/test/unit/js/DocArchiveManagerTests.js @@ -12,441 +12,566 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const { - assert -} = require("chai"); -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { - expect -} = chai; -const modulePath = "../../../app/js/DocArchiveManager.js"; -const SandboxedModule = require('sandboxed-module'); -const { - ObjectId -} = require("mongojs"); -const Errors = require("../../../app/js/Errors"); -const crypto = require("crypto"); +const { assert } = require('chai') +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../app/js/DocArchiveManager.js' +const SandboxedModule = require('sandboxed-module') +const { ObjectId } = require('mongojs') +const Errors = require('../../../app/js/Errors') +const crypto = require('crypto') -describe("DocArchiveManager", function() { +describe('DocArchiveManager', function() { + beforeEach(function() { + this.settings = { + docstore: { + s3: { + secret: 'secret', + key: 'this_key', + bucket: 'doc-archive-unit-test' + } + } + } - beforeEach(function() { + this.request = { + put: {}, + get: {}, + del: {} + } - this.settings = { - docstore: { - s3: { - secret: "secret", - key: "this_key", - bucket:"doc-archive-unit-test" - } - } - }; + this.archivedDocs = [ + { + _id: ObjectId(), + inS3: true, + rev: 2 + }, + { + _id: ObjectId(), + inS3: true, + rev: 4 + }, + { + _id: ObjectId(), + inS3: true, + rev: 6 + } + ] - this.request = { - put: {}, - get: {}, - del: {} - }; + this.mongoDocs = [ + { + _id: ObjectId(), + lines: ['one', 'two', 'three'], + rev: 2 + }, + { + _id: ObjectId(), + lines: ['aaa', 'bbb', 'ccc'], + rev: 4 + }, + { + _id: ObjectId(), + inS3: true, + rev: 6 + }, + { + _id: ObjectId(), + inS3: true, + rev: 6 + }, + { + _id: ObjectId(), + lines: ['111', '222', '333'], + rev: 6 + } + ] - this.archivedDocs = [{ - _id: ObjectId(), - inS3:true, - rev: 2 - }, { - _id: ObjectId(), - inS3:true, - rev: 4 - }, { - _id: ObjectId(), - inS3:true, - rev: 6 - }]; + this.unarchivedDocs = [ + { + _id: ObjectId(), + lines: ['wombat', 'potato', 'banana'], + rev: 2 + }, + { + _id: ObjectId(), + lines: ['llama', 'turnip', 'apple'], + rev: 4 + }, + { + _id: ObjectId(), + lines: ['elephant', 'swede', 'nectarine'], + rev: 6 + } + ] - this.mongoDocs = [{ - _id: ObjectId(), - lines: ["one", "two", "three"], - rev: 2 - }, { - _id: ObjectId(), - lines: ["aaa", "bbb", "ccc"], - rev: 4 - }, { - _id: ObjectId(), - inS3: true, - rev: 6 - }, { - _id: ObjectId(), - inS3: true, - rev: 6 - }, { - _id: ObjectId(), - lines: ["111", "222", "333"], - rev: 6 - }]; + this.mixedDocs = this.archivedDocs.concat(this.unarchivedDocs) - this.unarchivedDocs = [{ - _id: ObjectId(), - lines: ["wombat", "potato", "banana"], - rev: 2 - }, { - _id: ObjectId(), - lines: ["llama", "turnip", "apple"], - rev: 4 - }, { - _id: ObjectId(), - lines: ["elephant", "swede", "nectarine"], - rev: 6 - }]; + this.MongoManager = { + markDocAsArchived: sinon.stub().callsArgWith(2, null), + upsertIntoDocCollection: sinon.stub().callsArgWith(3, null), + getProjectsDocs: sinon.stub().callsArgWith(3, null, this.mongoDocs), + getArchivedProjectDocs: sinon.stub().callsArgWith(2, null, this.mongoDocs) + } - this.mixedDocs = this.archivedDocs.concat(this.unarchivedDocs); + this.requires = { + 'settings-sharelatex': this.settings, + './MongoManager': this.MongoManager, + request: this.request, + './RangeManager': (this.RangeManager = {}), + 'logger-sharelatex': { + log() {}, + err() {} + } + } + this.globals = { JSON } - this.MongoManager = { - markDocAsArchived: sinon.stub().callsArgWith(2, null), - upsertIntoDocCollection: sinon.stub().callsArgWith(3, null), - getProjectsDocs: sinon.stub().callsArgWith(3, null, this.mongoDocs), - getArchivedProjectDocs: sinon.stub().callsArgWith(2, null, this.mongoDocs) - }; + this.error = 'my errror' + this.project_id = ObjectId().toString() + this.stubbedError = new Errors.NotFoundError('Error in S3 request') + return (this.DocArchiveManager = SandboxedModule.require(modulePath, { + requires: this.requires, + globals: this.globals + })) + }) - this.requires = { - "settings-sharelatex": this.settings, - "./MongoManager": this.MongoManager, - "request": this.request, - "./RangeManager": (this.RangeManager = {}), - "logger-sharelatex": { - log() {}, - err() {} - } - }; - this.globals = - {JSON}; + describe('archiveDoc', function() { + it('should use correct options', function(done) { + this.request.put = sinon + .stub() + .callsArgWith(1, null, { statusCode: 200, headers: { etag: '' } }) + return this.DocArchiveManager.archiveDoc( + this.project_id, + this.mongoDocs[0], + err => { + const opts = this.request.put.args[0][0] + assert.deepEqual(opts.aws, { + key: this.settings.docstore.s3.key, + secret: this.settings.docstore.s3.secret, + bucket: this.settings.docstore.s3.bucket + }) + opts.body.should.equal( + JSON.stringify({ + lines: this.mongoDocs[0].lines, + ranges: this.mongoDocs[0].ranges, + schema_v: 1 + }) + ) + opts.timeout.should.equal(30 * 1000) + opts.uri.should.equal( + `https://${this.settings.docstore.s3.bucket}.s3.amazonaws.com/${this.project_id}/${this.mongoDocs[0]._id}` + ) + return done() + } + ) + }) - this.error = "my errror"; - this.project_id = ObjectId().toString(); - this.stubbedError = new Errors.NotFoundError("Error in S3 request"); - return this.DocArchiveManager = SandboxedModule.require(modulePath, {requires: this.requires, globals: this.globals}); - }); + it('should return no md5 error', function(done) { + const data = JSON.stringify({ + lines: this.mongoDocs[0].lines, + ranges: this.mongoDocs[0].ranges, + schema_v: 1 + }) + this.md5 = crypto + .createHash('md5') + .update(data) + .digest('hex') + this.request.put = sinon + .stub() + .callsArgWith(1, null, { statusCode: 200, headers: { etag: this.md5 } }) + return this.DocArchiveManager.archiveDoc( + this.project_id, + this.mongoDocs[0], + err => { + should.not.exist(err) + return done() + } + ) + }) - describe("archiveDoc", function() { + return it('should return the error', function(done) { + this.request.put = sinon.stub().callsArgWith(1, this.stubbedError, { + statusCode: 400, + headers: { etag: '' } + }) + return this.DocArchiveManager.archiveDoc( + this.project_id, + this.mongoDocs[0], + err => { + should.exist(err) + return done() + } + ) + }) + }) - it("should use correct options", function(done){ - this.request.put = sinon.stub().callsArgWith(1, null, {statusCode:200,headers:{etag:""}}); - return this.DocArchiveManager.archiveDoc(this.project_id, this.mongoDocs[0], err=> { - const opts = this.request.put.args[0][0]; - assert.deepEqual(opts.aws, {key:this.settings.docstore.s3.key, secret:this.settings.docstore.s3.secret, bucket:this.settings.docstore.s3.bucket}); - opts.body.should.equal(JSON.stringify({ - lines: this.mongoDocs[0].lines, - ranges: this.mongoDocs[0].ranges, - schema_v: 1 - }) - ); - opts.timeout.should.equal((30*1000)); - opts.uri.should.equal(`https://${this.settings.docstore.s3.bucket}.s3.amazonaws.com/${this.project_id}/${this.mongoDocs[0]._id}`); - return done(); - }); - }); + describe('unarchiveDoc', function() { + it('should use correct options', function(done) { + this.request.get = sinon + .stub() + .callsArgWith(1, null, { statusCode: 200 }, this.mongoDocs[0].lines) + this.request.del = sinon + .stub() + .callsArgWith(1, null, { statusCode: 204 }, {}) + return this.DocArchiveManager.unarchiveDoc( + this.project_id, + this.mongoDocs[0]._id, + err => { + const opts = this.request.get.args[0][0] + assert.deepEqual(opts.aws, { + key: this.settings.docstore.s3.key, + secret: this.settings.docstore.s3.secret, + bucket: this.settings.docstore.s3.bucket + }) + opts.json.should.equal(true) + opts.timeout.should.equal(30 * 1000) + opts.uri.should.equal( + `https://${this.settings.docstore.s3.bucket}.s3.amazonaws.com/${this.project_id}/${this.mongoDocs[0]._id}` + ) + return done() + } + ) + }) - it("should return no md5 error", function(done){ - const data = JSON.stringify({ - lines: this.mongoDocs[0].lines, - ranges: this.mongoDocs[0].ranges, - schema_v: 1 - }); - this.md5 = crypto.createHash("md5").update(data).digest("hex"); - this.request.put = sinon.stub().callsArgWith(1, null, {statusCode:200,headers:{etag:this.md5}}); - return this.DocArchiveManager.archiveDoc(this.project_id, this.mongoDocs[0], err=> { - should.not.exist(err); - return done(); - }); - }); + it('should return the error', function(done) { + this.request.get = sinon.stub().callsArgWith(1, this.stubbedError, {}, {}) + return this.DocArchiveManager.unarchiveDoc( + this.project_id, + this.mongoDocs[0], + err => { + should.exist(err) + return done() + } + ) + }) - return it("should return the error", function(done){ - this.request.put = sinon.stub().callsArgWith(1, this.stubbedError, {statusCode:400,headers:{etag:""}}); - return this.DocArchiveManager.archiveDoc(this.project_id, this.mongoDocs[0], err=> { - should.exist(err); - return done(); - }); - }); - }); + return it('should error if the doc lines are a string not an array', function(done) { + this.request.get = sinon + .stub() + .callsArgWith(1, null, { statusCode: 200 }, 'this is a string') + this.request.del = sinon.stub() + return this.DocArchiveManager.unarchiveDoc( + this.project_id, + this.mongoDocs[0], + err => { + should.exist(err) + this.request.del.called.should.equal(false) + return done() + } + ) + }) + }) - describe("unarchiveDoc", function() { + describe('archiveAllDocs', function() { + it('should archive all project docs which are not in s3', function(done) { + this.MongoManager.getProjectsDocs = sinon + .stub() + .callsArgWith(3, null, this.mongoDocs) + this.DocArchiveManager.archiveDoc = sinon.stub().callsArgWith(2, null) - it("should use correct options", function(done){ - this.request.get = sinon.stub().callsArgWith(1, null, {statusCode:200}, this.mongoDocs[0].lines); - this.request.del = sinon.stub().callsArgWith(1, null, {statusCode:204}, {}); - return this.DocArchiveManager.unarchiveDoc(this.project_id, this.mongoDocs[0]._id, err=> { - const opts = this.request.get.args[0][0]; - assert.deepEqual(opts.aws, {key:this.settings.docstore.s3.key, secret:this.settings.docstore.s3.secret, bucket:this.settings.docstore.s3.bucket}); - opts.json.should.equal(true); - opts.timeout.should.equal((30*1000)); - opts.uri.should.equal(`https://${this.settings.docstore.s3.bucket}.s3.amazonaws.com/${this.project_id}/${this.mongoDocs[0]._id}`); - return done(); - }); - }); + return this.DocArchiveManager.archiveAllDocs(this.project_id, err => { + this.DocArchiveManager.archiveDoc + .calledWith(this.project_id, this.mongoDocs[0]) + .should.equal(true) + this.DocArchiveManager.archiveDoc + .calledWith(this.project_id, this.mongoDocs[1]) + .should.equal(true) + this.DocArchiveManager.archiveDoc + .calledWith(this.project_id, this.mongoDocs[4]) + .should.equal(true) - it("should return the error", function(done){ - this.request.get = sinon.stub().callsArgWith(1, this.stubbedError, {}, {}); - return this.DocArchiveManager.unarchiveDoc(this.project_id, this.mongoDocs[0], err=> { - should.exist(err); - return done(); - }); - }); + this.DocArchiveManager.archiveDoc + .calledWith(this.project_id, this.mongoDocs[2]) + .should.equal(false) + this.DocArchiveManager.archiveDoc + .calledWith(this.project_id, this.mongoDocs[3]) + .should.equal(false) - return it("should error if the doc lines are a string not an array", function(done){ - this.request.get = sinon.stub().callsArgWith(1, null, {statusCode:200}, "this is a string"); - this.request.del = sinon.stub(); - return this.DocArchiveManager.unarchiveDoc(this.project_id, this.mongoDocs[0], err=> { - should.exist(err); - this.request.del.called.should.equal(false); - return done(); - }); - }); - }); + should.not.exist(err) + return done() + }) + }) - describe("archiveAllDocs", function() { + it('should return error if have no docs', function(done) { + this.MongoManager.getProjectsDocs = sinon + .stub() + .callsArgWith(3, null, null) - it("should archive all project docs which are not in s3", function(done){ - this.MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, this.mongoDocs); - this.DocArchiveManager.archiveDoc = sinon.stub().callsArgWith(2, null); + return this.DocArchiveManager.archiveAllDocs(this.project_id, err => { + should.exist(err) + return done() + }) + }) - return this.DocArchiveManager.archiveAllDocs(this.project_id, err=> { - this.DocArchiveManager.archiveDoc.calledWith(this.project_id, this.mongoDocs[0]).should.equal(true); - this.DocArchiveManager.archiveDoc.calledWith(this.project_id, this.mongoDocs[1]).should.equal(true); - this.DocArchiveManager.archiveDoc.calledWith(this.project_id, this.mongoDocs[4]).should.equal(true); + it('should return the error', function(done) { + this.MongoManager.getProjectsDocs = sinon + .stub() + .callsArgWith(3, this.error, null) - this.DocArchiveManager.archiveDoc.calledWith(this.project_id, this.mongoDocs[2]).should.equal(false); - this.DocArchiveManager.archiveDoc.calledWith(this.project_id, this.mongoDocs[3]).should.equal(false); - - should.not.exist(err); - return done(); - }); - }); + return this.DocArchiveManager.archiveAllDocs(this.project_id, err => { + err.should.equal(this.error) + return done() + }) + }) - it("should return error if have no docs", function(done){ - this.MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, null); + return describe('when most have been already put in s3', function() { + beforeEach(function() { + let numberOfDocs = 10 * 1000 + this.mongoDocs = [] + while (--numberOfDocs !== 0) { + this.mongoDocs.push({ inS3: true, _id: ObjectId() }) + } - return this.DocArchiveManager.archiveAllDocs(this.project_id, err=> { - should.exist(err); - return done(); - }); - }); + this.MongoManager.getProjectsDocs = sinon + .stub() + .callsArgWith(3, null, this.mongoDocs) + return (this.DocArchiveManager.archiveDoc = sinon + .stub() + .callsArgWith(2, null)) + }) - it("should return the error", function(done){ - this.MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, this.error, null); + return it('should not throw and error', function(done) { + return this.DocArchiveManager.archiveAllDocs(this.project_id, err => { + should.not.exist(err) + return done() + }) + }) + }) + }) - return this.DocArchiveManager.archiveAllDocs(this.project_id, err=> { - err.should.equal(this.error); - return done(); - }); - }); + describe('unArchiveAllDocs', function() { + it('should unarchive all inS3 docs', function(done) { + this.MongoManager.getArchivedProjectDocs = sinon + .stub() + .callsArgWith(1, null, this.archivedDocs) + this.DocArchiveManager.unarchiveDoc = sinon.stub().callsArgWith(2, null) + return this.DocArchiveManager.unArchiveAllDocs(this.project_id, err => { + for (const doc of Array.from(this.archivedDocs)) { + this.DocArchiveManager.unarchiveDoc + .calledWith(this.project_id, doc._id) + .should.equal(true) + } + should.not.exist(err) + return done() + }) + }) - return describe("when most have been already put in s3", function() { + it('should return error if have no docs', function(done) { + this.MongoManager.getArchivedProjectDocs = sinon + .stub() + .callsArgWith(1, null, null) + return this.DocArchiveManager.unArchiveAllDocs(this.project_id, err => { + should.exist(err) + return done() + }) + }) - beforeEach(function() { - let numberOfDocs = 10 * 1000; - this.mongoDocs = []; - while (--numberOfDocs !== 0) { - this.mongoDocs.push({inS3:true, _id: ObjectId()}); - } + return it('should return the error', function(done) { + this.MongoManager.getArchivedProjectDocs = sinon + .stub() + .callsArgWith(1, this.error, null) + return this.DocArchiveManager.unArchiveAllDocs(this.project_id, err => { + err.should.equal(this.error) + return done() + }) + }) + }) - this.MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, this.mongoDocs); - return this.DocArchiveManager.archiveDoc = sinon.stub().callsArgWith(2, null); - }); + describe('destroyAllDocs', function() { + beforeEach(function() { + this.request.del = sinon + .stub() + .callsArgWith(1, null, { statusCode: 204 }, {}) + this.MongoManager.getProjectsDocs = sinon + .stub() + .callsArgWith(3, null, this.mixedDocs) + this.MongoManager.findDoc = sinon.stub().callsArgWith(3, null, null) + this.MongoManager.destroyDoc = sinon.stub().yields() + return Array.from(this.mixedDocs).map(doc => + this.MongoManager.findDoc + .withArgs(this.project_id, doc._id) + .callsArgWith(3, null, doc) + ) + }) - return it("should not throw and error", function(done){ - return this.DocArchiveManager.archiveAllDocs(this.project_id, err=> { - should.not.exist(err); - return done(); - }); - }); - }); - }); + it('should destroy all the docs', function(done) { + this.DocArchiveManager.destroyDoc = sinon.stub().callsArgWith(2, null) + return this.DocArchiveManager.destroyAllDocs(this.project_id, err => { + for (const doc of Array.from(this.mixedDocs)) { + this.DocArchiveManager.destroyDoc + .calledWith(this.project_id, doc._id) + .should.equal(true) + } + should.not.exist(err) + return done() + }) + }) + it('should only the s3 docs from s3', function(done) { + const docOpts = doc => { + return JSON.parse( + JSON.stringify({ + aws: { + key: this.settings.docstore.s3.key, + secret: this.settings.docstore.s3.secret, + bucket: this.settings.docstore.s3.bucket + }, + json: true, + timeout: 30 * 1000, + uri: `https://${this.settings.docstore.s3.bucket}.s3.amazonaws.com/${this.project_id}/${doc._id}` + }) + ) + } - describe("unArchiveAllDocs", function() { + return this.DocArchiveManager.destroyAllDocs(this.project_id, err => { + let doc + expect(err).not.to.exist - it("should unarchive all inS3 docs", function(done){ - this.MongoManager.getArchivedProjectDocs = sinon.stub().callsArgWith(1, null, this.archivedDocs); - this.DocArchiveManager.unarchiveDoc = sinon.stub().callsArgWith(2, null); - return this.DocArchiveManager.unArchiveAllDocs(this.project_id, err=> { - for (const doc of Array.from(this.archivedDocs)) { - this.DocArchiveManager.unarchiveDoc.calledWith(this.project_id, doc._id).should.equal(true); - } - should.not.exist(err); - return done(); - }); - }); + for (doc of Array.from(this.archivedDocs)) { + sinon.assert.calledWith(this.request.del, docOpts(doc)) + } + for (doc of Array.from(this.unarchivedDocs)) { + expect(this.request.del.calledWith(docOpts(doc))).to.equal(false) + } // no notCalledWith - it("should return error if have no docs", function(done){ - this.MongoManager.getArchivedProjectDocs = sinon.stub().callsArgWith(1, null, null); - return this.DocArchiveManager.unArchiveAllDocs(this.project_id, err=> { - should.exist(err); - return done(); - }); - }); + return done() + }) + }) - return it("should return the error", function(done){ - this.MongoManager.getArchivedProjectDocs = sinon.stub().callsArgWith(1, this.error, null); - return this.DocArchiveManager.unArchiveAllDocs(this.project_id, err=> { - err.should.equal(this.error); - return done(); - }); - }); - }); + return it('should remove the docs from mongo', function(done) { + this.DocArchiveManager.destroyAllDocs(this.project_id, err => { + return expect(err).not.to.exist + }) - describe("destroyAllDocs", function() { - beforeEach(function() { - this.request.del = sinon.stub().callsArgWith(1, null, {statusCode:204}, {}); - this.MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, this.mixedDocs); - this.MongoManager.findDoc = sinon.stub().callsArgWith(3, null, null); - this.MongoManager.destroyDoc = sinon.stub().yields(); - return Array.from(this.mixedDocs).map((doc) => - this.MongoManager.findDoc.withArgs(this.project_id, doc._id).callsArgWith(3, null, doc)); - }); + for (const doc of Array.from(this.mixedDocs)) { + sinon.assert.calledWith(this.MongoManager.destroyDoc, doc._id) + } - it("should destroy all the docs", function(done){ - this.DocArchiveManager.destroyDoc = sinon.stub().callsArgWith(2, null); - return this.DocArchiveManager.destroyAllDocs(this.project_id, err=> { - for (const doc of Array.from(this.mixedDocs)) { - this.DocArchiveManager.destroyDoc.calledWith(this.project_id, doc._id).should.equal(true); - } - should.not.exist(err); - return done(); - }); - }); + return done() + }) + }) - it("should only the s3 docs from s3", function(done){ - const docOpts = doc => { - return JSON.parse(JSON.stringify({ - aws: {key:this.settings.docstore.s3.key, secret:this.settings.docstore.s3.secret, bucket:this.settings.docstore.s3.bucket}, - json: true, - timeout: 30 * 1000, - uri:`https://${this.settings.docstore.s3.bucket}.s3.amazonaws.com/${this.project_id}/${doc._id}` - })); - }; + describe('_s3DocToMongoDoc', function() { + describe('with the old schema', function() { + return it('should return the docs lines', function(done) { + return this.DocArchiveManager._s3DocToMongoDoc( + ['doc', 'lines'], + (error, doc) => { + expect(doc).to.deep.equal({ + lines: ['doc', 'lines'] + }) + return done() + } + ) + }) + }) - return this.DocArchiveManager.destroyAllDocs(this.project_id, err=> { - let doc; - expect(err).not.to.exist; + describe('with the new schema', function() { + it('should return the doc lines and ranges', function(done) { + this.RangeManager.jsonRangesToMongo = sinon + .stub() + .returns({ mongo: 'ranges' }) + return this.DocArchiveManager._s3DocToMongoDoc( + { + lines: ['doc', 'lines'], + ranges: { json: 'ranges' }, + schema_v: 1 + }, + (error, doc) => { + expect(doc).to.deep.equal({ + lines: ['doc', 'lines'], + ranges: { mongo: 'ranges' } + }) + return done() + } + ) + }) - for (doc of Array.from(this.archivedDocs)) { - sinon.assert.calledWith(this.request.del, docOpts(doc)); - } - for (doc of Array.from(this.unarchivedDocs)) { - expect(this.request.del.calledWith(docOpts(doc))).to.equal(false); - } // no notCalledWith + return it('should return just the doc lines when there are no ranges', function(done) { + return this.DocArchiveManager._s3DocToMongoDoc( + { + lines: ['doc', 'lines'], + schema_v: 1 + }, + (error, doc) => { + expect(doc).to.deep.equal({ + lines: ['doc', 'lines'] + }) + return done() + } + ) + }) + }) - return done(); - }); - }); + return describe('with an unrecognised schema', function() { + return it('should return an error', function(done) { + return this.DocArchiveManager._s3DocToMongoDoc( + { + schema_v: 2 + }, + (error, doc) => { + expect(error).to.exist + return done() + } + ) + }) + }) + }) - return it("should remove the docs from mongo", function(done){ - this.DocArchiveManager.destroyAllDocs(this.project_id, err=> { - return expect(err).not.to.exist; - }); + return describe('_mongoDocToS3Doc', function() { + describe('with a valid doc', function() { + return it('should return the json version', function(done) { + let doc + return this.DocArchiveManager._mongoDocToS3Doc( + (doc = { + lines: ['doc', 'lines'], + ranges: { mock: 'ranges' } + }), + (err, s3_doc) => { + expect(s3_doc).to.equal( + JSON.stringify({ + lines: ['doc', 'lines'], + ranges: { mock: 'ranges' }, + schema_v: 1 + }) + ) + return done() + } + ) + }) + }) - for (const doc of Array.from(this.mixedDocs)) { - sinon.assert.calledWith(this.MongoManager.destroyDoc, doc._id); - } + describe('with null bytes in the result', function() { + beforeEach(function() { + this._stringify = JSON.stringify + return (JSON.stringify = sinon.stub().returns('{"bad": "\u0000"}')) + }) - return done(); - }); - }); - - describe("_s3DocToMongoDoc", function() { - describe("with the old schema", function() { return it("should return the docs lines", function(done) { - return this.DocArchiveManager._s3DocToMongoDoc(["doc", "lines"], (error, doc) => { - expect(doc).to.deep.equal({ - lines: ["doc", "lines"] - }); - return done(); - }); - }); }); - - describe("with the new schema", function() { - it("should return the doc lines and ranges", function(done) { - this.RangeManager.jsonRangesToMongo = sinon.stub().returns({"mongo": "ranges"}); - return this.DocArchiveManager._s3DocToMongoDoc({ - lines: ["doc", "lines"], - ranges: {"json": "ranges"}, - schema_v: 1 - }, (error, doc) => { - expect(doc).to.deep.equal({ - lines: ["doc", "lines"], - ranges: {"mongo": "ranges"} - }); - return done(); - }); - }); - - return it("should return just the doc lines when there are no ranges", function(done) { - return this.DocArchiveManager._s3DocToMongoDoc({ - lines: ["doc", "lines"], - schema_v: 1 - }, (error, doc) => { - expect(doc).to.deep.equal({ - lines: ["doc", "lines"] - }); - return done(); - }); - }); - }); - - return describe("with an unrecognised schema", function() { return it("should return an error", function(done) { - return this.DocArchiveManager._s3DocToMongoDoc({ - schema_v: 2 - }, (error, doc) => { - expect(error).to.exist; - return done(); - }); - }); }); - }); - - return describe("_mongoDocToS3Doc", function() { - describe("with a valid doc", function() { return it("should return the json version", function(done) { - let doc; - return this.DocArchiveManager._mongoDocToS3Doc((doc = { - lines: ["doc", "lines"], - ranges: { "mock": "ranges" } - }), (err, s3_doc) => { - expect(s3_doc).to.equal(JSON.stringify({ - lines: ["doc", "lines"], - ranges: { "mock": "ranges" }, - schema_v: 1 - }) - ); - return done(); - }); - }); }); - - describe("with null bytes in the result", function() { - beforeEach(function() { - this._stringify = JSON.stringify; - return JSON.stringify = sinon.stub().returns('{"bad": "\u0000"}'); - }); - - afterEach(function() { - return JSON.stringify = this._stringify; - }); - - return it("should return an error", function(done) { - return this.DocArchiveManager._mongoDocToS3Doc({ - lines: ["doc", "lines"], - ranges: { "mock": "ranges" } - }, (err, s3_doc) => { - expect(err).to.exist; - return done(); - }); - }); - }); - - return describe("without doc lines", function() { return it("should return an error", function(done) { - return this.DocArchiveManager._mongoDocToS3Doc({}, (err, s3_doc) => { - expect(err).to.exist; - return done(); - }); - }); }); - }); -}); - - + afterEach(function() { + return (JSON.stringify = this._stringify) + }) + + return it('should return an error', function(done) { + return this.DocArchiveManager._mongoDocToS3Doc( + { + lines: ['doc', 'lines'], + ranges: { mock: 'ranges' } + }, + (err, s3_doc) => { + expect(err).to.exist + return done() + } + ) + }) + }) + + return describe('without doc lines', function() { + return it('should return an error', function(done) { + return this.DocArchiveManager._mongoDocToS3Doc({}, (err, s3_doc) => { + expect(err).to.exist + return done() + }) + }) + }) + }) +}) diff --git a/services/docstore/test/unit/js/DocManagerTests.js b/services/docstore/test/unit/js/DocManagerTests.js index 4130818601..9508e74f2e 100644 --- a/services/docstore/test/unit/js/DocManagerTests.js +++ b/services/docstore/test/unit/js/DocManagerTests.js @@ -12,535 +12,796 @@ * 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 sinon = require('sinon'); -const chai = require('chai'); -const { - assert -} = require("chai"); -chai.should(); -const { - expect -} = chai; -const modulePath = require('path').join(__dirname, '../../../app/js/DocManager'); -const { - ObjectId -} = require("mongojs"); -const Errors = require("../../../app/js/Errors"); - -describe("DocManager", function() { - beforeEach(function() { - this.DocManager = SandboxedModule.require(modulePath, { requires: { - "./MongoManager": (this.MongoManager = {}), - "./DocArchiveManager": (this.DocArchiveManager = {}), - "./RangeManager": (this.RangeManager = { - jsonRangesToMongo(r) { return r; }, - shouldUpdateRanges: sinon.stub().returns(false) - }), - "logger-sharelatex": (this.logger = { - log: sinon.stub(), - warn() {}, - err() {} - }) - } - } - ); - this.doc_id = ObjectId().toString(); - this.project_id = ObjectId().toString(); - this.another_project_id = ObjectId().toString(); - this.callback = sinon.stub(); - return this.stubbedError = new Error("blew up"); - }); - - - describe("checkDocExists", function() { - beforeEach(function() { - return this.DocManager._getDoc = sinon.stub(); - }); - - it("should call get doc with a quick filter", function(done){ - this.DocManager._getDoc.callsArgWith(3, null, {_id:this.doc_id}); - return this.DocManager.checkDocExists(this.project_id, this.doc_id, (err, exist)=> { - exist.should.equal(true); - this.DocManager._getDoc.calledWith(this.project_id, this.doc_id, {_id:1, inS3:true}).should.equal(true); - return done(); - }); - }); - - it("should return false when doc is not there", function(done){ - this.DocManager._getDoc.callsArgWith(3, null); - return this.DocManager.checkDocExists(this.project_id, this.doc_id, (err, exist)=> { - exist.should.equal(false); - return done(); - }); - }); - - return it("should return error when get doc errors", function(done){ - this.DocManager._getDoc.callsArgWith(3, "error"); - return this.DocManager.checkDocExists(this.project_id, this.doc_id, (err, exist)=> { - err.should.equal("error"); - return done(); - }); - }); - }); - - describe("getFullDoc", function() { - beforeEach(function() { - this.DocManager._getDoc = sinon.stub(); - return this.doc = { - _id: this.doc_id, - lines:["2134"] - };}); - - it("should call get doc with a quick filter", function(done){ - this.DocManager._getDoc.callsArgWith(3, null, this.doc); - return this.DocManager.getFullDoc(this.project_id, this.doc_id, (err, doc)=> { - doc.should.equal(this.doc); - this.DocManager._getDoc.calledWith(this.project_id, this.doc_id, {lines: true, rev: true, deleted: true, version: true, ranges: true, inS3:true}).should.equal(true); - return done(); - }); - }); - - return it("should return error when get doc errors", function(done){ - this.DocManager._getDoc.callsArgWith(3, "error"); - return this.DocManager.getFullDoc(this.project_id, this.doc_id, (err, exist)=> { - err.should.equal("error"); - return done(); - }); - }); - }); - - describe("getRawDoc", function() { - - beforeEach(function() { - this.DocManager._getDoc = sinon.stub(); - return this.doc = - {lines:["2134"]};}); - - it("should call get doc with a quick filter", function(done){ - this.DocManager._getDoc.callsArgWith(3, null, this.doc); - return this.DocManager.getDocLines(this.project_id, this.doc_id, (err, doc)=> { - doc.should.equal(this.doc); - this.DocManager._getDoc.calledWith(this.project_id, this.doc_id, {lines: true, inS3:true}).should.equal(true); - return done(); - }); - }); - - return it("should return error when get doc errors", function(done){ - this.DocManager._getDoc.callsArgWith(3, "error"); - return this.DocManager.getDocLines(this.project_id, this.doc_id, (err, exist)=> { - err.should.equal("error"); - return done(); - }); - }); - }); - - - - describe("getDoc", function() { - beforeEach(function() { - this.project = { name: "mock-project" }; - this.doc = { _id: this.doc_id, project_id: this.project_id, lines: ["mock-lines"] }; - this.version = 42; - this.MongoManager.findDoc = sinon.stub(); - return this.MongoManager.getDocVersion = sinon.stub().yields(null, this.version); - }); - - describe("when using a filter", function() { - beforeEach(function() { - return this.MongoManager.findDoc.yields(null, this.doc); - }); - - it("should error if inS3 is not set to true", function(done){ - return this.DocManager._getDoc(this.project_id, this.doc_id, {inS3: false}, (err) => { - expect(err).to.exist; - return done(); - }); - }); - - it("should always get inS3 even when no filter is passed", function(done){ - return this.DocManager._getDoc(this.project_id, this.doc_id, undefined, err=> { - this.MongoManager.findDoc.called.should.equal(false); - expect(err).to.exist; - return done(); - }); - }); - - return it("should not error if inS3 is set to true", function(done){ - return this.DocManager._getDoc(this.project_id, this.doc_id, {inS3: true}, (err) => { - expect(err).to.not.exist; - return done(); - }); - }); - }); - - describe("when the doc is in the doc collection", function() { - beforeEach(function() { - this.MongoManager.findDoc.yields(null, this.doc); - return this.DocManager._getDoc(this.project_id, this.doc_id, {version: true, inS3:true}, this.callback); - }); - - it("should get the doc from the doc collection", function() { - return this.MongoManager.findDoc - .calledWith(this.project_id, this.doc_id) - .should.equal(true); - }); - - it("should get the doc version from the docOps collection", function() { - return this.MongoManager.getDocVersion - .calledWith(this.doc_id) - .should.equal(true); - }); - - return it("should return the callback with the doc with the version", function() { - this.callback.called.should.equal(true); - const doc = this.callback.args[0][1]; - doc.lines.should.equal(this.doc.lines); - return doc.version.should.equal(this.version); - }); - }); - - describe("without the version filter", function() { - beforeEach(function() { - this.MongoManager.findDoc.yields(null, this.doc); - return this.DocManager._getDoc(this.project_id, this.doc_id, {version: false, inS3:true}, this.callback); - }); - - return it("should not get the doc version from the docOps collection", function() { - return this.MongoManager.getDocVersion.called.should.equal(false); - }); - }); - - describe("when MongoManager.findDoc errors", function() { - beforeEach(function() { - this.MongoManager.findDoc.yields(this.stubbedError); - return this.DocManager._getDoc(this.project_id, this.doc_id, {version: true, inS3:true}, this.callback); - }); - - return it("should return the error", function() { - return this.callback.calledWith(this.stubbedError).should.equal(true); - }); - }); - - describe("when the doc is archived", function() { - beforeEach(function() { - this.doc = { _id: this.doc_id, project_id: this.project_id, lines: ["mock-lines"], inS3: true }; - this.MongoManager.findDoc.yields(null, this.doc); - this.DocArchiveManager.unarchiveDoc = (project_id, doc_id, callback) => { - this.doc.inS3 = false; - return callback(); - }; - sinon.spy(this.DocArchiveManager, "unarchiveDoc"); - return this.DocManager._getDoc(this.project_id, this.doc_id, {version: true, inS3:true}, this.callback); - }); - - it("should call the DocArchive to unarchive the doc", function() { - return this.DocArchiveManager.unarchiveDoc.calledWith(this.project_id, this.doc_id).should.equal(true); - }); - - it("should look up the doc twice", function() { - return this.MongoManager.findDoc.calledTwice.should.equal(true); - }); - - return it("should return the doc", function() { - return this.callback.calledWith(null, this.doc).should.equal(true); - }); - }); - - return describe("when the doc does not exist in the docs collection", function() { - beforeEach(function() { - this.MongoManager.findDoc = sinon.stub().yields(null, null); - return this.DocManager._getDoc(this.project_id, this.doc_id, {version: true, inS3:true}, this.callback); - }); - - return it("should return a NotFoundError", function() { - return this.callback - .calledWith(sinon.match.has('message', `No such doc: ${this.doc_id} in project ${this.project_id}`)) - .should.equal(true); - }); - }); - }); - - describe("getAllNonDeletedDocs", function() { - describe("when the project exists", function() { - beforeEach(function() { - this.docs = [{ _id: this.doc_id, project_id: this.project_id, lines: ["mock-lines"] }]; - this.MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, this.docs); - this.DocArchiveManager.unArchiveAllDocs = sinon.stub().callsArgWith(1, null, this.docs); - this.filter = { lines: true }; - return this.DocManager.getAllNonDeletedDocs(this.project_id, this.filter, this.callback); - }); - - it("should get the project from the database", function() { - return this.MongoManager.getProjectsDocs - .calledWith(this.project_id, {include_deleted: false}, this.filter) - .should.equal(true); - }); - - return it("should return the docs", function() { - return this.callback.calledWith(null, this.docs).should.equal(true); - }); - }); - - return describe("when there are no docs for the project", function() { - beforeEach(function() { - this.MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, null); - this.DocArchiveManager.unArchiveAllDocs = sinon.stub().callsArgWith(1, null); - return this.DocManager.getAllNonDeletedDocs(this.project_id, this.filter, this.callback); - }); - - return it("should return a NotFoundError", function() { - return this.callback - .calledWith(sinon.match.has('message', `No docs for project ${this.project_id}`)) - .should.equal(true); - }); - }); - }); - - describe("deleteDoc", function() { - describe("when the doc exists", function() { - beforeEach(function() { - this.lines = ["mock", "doc", "lines"]; - this.rev = 77; - this.DocManager.checkDocExists = sinon.stub().callsArgWith(2, null, true); - this.MongoManager.markDocAsDeleted = sinon.stub().callsArg(2); - return this.DocManager.deleteDoc(this.project_id, this.doc_id, this.callback); - }); - - it("should get the doc", function() { - return this.DocManager.checkDocExists - .calledWith(this.project_id, this.doc_id) - .should.equal(true); - }); - - it("should mark doc as deleted", function() { - return this.MongoManager.markDocAsDeleted - .calledWith(this.project_id, this.doc_id) - .should.equal(true); - }); - - return it("should return the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - return describe("when the doc does not exist", function() { - beforeEach(function() { - this.DocManager.checkDocExists = sinon.stub().callsArgWith(2, null, false); - return this.DocManager.deleteDoc(this.project_id, this.doc_id, this.callback); - }); - - return it("should return a NotFoundError", function() { - return this.callback - .calledWith(sinon.match.has('message', `No such project/doc to delete: ${this.project_id}/${this.doc_id}`)) - .should.equal(true); - }); - }); - }); - - return describe("updateDoc", function() { - beforeEach(function() { - this.oldDocLines = ["old", "doc", "lines"]; - this.newDocLines = ["new", "doc", "lines"]; - this.originalRanges = { - changes: [{ - id: ObjectId().toString(), - op: { i: "foo", p: 3 }, - meta: { - user_id: ObjectId().toString(), - ts: new Date().toString() - } - }] - }; - this.newRanges = { - changes: [{ - id: ObjectId().toString(), - op: { i: "bar", p: 6 }, - meta: { - user_id: ObjectId().toString(), - ts: new Date().toString() - } - }] - }; - this.version = 42; - this.doc = { _id: this.doc_id, project_id: this.project_id, lines: this.oldDocLines, rev: (this.rev = 5), version: this.version, ranges: this.originalRanges }; - - this.MongoManager.upsertIntoDocCollection = sinon.stub().callsArg(3); - this.MongoManager.setDocVersion = sinon.stub().yields(); - return this.DocManager._getDoc = sinon.stub(); - }); - - describe("when only the doc lines have changed", function() { - beforeEach(function() { - this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc); - return this.DocManager.updateDoc(this.project_id, this.doc_id, this.newDocLines, this.version, this.originalRanges, this.callback); - }); - - it("should get the existing doc", function() { - return this.DocManager._getDoc - .calledWith(this.project_id, this.doc_id, {version: true, rev: true, lines: true, version: true, ranges: true, inS3:true}) - .should.equal(true); - }); - - it("should upsert the document to the doc collection", function() { - return this.MongoManager.upsertIntoDocCollection - .calledWith(this.project_id, this.doc_id, {lines: this.newDocLines}) - .should.equal(true); - }); - - it("should not update the version", function() { - return this.MongoManager.setDocVersion.called.should.equal(false); - }); - - return it("should return the callback with the new rev", function() { - return this.callback.calledWith(null, true, this.rev + 1).should.equal(true); - }); - }); - - describe("when the doc ranges have changed", function() { - beforeEach(function() { - this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc); - this.RangeManager.shouldUpdateRanges.returns(true); - return this.DocManager.updateDoc(this.project_id, this.doc_id, this.oldDocLines, this.version, this.newRanges, this.callback); - }); - - it("should upsert the ranges", function() { - return this.MongoManager.upsertIntoDocCollection - .calledWith(this.project_id, this.doc_id, {ranges: this.newRanges}) - .should.equal(true); - }); - - it("should not update the version", function() { - return this.MongoManager.setDocVersion.called.should.equal(false); - }); - - return it("should return the callback with the new rev", function() { - return this.callback.calledWith(null, true, this.rev + 1).should.equal(true); - }); - }); - - describe("when only the version has changed", function() { - beforeEach(function() { - this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc); - return this.DocManager.updateDoc(this.project_id, this.doc_id, this.oldDocLines, this.version + 1, this.originalRanges, this.callback); - }); - - it("should not change the lines or ranges", function() { - return this.MongoManager.upsertIntoDocCollection.called.should.equal(false); - }); - - it("should update the version", function() { - return this.MongoManager.setDocVersion - .calledWith(this.doc_id, this.version + 1) - .should.equal(true); - }); - - return it("should return the callback with the old rev", function() { - return this.callback.calledWith(null, true, this.rev).should.equal(true); - }); - }); - - describe("when the doc has not changed at all", function() { - beforeEach(function() { - this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc); - return this.DocManager.updateDoc(this.project_id, this.doc_id, this.oldDocLines, this.version, this.originalRanges, this.callback); - }); - - it("should not update the ranges or lines", function() { - return this.MongoManager.upsertIntoDocCollection.called.should.equal(false); - }); - - it("should not update the version", function() { - return this.MongoManager.setDocVersion.called.should.equal(false); - }); - - return it("should return the callback with the old rev and modified == false", function() { - return this.callback.calledWith(null, false, this.rev).should.equal(true); - }); - }); - - describe("when the version is null", function() { - beforeEach(function() { - return this.DocManager.updateDoc(this.project_id, this.doc_id, this.newDocLines, null, this.originalRanges, this.callback); - }); - - return it("should return an error", function() { - return this.callback.calledWith(sinon.match.has('message', "no lines, version or ranges provided")).should.equal(true); - }); - }); - - describe("when the lines are null", function() { - beforeEach(function() { - return this.DocManager.updateDoc(this.project_id, this.doc_id, null, this.version, this.originalRanges, this.callback); - }); - - return it("should return an error", function() { - return this.callback.calledWith(sinon.match.has('message', "no lines, version or ranges provided")).should.equal(true); - }); - }); - - describe("when the ranges are null", function() { - beforeEach(function() { - return this.DocManager.updateDoc(this.project_id, this.doc_id, this.newDocLines, this.version, null, this.callback); - }); - - return it("should return an error", function() { - return this.callback.calledWith(sinon.match.has('message', "no lines, version or ranges provided")).should.equal(true); - }); - }); - - describe("when there is a generic error getting the doc", function() { - beforeEach(function() { - this.error = new Error("doc could not be found"); - this.DocManager._getDoc = sinon.stub().callsArgWith(3, this.error, null, null); - return this.DocManager.updateDoc(this.project_id, this.doc_id, this.newDocLines, this.version, this.originalRanges, this.callback); - }); - - it("should not upsert the document to the doc collection", function() { - return this.MongoManager.upsertIntoDocCollection.called.should.equal(false); - }); - - return it("should return the callback with the error", function() { - return this.callback.calledWith(this.error).should.equal(true); - }); - }); - - describe("when the doc lines have not changed", function() { - beforeEach(function() { - this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc); - return this.DocManager.updateDoc(this.project_id, this.doc_id, this.oldDocLines.slice(), this.version, this.originalRanges, this.callback); - }); - - it("should not update the doc", function() { - return this.MongoManager.upsertIntoDocCollection.called.should.equal(false); - }); - - return it("should return the callback with the existing rev", function() { - return this.callback.calledWith(null, false, this.rev).should.equal(true); - }); - }); - - return describe("when the doc does not exist", function() { - beforeEach(function() { - this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, null, null); - return this.DocManager.updateDoc(this.project_id, this.doc_id, this.newDocLines, this.version, this.originalRanges, this.callback); - }); - - it("should upsert the document to the doc collection", function() { - return this.MongoManager.upsertIntoDocCollection - .calledWith(this.project_id, this.doc_id, {lines: this.newDocLines, ranges: this.originalRanges}) - .should.equal(true); - }); - - it("should set the version", function() { - return this.MongoManager.setDocVersion - .calledWith(this.doc_id, this.version) - .should.equal(true); - }); - - return it("should return the callback with the new rev", function() { - return this.callback.calledWith(null, true, 1).should.equal(true); - }); - }); - }); -}); +const SandboxedModule = require('sandboxed-module') +const sinon = require('sinon') +const chai = require('chai') +const { assert } = require('chai') +chai.should() +const { expect } = chai +const modulePath = require('path').join(__dirname, '../../../app/js/DocManager') +const { ObjectId } = require('mongojs') +const Errors = require('../../../app/js/Errors') + +describe('DocManager', function() { + beforeEach(function() { + this.DocManager = SandboxedModule.require(modulePath, { + requires: { + './MongoManager': (this.MongoManager = {}), + './DocArchiveManager': (this.DocArchiveManager = {}), + './RangeManager': (this.RangeManager = { + jsonRangesToMongo(r) { + return r + }, + shouldUpdateRanges: sinon.stub().returns(false) + }), + 'logger-sharelatex': (this.logger = { + log: sinon.stub(), + warn() {}, + err() {} + }) + } + }) + this.doc_id = ObjectId().toString() + this.project_id = ObjectId().toString() + this.another_project_id = ObjectId().toString() + this.callback = sinon.stub() + return (this.stubbedError = new Error('blew up')) + }) + + describe('checkDocExists', function() { + beforeEach(function() { + return (this.DocManager._getDoc = sinon.stub()) + }) + + it('should call get doc with a quick filter', function(done) { + this.DocManager._getDoc.callsArgWith(3, null, { _id: this.doc_id }) + return this.DocManager.checkDocExists( + this.project_id, + this.doc_id, + (err, exist) => { + exist.should.equal(true) + this.DocManager._getDoc + .calledWith(this.project_id, this.doc_id, { _id: 1, inS3: true }) + .should.equal(true) + return done() + } + ) + }) + + it('should return false when doc is not there', function(done) { + this.DocManager._getDoc.callsArgWith(3, null) + return this.DocManager.checkDocExists( + this.project_id, + this.doc_id, + (err, exist) => { + exist.should.equal(false) + return done() + } + ) + }) + + return it('should return error when get doc errors', function(done) { + this.DocManager._getDoc.callsArgWith(3, 'error') + return this.DocManager.checkDocExists( + this.project_id, + this.doc_id, + (err, exist) => { + err.should.equal('error') + return done() + } + ) + }) + }) + + describe('getFullDoc', function() { + beforeEach(function() { + this.DocManager._getDoc = sinon.stub() + return (this.doc = { + _id: this.doc_id, + lines: ['2134'] + }) + }) + + it('should call get doc with a quick filter', function(done) { + this.DocManager._getDoc.callsArgWith(3, null, this.doc) + return this.DocManager.getFullDoc( + this.project_id, + this.doc_id, + (err, doc) => { + doc.should.equal(this.doc) + this.DocManager._getDoc + .calledWith(this.project_id, this.doc_id, { + lines: true, + rev: true, + deleted: true, + version: true, + ranges: true, + inS3: true + }) + .should.equal(true) + return done() + } + ) + }) + + return it('should return error when get doc errors', function(done) { + this.DocManager._getDoc.callsArgWith(3, 'error') + return this.DocManager.getFullDoc( + this.project_id, + this.doc_id, + (err, exist) => { + err.should.equal('error') + return done() + } + ) + }) + }) + + describe('getRawDoc', function() { + beforeEach(function() { + this.DocManager._getDoc = sinon.stub() + return (this.doc = { lines: ['2134'] }) + }) + + it('should call get doc with a quick filter', function(done) { + this.DocManager._getDoc.callsArgWith(3, null, this.doc) + return this.DocManager.getDocLines( + this.project_id, + this.doc_id, + (err, doc) => { + doc.should.equal(this.doc) + this.DocManager._getDoc + .calledWith(this.project_id, this.doc_id, { + lines: true, + inS3: true + }) + .should.equal(true) + return done() + } + ) + }) + + return it('should return error when get doc errors', function(done) { + this.DocManager._getDoc.callsArgWith(3, 'error') + return this.DocManager.getDocLines( + this.project_id, + this.doc_id, + (err, exist) => { + err.should.equal('error') + return done() + } + ) + }) + }) + + describe('getDoc', function() { + beforeEach(function() { + this.project = { name: 'mock-project' } + this.doc = { + _id: this.doc_id, + project_id: this.project_id, + lines: ['mock-lines'] + } + this.version = 42 + this.MongoManager.findDoc = sinon.stub() + return (this.MongoManager.getDocVersion = sinon + .stub() + .yields(null, this.version)) + }) + + describe('when using a filter', function() { + beforeEach(function() { + return this.MongoManager.findDoc.yields(null, this.doc) + }) + + it('should error if inS3 is not set to true', function(done) { + return this.DocManager._getDoc( + this.project_id, + this.doc_id, + { inS3: false }, + err => { + expect(err).to.exist + return done() + } + ) + }) + + it('should always get inS3 even when no filter is passed', function(done) { + return this.DocManager._getDoc( + this.project_id, + this.doc_id, + undefined, + err => { + this.MongoManager.findDoc.called.should.equal(false) + expect(err).to.exist + return done() + } + ) + }) + + return it('should not error if inS3 is set to true', function(done) { + return this.DocManager._getDoc( + this.project_id, + this.doc_id, + { inS3: true }, + err => { + expect(err).to.not.exist + return done() + } + ) + }) + }) + + describe('when the doc is in the doc collection', function() { + beforeEach(function() { + this.MongoManager.findDoc.yields(null, this.doc) + return this.DocManager._getDoc( + this.project_id, + this.doc_id, + { version: true, inS3: true }, + this.callback + ) + }) + + it('should get the doc from the doc collection', function() { + return this.MongoManager.findDoc + .calledWith(this.project_id, this.doc_id) + .should.equal(true) + }) + + it('should get the doc version from the docOps collection', function() { + return this.MongoManager.getDocVersion + .calledWith(this.doc_id) + .should.equal(true) + }) + + return it('should return the callback with the doc with the version', function() { + this.callback.called.should.equal(true) + const doc = this.callback.args[0][1] + doc.lines.should.equal(this.doc.lines) + return doc.version.should.equal(this.version) + }) + }) + + describe('without the version filter', function() { + beforeEach(function() { + this.MongoManager.findDoc.yields(null, this.doc) + return this.DocManager._getDoc( + this.project_id, + this.doc_id, + { version: false, inS3: true }, + this.callback + ) + }) + + return it('should not get the doc version from the docOps collection', function() { + return this.MongoManager.getDocVersion.called.should.equal(false) + }) + }) + + describe('when MongoManager.findDoc errors', function() { + beforeEach(function() { + this.MongoManager.findDoc.yields(this.stubbedError) + return this.DocManager._getDoc( + this.project_id, + this.doc_id, + { version: true, inS3: true }, + this.callback + ) + }) + + return it('should return the error', function() { + return this.callback.calledWith(this.stubbedError).should.equal(true) + }) + }) + + describe('when the doc is archived', function() { + beforeEach(function() { + this.doc = { + _id: this.doc_id, + project_id: this.project_id, + lines: ['mock-lines'], + inS3: true + } + this.MongoManager.findDoc.yields(null, this.doc) + this.DocArchiveManager.unarchiveDoc = ( + project_id, + doc_id, + callback + ) => { + this.doc.inS3 = false + return callback() + } + sinon.spy(this.DocArchiveManager, 'unarchiveDoc') + return this.DocManager._getDoc( + this.project_id, + this.doc_id, + { version: true, inS3: true }, + this.callback + ) + }) + + it('should call the DocArchive to unarchive the doc', function() { + return this.DocArchiveManager.unarchiveDoc + .calledWith(this.project_id, this.doc_id) + .should.equal(true) + }) + + it('should look up the doc twice', function() { + return this.MongoManager.findDoc.calledTwice.should.equal(true) + }) + + return it('should return the doc', function() { + return this.callback.calledWith(null, this.doc).should.equal(true) + }) + }) + + return describe('when the doc does not exist in the docs collection', function() { + beforeEach(function() { + this.MongoManager.findDoc = sinon.stub().yields(null, null) + return this.DocManager._getDoc( + this.project_id, + this.doc_id, + { version: true, inS3: true }, + this.callback + ) + }) + + return it('should return a NotFoundError', function() { + return this.callback + .calledWith( + sinon.match.has( + 'message', + `No such doc: ${this.doc_id} in project ${this.project_id}` + ) + ) + .should.equal(true) + }) + }) + }) + + describe('getAllNonDeletedDocs', function() { + describe('when the project exists', function() { + beforeEach(function() { + this.docs = [ + { + _id: this.doc_id, + project_id: this.project_id, + lines: ['mock-lines'] + } + ] + this.MongoManager.getProjectsDocs = sinon + .stub() + .callsArgWith(3, null, this.docs) + this.DocArchiveManager.unArchiveAllDocs = sinon + .stub() + .callsArgWith(1, null, this.docs) + this.filter = { lines: true } + return this.DocManager.getAllNonDeletedDocs( + this.project_id, + this.filter, + this.callback + ) + }) + + it('should get the project from the database', function() { + return this.MongoManager.getProjectsDocs + .calledWith(this.project_id, { include_deleted: false }, this.filter) + .should.equal(true) + }) + + return it('should return the docs', function() { + return this.callback.calledWith(null, this.docs).should.equal(true) + }) + }) + + return describe('when there are no docs for the project', function() { + beforeEach(function() { + this.MongoManager.getProjectsDocs = sinon + .stub() + .callsArgWith(3, null, null) + this.DocArchiveManager.unArchiveAllDocs = sinon + .stub() + .callsArgWith(1, null) + return this.DocManager.getAllNonDeletedDocs( + this.project_id, + this.filter, + this.callback + ) + }) + + return it('should return a NotFoundError', function() { + return this.callback + .calledWith( + sinon.match.has('message', `No docs for project ${this.project_id}`) + ) + .should.equal(true) + }) + }) + }) + + describe('deleteDoc', function() { + describe('when the doc exists', function() { + beforeEach(function() { + this.lines = ['mock', 'doc', 'lines'] + this.rev = 77 + this.DocManager.checkDocExists = sinon + .stub() + .callsArgWith(2, null, true) + this.MongoManager.markDocAsDeleted = sinon.stub().callsArg(2) + return this.DocManager.deleteDoc( + this.project_id, + this.doc_id, + this.callback + ) + }) + + it('should get the doc', function() { + return this.DocManager.checkDocExists + .calledWith(this.project_id, this.doc_id) + .should.equal(true) + }) + + it('should mark doc as deleted', function() { + return this.MongoManager.markDocAsDeleted + .calledWith(this.project_id, this.doc_id) + .should.equal(true) + }) + + return it('should return the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + return describe('when the doc does not exist', function() { + beforeEach(function() { + this.DocManager.checkDocExists = sinon + .stub() + .callsArgWith(2, null, false) + return this.DocManager.deleteDoc( + this.project_id, + this.doc_id, + this.callback + ) + }) + + return it('should return a NotFoundError', function() { + return this.callback + .calledWith( + sinon.match.has( + 'message', + `No such project/doc to delete: ${this.project_id}/${this.doc_id}` + ) + ) + .should.equal(true) + }) + }) + }) + + return describe('updateDoc', function() { + beforeEach(function() { + this.oldDocLines = ['old', 'doc', 'lines'] + this.newDocLines = ['new', 'doc', 'lines'] + this.originalRanges = { + changes: [ + { + id: ObjectId().toString(), + op: { i: 'foo', p: 3 }, + meta: { + user_id: ObjectId().toString(), + ts: new Date().toString() + } + } + ] + } + this.newRanges = { + changes: [ + { + id: ObjectId().toString(), + op: { i: 'bar', p: 6 }, + meta: { + user_id: ObjectId().toString(), + ts: new Date().toString() + } + } + ] + } + this.version = 42 + this.doc = { + _id: this.doc_id, + project_id: this.project_id, + lines: this.oldDocLines, + rev: (this.rev = 5), + version: this.version, + ranges: this.originalRanges + } + + this.MongoManager.upsertIntoDocCollection = sinon.stub().callsArg(3) + this.MongoManager.setDocVersion = sinon.stub().yields() + return (this.DocManager._getDoc = sinon.stub()) + }) + + describe('when only the doc lines have changed', function() { + beforeEach(function() { + this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc) + return this.DocManager.updateDoc( + this.project_id, + this.doc_id, + this.newDocLines, + this.version, + this.originalRanges, + this.callback + ) + }) + + it('should get the existing doc', function() { + return this.DocManager._getDoc + .calledWith(this.project_id, this.doc_id, { + version: true, + rev: true, + lines: true, + version: true, + ranges: true, + inS3: true + }) + .should.equal(true) + }) + + it('should upsert the document to the doc collection', function() { + return this.MongoManager.upsertIntoDocCollection + .calledWith(this.project_id, this.doc_id, { lines: this.newDocLines }) + .should.equal(true) + }) + + it('should not update the version', function() { + return this.MongoManager.setDocVersion.called.should.equal(false) + }) + + return it('should return the callback with the new rev', function() { + return this.callback + .calledWith(null, true, this.rev + 1) + .should.equal(true) + }) + }) + + describe('when the doc ranges have changed', function() { + beforeEach(function() { + this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc) + this.RangeManager.shouldUpdateRanges.returns(true) + return this.DocManager.updateDoc( + this.project_id, + this.doc_id, + this.oldDocLines, + this.version, + this.newRanges, + this.callback + ) + }) + + it('should upsert the ranges', function() { + return this.MongoManager.upsertIntoDocCollection + .calledWith(this.project_id, this.doc_id, { ranges: this.newRanges }) + .should.equal(true) + }) + + it('should not update the version', function() { + return this.MongoManager.setDocVersion.called.should.equal(false) + }) + + return it('should return the callback with the new rev', function() { + return this.callback + .calledWith(null, true, this.rev + 1) + .should.equal(true) + }) + }) + + describe('when only the version has changed', function() { + beforeEach(function() { + this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc) + return this.DocManager.updateDoc( + this.project_id, + this.doc_id, + this.oldDocLines, + this.version + 1, + this.originalRanges, + this.callback + ) + }) + + it('should not change the lines or ranges', function() { + return this.MongoManager.upsertIntoDocCollection.called.should.equal( + false + ) + }) + + it('should update the version', function() { + return this.MongoManager.setDocVersion + .calledWith(this.doc_id, this.version + 1) + .should.equal(true) + }) + + return it('should return the callback with the old rev', function() { + return this.callback.calledWith(null, true, this.rev).should.equal(true) + }) + }) + + describe('when the doc has not changed at all', function() { + beforeEach(function() { + this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc) + return this.DocManager.updateDoc( + this.project_id, + this.doc_id, + this.oldDocLines, + this.version, + this.originalRanges, + this.callback + ) + }) + + it('should not update the ranges or lines', function() { + return this.MongoManager.upsertIntoDocCollection.called.should.equal( + false + ) + }) + + it('should not update the version', function() { + return this.MongoManager.setDocVersion.called.should.equal(false) + }) + + return it('should return the callback with the old rev and modified == false', function() { + return this.callback + .calledWith(null, false, this.rev) + .should.equal(true) + }) + }) + + describe('when the version is null', function() { + beforeEach(function() { + return this.DocManager.updateDoc( + this.project_id, + this.doc_id, + this.newDocLines, + null, + this.originalRanges, + this.callback + ) + }) + + return it('should return an error', function() { + return this.callback + .calledWith( + sinon.match.has('message', 'no lines, version or ranges provided') + ) + .should.equal(true) + }) + }) + + describe('when the lines are null', function() { + beforeEach(function() { + return this.DocManager.updateDoc( + this.project_id, + this.doc_id, + null, + this.version, + this.originalRanges, + this.callback + ) + }) + + return it('should return an error', function() { + return this.callback + .calledWith( + sinon.match.has('message', 'no lines, version or ranges provided') + ) + .should.equal(true) + }) + }) + + describe('when the ranges are null', function() { + beforeEach(function() { + return this.DocManager.updateDoc( + this.project_id, + this.doc_id, + this.newDocLines, + this.version, + null, + this.callback + ) + }) + + return it('should return an error', function() { + return this.callback + .calledWith( + sinon.match.has('message', 'no lines, version or ranges provided') + ) + .should.equal(true) + }) + }) + + describe('when there is a generic error getting the doc', function() { + beforeEach(function() { + this.error = new Error('doc could not be found') + this.DocManager._getDoc = sinon + .stub() + .callsArgWith(3, this.error, null, null) + return this.DocManager.updateDoc( + this.project_id, + this.doc_id, + this.newDocLines, + this.version, + this.originalRanges, + this.callback + ) + }) + + it('should not upsert the document to the doc collection', function() { + return this.MongoManager.upsertIntoDocCollection.called.should.equal( + false + ) + }) + + return it('should return the callback with the error', function() { + return this.callback.calledWith(this.error).should.equal(true) + }) + }) + + describe('when the doc lines have not changed', function() { + beforeEach(function() { + this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc) + return this.DocManager.updateDoc( + this.project_id, + this.doc_id, + this.oldDocLines.slice(), + this.version, + this.originalRanges, + this.callback + ) + }) + + it('should not update the doc', function() { + return this.MongoManager.upsertIntoDocCollection.called.should.equal( + false + ) + }) + + return it('should return the callback with the existing rev', function() { + return this.callback + .calledWith(null, false, this.rev) + .should.equal(true) + }) + }) + + return describe('when the doc does not exist', function() { + beforeEach(function() { + this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, null, null) + return this.DocManager.updateDoc( + this.project_id, + this.doc_id, + this.newDocLines, + this.version, + this.originalRanges, + this.callback + ) + }) + + it('should upsert the document to the doc collection', function() { + return this.MongoManager.upsertIntoDocCollection + .calledWith(this.project_id, this.doc_id, { + lines: this.newDocLines, + ranges: this.originalRanges + }) + .should.equal(true) + }) + + it('should set the version', function() { + return this.MongoManager.setDocVersion + .calledWith(this.doc_id, this.version) + .should.equal(true) + }) + + return it('should return the callback with the new rev', function() { + return this.callback.calledWith(null, true, 1).should.equal(true) + }) + }) + }) +}) diff --git a/services/docstore/test/unit/js/HttpControllerTests.js b/services/docstore/test/unit/js/HttpControllerTests.js index 27bb31a325..e282c247ff 100644 --- a/services/docstore/test/unit/js/HttpControllerTests.js +++ b/services/docstore/test/unit/js/HttpControllerTests.js @@ -9,442 +9,481 @@ * 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"); +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('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('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) + }) - 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) + }) - 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 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)) + }) - 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 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) + }) - 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) + }) + }) + }) - 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) + }) - 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 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) + }) - 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]}` + ) + }) + }) - 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) + }) - 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) + }) - 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 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) + }) - 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) + }) - 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) + }) + }) + }) - 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', function() { + return 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) + }) - describe("getAllRanges", function() { return 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) + }) - 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) + }) + }) + }) - 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("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) + }) - 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) + }) - 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) + }) + }) - 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) + }) - 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) + }) + }) - 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) + }) - 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) + }) - 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 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) + }) - 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) + }) - 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 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) + }) - 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) + }) - 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 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) + }) - 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) + }) - 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') + }) + }) + }) - 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) + }) - 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) + }) - 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) + }) + }) - 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) + }) - 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) + }) - 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.calledWith(204).should.equal(true) + }) + }) - return it("should return a 204 (No Content)", function() { - return this.res.send - .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) + }) - 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); - }); - }); -}); + 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) + }) + }) +}) diff --git a/services/docstore/test/unit/js/MongoManagerTests.js b/services/docstore/test/unit/js/MongoManagerTests.js index d7dabe3afb..0af25c5ce7 100644 --- a/services/docstore/test/unit/js/MongoManagerTests.js +++ b/services/docstore/test/unit/js/MongoManagerTests.js @@ -9,223 +9,285 @@ * 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 sinon = require('sinon'); -require('chai').should(); -const modulePath = require('path').join(__dirname, '../../../app/js/MongoManager'); -const { - ObjectId -} = require("mongojs"); -const { - assert -} = require("chai"); +const SandboxedModule = require('sandboxed-module') +const sinon = require('sinon') +require('chai').should() +const modulePath = require('path').join( + __dirname, + '../../../app/js/MongoManager' +) +const { ObjectId } = require('mongojs') +const { assert } = require('chai') -describe("MongoManager", function() { - beforeEach(function() { - this.MongoManager = SandboxedModule.require(modulePath, { requires: { - "./mongojs": { - db: (this.db = { docs: {}, docOps: {} }), - ObjectId - }, - 'metrics-sharelatex': {timeAsyncMethod: sinon.stub()}, - 'logger-sharelatex': {log(){}} - } - }); - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.callback = sinon.stub(); - return this.stubbedErr = new Error("hello world"); - }); +describe('MongoManager', function() { + beforeEach(function() { + this.MongoManager = SandboxedModule.require(modulePath, { + requires: { + './mongojs': { + db: (this.db = { docs: {}, docOps: {} }), + ObjectId + }, + 'metrics-sharelatex': { timeAsyncMethod: sinon.stub() }, + 'logger-sharelatex': { log() {} } + } + }) + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.callback = sinon.stub() + return (this.stubbedErr = new Error('hello world')) + }) - describe("findDoc", function() { - beforeEach(function() { - this.doc = { name: "mock-doc"}; - this.db.docs.find = sinon.stub().callsArgWith(2, null, [this.doc]); - this.filter = { lines: true }; - return this.MongoManager.findDoc(this.project_id, this.doc_id, this.filter, this.callback); - }); + describe('findDoc', function() { + beforeEach(function() { + this.doc = { name: 'mock-doc' } + this.db.docs.find = sinon.stub().callsArgWith(2, null, [this.doc]) + this.filter = { lines: true } + return this.MongoManager.findDoc( + this.project_id, + this.doc_id, + this.filter, + this.callback + ) + }) - it("should find the doc", function() { - return this.db.docs.find - .calledWith({ - _id: ObjectId(this.doc_id), - project_id: ObjectId(this.project_id) - }, this.filter) - .should.equal(true); - }); + it('should find the doc', function() { + return this.db.docs.find + .calledWith( + { + _id: ObjectId(this.doc_id), + project_id: ObjectId(this.project_id) + }, + this.filter + ) + .should.equal(true) + }) - return it("should call the callback with the doc", function() { - return this.callback.calledWith(null, this.doc).should.equal(true); - }); - }); + return it('should call the callback with the doc', function() { + return this.callback.calledWith(null, this.doc).should.equal(true) + }) + }) - describe("getProjectsDocs", function() { - beforeEach(function() { - this.filter = {lines: true}; - this.doc1 = { name: "mock-doc1" }; - this.doc2 = { name: "mock-doc2" }; - this.doc3 = { name: "mock-doc3" }; - this.doc4 = { name: "mock-doc4" }; - return this.db.docs.find = sinon.stub().callsArgWith(2, null, [this.doc, this.doc3, this.doc4]); - }); - - describe("with included_deleted = false", function() { - beforeEach(function() { - return this.MongoManager.getProjectsDocs(this.project_id, {include_deleted: false}, this.filter, this.callback); - }); + describe('getProjectsDocs', function() { + beforeEach(function() { + this.filter = { lines: true } + this.doc1 = { name: 'mock-doc1' } + this.doc2 = { name: 'mock-doc2' } + this.doc3 = { name: 'mock-doc3' } + this.doc4 = { name: 'mock-doc4' } + return (this.db.docs.find = sinon + .stub() + .callsArgWith(2, null, [this.doc, this.doc3, this.doc4])) + }) - it("should find the non-deleted docs via the project_id", function() { - return this.db.docs.find - .calledWith({ - project_id: ObjectId(this.project_id), - deleted: { $ne: true } - }, this.filter) - .should.equal(true); - }); + describe('with included_deleted = false', function() { + beforeEach(function() { + return this.MongoManager.getProjectsDocs( + this.project_id, + { include_deleted: false }, + this.filter, + this.callback + ) + }) - return it("should call the callback with the docs", function() { - return this.callback.calledWith(null, [this.doc, this.doc3, this.doc4]).should.equal(true); - }); - }); - - return describe("with included_deleted = true", function() { - beforeEach(function() { - return this.MongoManager.getProjectsDocs(this.project_id, {include_deleted: true}, this.filter, this.callback); - }); + it('should find the non-deleted docs via the project_id', function() { + return this.db.docs.find + .calledWith( + { + project_id: ObjectId(this.project_id), + deleted: { $ne: true } + }, + this.filter + ) + .should.equal(true) + }) - it("should find all via the project_id", function() { - return this.db.docs.find - .calledWith({ - project_id: ObjectId(this.project_id) - }, this.filter) - .should.equal(true); - }); + return it('should call the callback with the docs', function() { + return this.callback + .calledWith(null, [this.doc, this.doc3, this.doc4]) + .should.equal(true) + }) + }) - return it("should call the callback with the docs", function() { - return this.callback.calledWith(null, [this.doc, this.doc3, this.doc4]).should.equal(true); - }); - }); - }); + return describe('with included_deleted = true', function() { + beforeEach(function() { + return this.MongoManager.getProjectsDocs( + this.project_id, + { include_deleted: true }, + this.filter, + this.callback + ) + }) - describe("upsertIntoDocCollection", function() { - beforeEach(function() { - this.db.docs.update = sinon.stub().callsArgWith(3, this.stubbedErr); - return this.oldRev = 77; - }); + it('should find all via the project_id', function() { + return this.db.docs.find + .calledWith( + { + project_id: ObjectId(this.project_id) + }, + this.filter + ) + .should.equal(true) + }) - it("should upsert the document", function(done){ - return this.MongoManager.upsertIntoDocCollection(this.project_id, this.doc_id, {lines: this.lines}, err=> { - const args = this.db.docs.update.args[0]; - assert.deepEqual(args[0], {_id: ObjectId(this.doc_id)}); - assert.equal(args[1].$set.lines, this.lines); - assert.equal(args[1].$inc.rev, 1); - assert.deepEqual(args[1].$set.project_id, ObjectId(this.project_id)); - return done(); - }); - }); + return it('should call the callback with the docs', function() { + return this.callback + .calledWith(null, [this.doc, this.doc3, this.doc4]) + .should.equal(true) + }) + }) + }) - return it("should return the error", function(done){ - return this.MongoManager.upsertIntoDocCollection(this.project_id, this.doc_id, {lines: this.lines}, err=> { - err.should.equal(this.stubbedErr); - return done(); - }); - }); - }); + describe('upsertIntoDocCollection', function() { + beforeEach(function() { + this.db.docs.update = sinon.stub().callsArgWith(3, this.stubbedErr) + return (this.oldRev = 77) + }) - describe("markDocAsDeleted", function() { - beforeEach(function() { - this.db.docs.update = sinon.stub().callsArgWith(2, this.stubbedErr); - return this.oldRev = 77; - }); + it('should upsert the document', function(done) { + return this.MongoManager.upsertIntoDocCollection( + this.project_id, + this.doc_id, + { lines: this.lines }, + err => { + const args = this.db.docs.update.args[0] + assert.deepEqual(args[0], { _id: ObjectId(this.doc_id) }) + assert.equal(args[1].$set.lines, this.lines) + assert.equal(args[1].$inc.rev, 1) + assert.deepEqual(args[1].$set.project_id, ObjectId(this.project_id)) + return done() + } + ) + }) - it("should process the update", function(done) { - return this.MongoManager.markDocAsDeleted(this.project_id, this.doc_id, err=> { - const args = this.db.docs.update.args[0]; - assert.deepEqual(args[0], {_id: ObjectId(this.doc_id), project_id: ObjectId(this.project_id)}); - assert.equal(args[1].$set.deleted, true); - return done(); - }); - }); + return it('should return the error', function(done) { + return this.MongoManager.upsertIntoDocCollection( + this.project_id, + this.doc_id, + { lines: this.lines }, + err => { + err.should.equal(this.stubbedErr) + return done() + } + ) + }) + }) - return it("should return the error", function(done){ - return this.MongoManager.markDocAsDeleted(this.project_id, this.doc_id, err=> { - err.should.equal(this.stubbedErr); - return done(); - }); - }); - }); + describe('markDocAsDeleted', function() { + beforeEach(function() { + this.db.docs.update = sinon.stub().callsArgWith(2, this.stubbedErr) + return (this.oldRev = 77) + }) - describe("destroyDoc", function() { - beforeEach(function(done) { - this.db.docs.remove = sinon.stub().yields(); - this.db.docOps.remove = sinon.stub().yields(); - return this.MongoManager.destroyDoc('123456789012', done); - }); + it('should process the update', function(done) { + return this.MongoManager.markDocAsDeleted( + this.project_id, + this.doc_id, + err => { + const args = this.db.docs.update.args[0] + assert.deepEqual(args[0], { + _id: ObjectId(this.doc_id), + project_id: ObjectId(this.project_id) + }) + assert.equal(args[1].$set.deleted, true) + return done() + } + ) + }) - it("should destroy the doc", function() { - return sinon.assert.calledWith(this.db.docs.remove, {_id: ObjectId('123456789012')}); - }); + return it('should return the error', function(done) { + return this.MongoManager.markDocAsDeleted( + this.project_id, + this.doc_id, + err => { + err.should.equal(this.stubbedErr) + return done() + } + ) + }) + }) - return it("should destroy the docOps", function() { - return sinon.assert.calledWith(this.db.docOps.remove, {doc_id: ObjectId('123456789012')}); - }); - }); + describe('destroyDoc', function() { + beforeEach(function(done) { + this.db.docs.remove = sinon.stub().yields() + this.db.docOps.remove = sinon.stub().yields() + return this.MongoManager.destroyDoc('123456789012', done) + }) - describe("getDocVersion", function() { - describe("when the doc exists", function() { - beforeEach(function() { - this.doc = - {version: (this.version = 42)}; - this.db.docOps.find = sinon.stub().callsArgWith(2, null, [this.doc]); - return this.MongoManager.getDocVersion(this.doc_id, this.callback); - }); + it('should destroy the doc', function() { + return sinon.assert.calledWith(this.db.docs.remove, { + _id: ObjectId('123456789012') + }) + }) - it("should look for the doc in the database", function() { - return this.db.docOps.find - .calledWith({ doc_id: ObjectId(this.doc_id) }, {version: 1}) - .should.equal(true); - }); + return it('should destroy the docOps', function() { + return sinon.assert.calledWith(this.db.docOps.remove, { + doc_id: ObjectId('123456789012') + }) + }) + }) - return it("should call the callback with the version", function() { - return this.callback.calledWith(null, this.version).should.equal(true); - }); - }); + describe('getDocVersion', function() { + describe('when the doc exists', function() { + beforeEach(function() { + this.doc = { version: (this.version = 42) } + this.db.docOps.find = sinon.stub().callsArgWith(2, null, [this.doc]) + return this.MongoManager.getDocVersion(this.doc_id, this.callback) + }) - return describe("when the doc doesn't exist", function() { - beforeEach(function() { - this.db.docOps.find = sinon.stub().callsArgWith(2, null, []); - return this.MongoManager.getDocVersion(this.doc_id, this.callback); - }); + it('should look for the doc in the database', function() { + return this.db.docOps.find + .calledWith({ doc_id: ObjectId(this.doc_id) }, { version: 1 }) + .should.equal(true) + }) - return it("should call the callback with 0", function() { - return this.callback.calledWith(null, 0).should.equal(true); - }); - }); - }); + return it('should call the callback with the version', function() { + return this.callback.calledWith(null, this.version).should.equal(true) + }) + }) - return describe("setDocVersion", function() { - beforeEach(function() { - this.version = 42; - this.db.docOps.update = sinon.stub().callsArg(3); - return this.MongoManager.setDocVersion(this.doc_id, this.version, this.callback); - }); + return describe("when the doc doesn't exist", function() { + beforeEach(function() { + this.db.docOps.find = sinon.stub().callsArgWith(2, null, []) + return this.MongoManager.getDocVersion(this.doc_id, this.callback) + }) - it("should update the doc version", function() { - return this.db.docOps.update - .calledWith({ - doc_id: ObjectId(this.doc_id) - }, { - $set: { - version: this.version - } - }, { - upsert: true - }) - .should.equal(true); - }); + return it('should call the callback with 0', function() { + return this.callback.calledWith(null, 0).should.equal(true) + }) + }) + }) - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); -}); + return describe('setDocVersion', function() { + beforeEach(function() { + this.version = 42 + this.db.docOps.update = sinon.stub().callsArg(3) + return this.MongoManager.setDocVersion( + this.doc_id, + this.version, + this.callback + ) + }) + + it('should update the doc version', function() { + return this.db.docOps.update + .calledWith( + { + doc_id: ObjectId(this.doc_id) + }, + { + $set: { + version: this.version + } + }, + { + upsert: true + } + ) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) +}) diff --git a/services/docstore/test/unit/js/RangeManagerTests.js b/services/docstore/test/unit/js/RangeManagerTests.js index b67e535181..ff6d96a0d1 100644 --- a/services/docstore/test/unit/js/RangeManagerTests.js +++ b/services/docstore/test/unit/js/RangeManagerTests.js @@ -10,190 +10,248 @@ * 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 sinon = require('sinon'); -require('chai').should(); -const { - expect -} = require('chai'); -const modulePath = require('path').join(__dirname, '../../../app/js/RangeManager'); -const { - ObjectId -} = require("mongojs"); -const { - assert -} = require("chai"); -const _ = require("underscore"); +const SandboxedModule = require('sandboxed-module') +const sinon = require('sinon') +require('chai').should() +const { expect } = require('chai') +const modulePath = require('path').join( + __dirname, + '../../../app/js/RangeManager' +) +const { ObjectId } = require('mongojs') +const { assert } = require('chai') +const _ = require('underscore') -describe("RangeManager", function() { - beforeEach(function() { - return this.RangeManager = SandboxedModule.require(modulePath, { requires: { - "./mongojs": { - ObjectId - } - } - } - ); - }); +describe('RangeManager', function() { + beforeEach(function() { + return (this.RangeManager = SandboxedModule.require(modulePath, { + requires: { + './mongojs': { + ObjectId + } + } + })) + }) - describe("jsonRangesToMongo", function() { - it("should convert ObjectIds and dates to proper objects", function() { - const change_id = ObjectId().toString(); - const comment_id = ObjectId().toString(); - const user_id = ObjectId().toString(); - const thread_id = ObjectId().toString(); - const ts = new Date().toJSON(); - return this.RangeManager.jsonRangesToMongo({ - changes: [{ - id: change_id, - op: { i: "foo", p: 3 }, - metadata: { - user_id, - ts - } - }], - comments: [{ - id: comment_id, - op: { c: "foo", p: 3, t: thread_id } - }] - }).should.deep.equal({ - changes: [{ - id: ObjectId(change_id), - op: { i: "foo", p: 3 }, - metadata: { - user_id: ObjectId(user_id), - ts: new Date(ts) - } - }], - comments: [{ - id: ObjectId(comment_id), - op: { c: "foo", p: 3, t: ObjectId(thread_id) } - }] - }); - }); - - it("should leave malformed ObjectIds as they are", function() { - const change_id = "foo"; - const comment_id = "bar"; - const user_id = "baz"; - return this.RangeManager.jsonRangesToMongo({ - changes: [{ - id: change_id, - metadata: { - user_id - } - }], - comments: [{ - id: comment_id - }] - }).should.deep.equal({ - changes: [{ - id: change_id, - metadata: { - user_id - } - }], - comments: [{ - id: comment_id - }] - }); - }); - - return it("should be consistent when transformed through json -> mongo -> json", function() { - const change_id = ObjectId().toString(); - const comment_id = ObjectId().toString(); - const user_id = ObjectId().toString(); - const thread_id = ObjectId().toString(); - const ts = new Date().toJSON(); - const ranges1 = { - changes: [{ - id: change_id, - op: { i: "foo", p: 3 }, - metadata: { - user_id, - ts - } - }], - comments: [{ - id: comment_id, - op: { c: "foo", p: 3, t: thread_id } - }] - }; - const ranges1_copy = JSON.parse(JSON.stringify(ranges1)); // jsonRangesToMongo modifies in place - const ranges2 = JSON.parse(JSON.stringify(this.RangeManager.jsonRangesToMongo(ranges1_copy))); - return ranges1.should.deep.equal(ranges2); - }); - }); - - return describe("shouldUpdateRanges", function() { - beforeEach(function() { - this.ranges = { - changes: [{ - id: ObjectId(), - op: { i: "foo", p: 3 }, - metadata: { - user_id: ObjectId(), - ts: new Date() - } - }], - comments: [{ - id: ObjectId(), - op: { c: "foo", p: 3, t: ObjectId() } - }] - }; - return this.ranges_copy = this.RangeManager.jsonRangesToMongo(JSON.parse(JSON.stringify(this.ranges))); - }); + describe('jsonRangesToMongo', function() { + it('should convert ObjectIds and dates to proper objects', function() { + const change_id = ObjectId().toString() + const comment_id = ObjectId().toString() + const user_id = ObjectId().toString() + const thread_id = ObjectId().toString() + const ts = new Date().toJSON() + return this.RangeManager.jsonRangesToMongo({ + changes: [ + { + id: change_id, + op: { i: 'foo', p: 3 }, + metadata: { + user_id, + ts + } + } + ], + comments: [ + { + id: comment_id, + op: { c: 'foo', p: 3, t: thread_id } + } + ] + }).should.deep.equal({ + changes: [ + { + id: ObjectId(change_id), + op: { i: 'foo', p: 3 }, + metadata: { + user_id: ObjectId(user_id), + ts: new Date(ts) + } + } + ], + comments: [ + { + id: ObjectId(comment_id), + op: { c: 'foo', p: 3, t: ObjectId(thread_id) } + } + ] + }) + }) - describe("with a blank new range", function() { return it("should throw an error", function() { - return expect(() => { - return this.RangeManager.shouldUpdateRanges(this.ranges, null); - }).to.throw(Error); - }); }); - - describe("with a blank old range", function() { return it("should treat it like {}", function() { - this.RangeManager.shouldUpdateRanges(null, {}).should.equal(false); - return this.RangeManager.shouldUpdateRanges(null, this.ranges).should.equal(true); - }); }); - - describe("with no changes", function() { return it("should return false", function() { - return this.RangeManager.shouldUpdateRanges(this.ranges, this.ranges_copy).should.equal(false); - }); }); - - return describe("with changes", function() { - it("should return true when the change id changes", function() { - this.ranges_copy.changes[0].id = ObjectId(); - return this.RangeManager.shouldUpdateRanges(this.ranges, this.ranges_copy).should.equal(true); - }); + it('should leave malformed ObjectIds as they are', function() { + const change_id = 'foo' + const comment_id = 'bar' + const user_id = 'baz' + return this.RangeManager.jsonRangesToMongo({ + changes: [ + { + id: change_id, + metadata: { + user_id + } + } + ], + comments: [ + { + id: comment_id + } + ] + }).should.deep.equal({ + changes: [ + { + id: change_id, + metadata: { + user_id + } + } + ], + comments: [ + { + id: comment_id + } + ] + }) + }) - it("should return true when the change user id changes", function() { - this.ranges_copy.changes[0].metadata.user_id = ObjectId(); - return this.RangeManager.shouldUpdateRanges(this.ranges, this.ranges_copy).should.equal(true); - }); + return it('should be consistent when transformed through json -> mongo -> json', function() { + const change_id = ObjectId().toString() + const comment_id = ObjectId().toString() + const user_id = ObjectId().toString() + const thread_id = ObjectId().toString() + const ts = new Date().toJSON() + const ranges1 = { + changes: [ + { + id: change_id, + op: { i: 'foo', p: 3 }, + metadata: { + user_id, + ts + } + } + ], + comments: [ + { + id: comment_id, + op: { c: 'foo', p: 3, t: thread_id } + } + ] + } + const ranges1_copy = JSON.parse(JSON.stringify(ranges1)) // jsonRangesToMongo modifies in place + const ranges2 = JSON.parse( + JSON.stringify(this.RangeManager.jsonRangesToMongo(ranges1_copy)) + ) + return ranges1.should.deep.equal(ranges2) + }) + }) - it("should return true when the change ts changes", function() { - this.ranges_copy.changes[0].metadata.ts = new Date(Date.now() + 1000); - return this.RangeManager.shouldUpdateRanges(this.ranges, this.ranges_copy).should.equal(true); - }); + return describe('shouldUpdateRanges', function() { + beforeEach(function() { + this.ranges = { + changes: [ + { + id: ObjectId(), + op: { i: 'foo', p: 3 }, + metadata: { + user_id: ObjectId(), + ts: new Date() + } + } + ], + comments: [ + { + id: ObjectId(), + op: { c: 'foo', p: 3, t: ObjectId() } + } + ] + } + return (this.ranges_copy = this.RangeManager.jsonRangesToMongo( + JSON.parse(JSON.stringify(this.ranges)) + )) + }) - it("should return true when the change op changes", function() { - this.ranges_copy.changes[0].op.i = "bar"; - return this.RangeManager.shouldUpdateRanges(this.ranges, this.ranges_copy).should.equal(true); - }); + describe('with a blank new range', function() { + return it('should throw an error', function() { + return expect(() => { + return this.RangeManager.shouldUpdateRanges(this.ranges, null) + }).to.throw(Error) + }) + }) - it("should return true when the comment id changes", function() { - this.ranges_copy.comments[0].id = ObjectId(); - return this.RangeManager.shouldUpdateRanges(this.ranges, this.ranges_copy).should.equal(true); - }); + describe('with a blank old range', function() { + return it('should treat it like {}', function() { + this.RangeManager.shouldUpdateRanges(null, {}).should.equal(false) + return this.RangeManager.shouldUpdateRanges( + null, + this.ranges + ).should.equal(true) + }) + }) - it("should return true when the comment offset changes", function() { - this.ranges_copy.comments[0].op.p = 17; - return this.RangeManager.shouldUpdateRanges(this.ranges, this.ranges_copy).should.equal(true); - }); + describe('with no changes', function() { + return it('should return false', function() { + return this.RangeManager.shouldUpdateRanges( + this.ranges, + this.ranges_copy + ).should.equal(false) + }) + }) - return it("should return true when the comment content changes", function() { - this.ranges_copy.comments[0].op.c = "bar"; - return this.RangeManager.shouldUpdateRanges(this.ranges, this.ranges_copy).should.equal(true); - }); - }); - }); -}); \ No newline at end of file + return describe('with changes', function() { + it('should return true when the change id changes', function() { + this.ranges_copy.changes[0].id = ObjectId() + return this.RangeManager.shouldUpdateRanges( + this.ranges, + this.ranges_copy + ).should.equal(true) + }) + + it('should return true when the change user id changes', function() { + this.ranges_copy.changes[0].metadata.user_id = ObjectId() + return this.RangeManager.shouldUpdateRanges( + this.ranges, + this.ranges_copy + ).should.equal(true) + }) + + it('should return true when the change ts changes', function() { + this.ranges_copy.changes[0].metadata.ts = new Date(Date.now() + 1000) + return this.RangeManager.shouldUpdateRanges( + this.ranges, + this.ranges_copy + ).should.equal(true) + }) + + it('should return true when the change op changes', function() { + this.ranges_copy.changes[0].op.i = 'bar' + return this.RangeManager.shouldUpdateRanges( + this.ranges, + this.ranges_copy + ).should.equal(true) + }) + + it('should return true when the comment id changes', function() { + this.ranges_copy.comments[0].id = ObjectId() + return this.RangeManager.shouldUpdateRanges( + this.ranges, + this.ranges_copy + ).should.equal(true) + }) + + it('should return true when the comment offset changes', function() { + this.ranges_copy.comments[0].op.p = 17 + return this.RangeManager.shouldUpdateRanges( + this.ranges, + this.ranges_copy + ).should.equal(true) + }) + + return it('should return true when the comment content changes', function() { + this.ranges_copy.comments[0].op.c = 'bar' + return this.RangeManager.shouldUpdateRanges( + this.ranges, + this.ranges_copy + ).should.equal(true) + }) + }) + }) +})