| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- import sinon from 'sinon'
- import { strict as esmock } from 'esmock'
- import mongodb from 'mongodb-legacy'
- const { ObjectId } = mongodb
- const MODULE_PATH = '../../../../app/js/HttpController.js'
- describe('HttpController', function () {
- beforeEach(async function () {
- this.UpdatesProcessor = {
- processUpdatesForProject: sinon.stub().yields(),
- }
- this.SummarizedUpdatesManager = {
- getSummarizedProjectUpdates: sinon.stub(),
- }
- this.DiffManager = {
- getDiff: sinon.stub(),
- }
- this.HistoryStoreManager = {
- deleteProject: sinon.stub().yields(),
- getMostRecentVersion: sinon.stub(),
- getProjectBlobStream: sinon.stub(),
- initializeProject: sinon.stub(),
- }
- this.SnapshotManager = {
- getFileSnapshotStream: sinon.stub(),
- getProjectSnapshot: sinon.stub(),
- }
- this.HealthChecker = {}
- this.SyncManager = {
- clearResyncState: sinon.stub().yields(),
- startResync: sinon.stub().yields(),
- }
- this.WebApiManager = {
- getHistoryId: sinon.stub(),
- }
- this.RedisManager = {
- destroyDocUpdatesQueue: sinon.stub().yields(),
- clearFirstOpTimestamp: sinon.stub().yields(),
- clearCachedHistoryId: sinon.stub().yields(),
- }
- this.ErrorRecorder = {
- record: sinon.stub().yields(),
- }
- this.LabelsManager = {
- createLabel: sinon.stub(),
- deleteLabel: sinon.stub().yields(),
- deleteLabelForUser: sinon.stub().yields(),
- getLabels: sinon.stub(),
- }
- this.HistoryApiManager = {
- shouldUseProjectHistory: sinon.stub(),
- }
- this.RetryManager = {}
- this.FlushManager = {}
- this.request = {}
- this.pipeline = sinon.stub()
- this.HttpController = await esmock(MODULE_PATH, {
- request: this.request,
- stream: { pipeline: this.pipeline },
- '../../../../app/js/UpdatesProcessor.js': this.UpdatesProcessor,
- '../../../../app/js/SummarizedUpdatesManager.js':
- this.SummarizedUpdatesManager,
- '../../../../app/js/DiffManager.js': this.DiffManager,
- '../../../../app/js/HistoryStoreManager.js': this.HistoryStoreManager,
- '../../../../app/js/SnapshotManager.js': this.SnapshotManager,
- '../../../../app/js/HealthChecker.js': this.HealthChecker,
- '../../../../app/js/SyncManager.js': this.SyncManager,
- '../../../../app/js/WebApiManager.js': this.WebApiManager,
- '../../../../app/js/RedisManager.js': this.RedisManager,
- '../../../../app/js/ErrorRecorder.js': this.ErrorRecorder,
- '../../../../app/js/LabelsManager.js': this.LabelsManager,
- '../../../../app/js/HistoryApiManager.js': this.HistoryApiManager,
- '../../../../app/js/RetryManager.js': this.RetryManager,
- '../../../../app/js/FlushManager.js': this.FlushManager,
- })
- this.pathname = 'doc-id-123'
- this.projectId = new ObjectId().toString()
- this.projectOwnerId = new ObjectId().toString()
- this.next = sinon.stub()
- this.userId = new ObjectId().toString()
- this.now = Date.now()
- this.res = {
- json: sinon.stub(),
- send: sinon.stub(),
- sendStatus: sinon.stub(),
- setHeader: sinon.stub(),
- }
- })
- describe('getProjectBlob', function () {
- beforeEach(function () {
- this.blobHash = 'abcd'
- this.stream = {}
- this.historyId = 1337
- this.HistoryStoreManager.getProjectBlobStream.yields(null, this.stream)
- this.HttpController.getProjectBlob(
- { params: { history_id: this.historyId, hash: this.blobHash } },
- this.res,
- this.next
- )
- })
- it('should get a blob stream', function () {
- this.HistoryStoreManager.getProjectBlobStream
- .calledWith(this.historyId, this.blobHash)
- .should.equal(true)
- this.pipeline.should.have.been.calledWith(this.stream, this.res)
- })
- it('should set caching header', function () {
- this.res.setHeader.should.have.been.calledWith(
- 'Cache-Control',
- 'private, max-age=86400'
- )
- })
- })
- describe('initializeProject', function () {
- beforeEach(function () {
- this.historyId = new ObjectId().toString()
- this.req = { body: { historyId: this.historyId } }
- this.HistoryStoreManager.initializeProject.yields(null, this.historyId)
- this.HttpController.initializeProject(this.req, this.res, this.next)
- })
- it('should initialize the project', function () {
- this.HistoryStoreManager.initializeProject.calledWith().should.equal(true)
- })
- it('should return the new overleaf id', function () {
- this.res.json
- .calledWith({ project: { id: this.historyId } })
- .should.equal(true)
- })
- })
- describe('flushProject', function () {
- beforeEach(function () {
- this.req = {
- params: {
- project_id: this.projectId,
- },
- query: {},
- }
- this.HttpController.flushProject(this.req, this.res, this.next)
- })
- it('should process the updates', function () {
- this.UpdatesProcessor.processUpdatesForProject
- .calledWith(this.projectId)
- .should.equal(true)
- })
- it('should return a success code', function () {
- this.res.sendStatus.calledWith(204).should.equal(true)
- })
- })
- describe('getDiff', function () {
- beforeEach(function () {
- this.from = 42
- this.to = 45
- this.req = {
- params: {
- project_id: this.projectId,
- },
- query: {
- pathname: this.pathname,
- from: this.from,
- to: this.to,
- },
- }
- this.diff = [{ u: 'mock-diff' }]
- this.DiffManager.getDiff.yields(null, this.diff)
- this.HttpController.getDiff(this.req, this.res, this.next)
- })
- it('should get the diff', function () {
- this.DiffManager.getDiff.should.have.been.calledWith(
- this.projectId,
- this.pathname,
- this.from,
- this.to
- )
- })
- it('should return the diff', function () {
- this.res.json.calledWith({ diff: this.diff }).should.equal(true)
- })
- })
- describe('getUpdates', function () {
- beforeEach(function () {
- this.before = Date.now()
- this.nextBeforeTimestamp = this.before - 100
- this.min_count = 10
- this.req = {
- params: {
- project_id: this.projectId,
- },
- query: {
- before: this.before,
- min_count: this.min_count,
- },
- }
- this.updates = [{ i: 'mock-summarized-updates', p: 10 }]
- this.SummarizedUpdatesManager.getSummarizedProjectUpdates.yields(
- null,
- this.updates,
- this.nextBeforeTimestamp
- )
- this.HttpController.getUpdates(this.req, this.res, this.next)
- })
- it('should get the updates', function () {
- this.SummarizedUpdatesManager.getSummarizedProjectUpdates.should.have.been.calledWith(
- this.projectId,
- {
- before: this.before,
- min_count: this.min_count,
- }
- )
- })
- it('should return the formatted updates', function () {
- this.res.json.should.have.been.calledWith({
- updates: this.updates,
- nextBeforeTimestamp: this.nextBeforeTimestamp,
- })
- })
- })
- describe('latestVersion', function () {
- beforeEach(function () {
- this.historyId = 1234
- this.req = {
- params: {
- project_id: this.projectId,
- },
- }
- this.version = 99
- this.lastChange = {
- v2Authors: ['1234'],
- timestamp: '2016-08-16T10:44:40.227Z',
- }
- this.versionInfo = {
- version: this.version,
- v2Authors: ['1234'],
- timestamp: '2016-08-16T10:44:40.227Z',
- }
- this.WebApiManager.getHistoryId.yields(null, this.historyId)
- this.HistoryStoreManager.getMostRecentVersion.yields(
- null,
- this.version,
- {},
- this.lastChange
- )
- this.HttpController.latestVersion(this.req, this.res, this.next)
- })
- it('should process the updates', function () {
- this.UpdatesProcessor.processUpdatesForProject
- .calledWith(this.projectId)
- .should.equal(true)
- })
- it('should get the ol project id', function () {
- this.WebApiManager.getHistoryId
- .calledWith(this.projectId)
- .should.equal(true)
- })
- it('should get the latest version', function () {
- this.HistoryStoreManager.getMostRecentVersion
- .calledWith(this.projectId, this.historyId)
- .should.equal(true)
- })
- it('should return version number', function () {
- this.res.json.calledWith(this.versionInfo).should.equal(true)
- })
- })
- describe('resyncProject', function () {
- beforeEach(function () {
- this.req = {
- params: {
- project_id: this.projectId,
- },
- query: {},
- body: {},
- }
- this.HttpController.resyncProject(this.req, this.res, this.next)
- })
- it('should resync the project', function () {
- this.SyncManager.startResync.calledWith(this.projectId).should.equal(true)
- })
- it('should flush the queue', function () {
- this.UpdatesProcessor.processUpdatesForProject
- .calledWith(this.projectId)
- .should.equal(true)
- })
- it('should return 204', function () {
- this.res.sendStatus.calledWith(204).should.equal(true)
- })
- })
- describe('getFileSnapshot', function () {
- beforeEach(function () {
- this.version = 42
- this.pathname = 'foo.tex'
- this.req = {
- params: {
- project_id: this.projectId,
- version: this.version,
- pathname: this.pathname,
- },
- }
- this.res = { mock: 'res' }
- this.stream = {}
- this.SnapshotManager.getFileSnapshotStream.yields(null, this.stream)
- this.HttpController.getFileSnapshot(this.req, this.res, this.next)
- })
- it('should get the snapshot', function () {
- this.SnapshotManager.getFileSnapshotStream.should.have.been.calledWith(
- this.projectId,
- this.version,
- this.pathname
- )
- })
- it('should pipe the returned stream into the response', function () {
- this.pipeline.should.have.been.calledWith(this.stream, this.res)
- })
- })
- describe('getProjectSnapshot', function () {
- beforeEach(function () {
- this.version = 42
- this.req = {
- params: {
- project_id: this.projectId,
- version: this.version,
- },
- }
- this.res = { json: sinon.stub() }
- this.snapshotData = { one: 1 }
- this.SnapshotManager.getProjectSnapshot.yields(null, this.snapshotData)
- this.HttpController.getProjectSnapshot(this.req, this.res, this.next)
- })
- it('should get the snapshot', function () {
- this.SnapshotManager.getProjectSnapshot.should.have.been.calledWith(
- this.projectId,
- this.version
- )
- })
- it('should send json response', function () {
- this.res.json.calledWith(this.snapshotData).should.equal(true)
- })
- })
- describe('getLabels', function () {
- beforeEach(function () {
- this.req = {
- params: {
- project_id: this.projectId,
- },
- }
- this.labels = ['label-1', 'label-2']
- this.LabelsManager.getLabels.yields(null, this.labels)
- })
- describe('project history is enabled', function () {
- beforeEach(function () {
- this.HistoryApiManager.shouldUseProjectHistory.yields(null, true)
- this.HttpController.getLabels(this.req, this.res, this.next)
- })
- it('should get the labels for a project', function () {
- this.LabelsManager.getLabels
- .calledWith(this.projectId)
- .should.equal(true)
- })
- it('should return the labels', function () {
- this.res.json.calledWith(this.labels).should.equal(true)
- })
- })
- describe('project history is not enabled', function () {
- beforeEach(function () {
- this.HistoryApiManager.shouldUseProjectHistory.yields(null, false)
- this.HttpController.getLabels(this.req, this.res, this.next)
- })
- it('should return 409', function () {
- this.res.sendStatus.calledWith(409).should.equal(true)
- })
- })
- })
- describe('createLabel', function () {
- beforeEach(function () {
- this.req = {
- params: {
- project_id: this.projectId,
- },
- body: {
- version: (this.version = 'label-1'),
- comment: (this.comment = 'a comment'),
- created_at: (this.created_at = Date.now().toString()),
- validate_exists: true,
- user_id: this.userId,
- },
- }
- this.label = { _id: new ObjectId() }
- this.LabelsManager.createLabel.yields(null, this.label)
- })
- describe('project history is enabled', function () {
- beforeEach(function () {
- this.HistoryApiManager.shouldUseProjectHistory.yields(null, true)
- this.HttpController.createLabel(this.req, this.res, this.next)
- })
- it('should create a label for a project', function () {
- this.LabelsManager.createLabel.should.have.been.calledWith(
- this.projectId,
- this.userId,
- this.version,
- this.comment,
- this.created_at,
- true
- )
- })
- it('should return the label', function () {
- this.res.json.calledWith(this.label).should.equal(true)
- })
- })
- describe('validate_exists = false is passed', function () {
- beforeEach(function () {
- this.req.body.validate_exists = false
- this.HistoryApiManager.shouldUseProjectHistory.yields(null, true)
- this.HttpController.createLabel(this.req, this.res, this.next)
- })
- it('should create a label for a project', function () {
- this.LabelsManager.createLabel
- .calledWith(
- this.projectId,
- this.userId,
- this.version,
- this.comment,
- this.created_at,
- false
- )
- .should.equal(true)
- })
- it('should return the label', function () {
- this.res.json.calledWith(this.label).should.equal(true)
- })
- })
- describe('project history is not enabled', function () {
- beforeEach(function () {
- this.HistoryApiManager.shouldUseProjectHistory.yields(null, false)
- this.HttpController.createLabel(this.req, this.res, this.next)
- })
- it('should return 409', function () {
- this.res.sendStatus.calledWith(409).should.equal(true)
- })
- })
- })
- describe('deleteLabelForUser', function () {
- beforeEach(function () {
- this.req = {
- params: {
- project_id: this.projectId,
- user_id: this.userId,
- label_id: (this.label_id = new ObjectId()),
- },
- }
- this.HttpController.deleteLabelForUser(this.req, this.res, this.next)
- })
- it('should delete a label for a project', function () {
- this.LabelsManager.deleteLabelForUser
- .calledWith(this.projectId, this.userId, this.label_id)
- .should.equal(true)
- })
- it('should return 204', function () {
- this.res.sendStatus.calledWith(204).should.equal(true)
- })
- })
- describe('deleteLabel', function () {
- beforeEach(function () {
- this.req = {
- params: {
- project_id: this.projectId,
- label_id: (this.label_id = new ObjectId()),
- },
- }
- this.HttpController.deleteLabel(this.req, this.res, this.next)
- })
- it('should delete a label for a project', function () {
- this.LabelsManager.deleteLabel
- .calledWith(this.projectId, this.label_id)
- .should.equal(true)
- })
- it('should return 204', function () {
- this.res.sendStatus.calledWith(204).should.equal(true)
- })
- })
- describe('deleteProject', function () {
- beforeEach(function () {
- this.req = {
- params: {
- project_id: this.projectId,
- },
- }
- this.WebApiManager.getHistoryId
- .withArgs(this.projectId)
- .yields(null, this.historyId)
- this.HttpController.deleteProject(this.req, this.res, this.next)
- })
- it('should delete the updates queue', function () {
- this.RedisManager.destroyDocUpdatesQueue.should.have.been.calledWith(
- this.projectId
- )
- })
- it('should clear the first op timestamp', function () {
- this.RedisManager.clearFirstOpTimestamp.should.have.been.calledWith(
- this.projectId
- )
- })
- it('should clear the cached history id', function () {
- this.RedisManager.clearCachedHistoryId.should.have.been.calledWith(
- this.projectId
- )
- })
- it('should clear the resync state', function () {
- this.SyncManager.clearResyncState.should.have.been.calledWith(
- this.projectId
- )
- })
- it('should clear any failure record', function () {
- this.ErrorRecorder.record.should.have.been.calledWith(
- this.projectId,
- 0,
- null
- )
- })
- })
- })
|