overleaf/services/project-history/test/unit/js/WebApiManager/WebApiManagerTests.js

164 lines
5 KiB
JavaScript
Raw Normal View History

import async from 'async'
import sinon from 'sinon'
import { expect } from 'chai'
import { strict as esmock } from 'esmock'
const MODULE_PATH = '../../../../app/js/WebApiManager.js'
describe('WebApiManager', function () {
beforeEach(async function () {
this.request = sinon.stub()
this.settings = {
apis: {
web: {
url: 'http://example.com',
user: 'sharelatex',
pass: 'password',
},
},
}
this.callback = sinon.stub()
this.userId = 'mock-user-id'
this.projectId = 'mock-project-id'
this.project = { features: 'mock-features' }
this.olProjectId = 12345
this.Metrics = { inc: sinon.stub() }
this.RedisManager = {
getCachedHistoryId: sinon.stub(),
setCachedHistoryId: sinon.stub().yields(),
}
this.WebApiManager = await esmock(MODULE_PATH, {
requestretry: this.request,
'@overleaf/settings': this.settings,
'@overleaf/metrics': this.Metrics,
'../../../../app/js/RedisManager.js': this.RedisManager,
})
})
describe('getHistoryId', function () {
describe('when there is no cached value and the web request is successful', function () {
beforeEach(function () {
this.RedisManager.getCachedHistoryId
.withArgs(this.projectId) // first call, no cached value returned
.onCall(0)
.yields()
this.RedisManager.getCachedHistoryId
.withArgs(this.projectId) // subsequent calls, return cached value
.yields(null, this.olProjectId)
this.RedisManager.getCachedHistoryId
.withArgs('mock-project-id-2') // no cached value for other project
.yields()
this.request.yields(
null,
{ statusCode: 200 },
{ overleaf: { history: { id: this.olProjectId } } }
)
})
it('should only request project details once per project', function (done) {
async.times(
5,
(n, cb) => {
this.WebApiManager.getHistoryId(this.projectId, cb)
},
() => {
this.request.callCount.should.equal(1)
this.WebApiManager.getHistoryId('mock-project-id-2', () => {
this.request.callCount.should.equal(2)
done()
})
}
)
})
it('should cache the history id', function (done) {
this.WebApiManager.getHistoryId(
this.projectId,
(error, olProjectId) => {
if (error) return done(error)
this.RedisManager.setCachedHistoryId
.calledWith(this.projectId, olProjectId)
.should.equal(true)
done()
}
)
})
it('should call the callback with the project', function (done) {
this.WebApiManager.getHistoryId(
this.projectId,
(error, olProjectId) => {
expect(error).to.be.null
expect(
this.request.calledWithMatch({
method: 'GET',
url: `${this.settings.apis.web.url}/project/${this.projectId}/details`,
json: true,
auth: {
user: this.settings.apis.web.user,
pass: this.settings.apis.web.pass,
sendImmediately: true,
},
})
).to.be.true
expect(olProjectId).to.equal(this.olProjectId)
done()
}
)
})
})
describe('when the web API returns an error', function () {
beforeEach(function () {
this.error = new Error('something went wrong')
this.request.yields(this.error)
this.RedisManager.getCachedHistoryId.yields()
this.WebApiManager.getHistoryId(this.projectId, this.callback)
})
it('should return an error to the callback', function () {
this.callback.calledWith(this.error).should.equal(true)
})
})
describe('when web returns a 404', function () {
beforeEach(function () {
this.request.callsArgWith(1, null, { statusCode: 404 }, '')
this.RedisManager.getCachedHistoryId.yields()
this.WebApiManager.getHistoryId(this.projectId, this.callback)
})
it('should return the callback with an error', function () {
this.callback
.calledWith(sinon.match.has('message', 'got a 404 from web api'))
.should.equal(true)
})
})
describe('when web returns a failure error code', function () {
beforeEach(function () {
this.RedisManager.getCachedHistoryId.yields()
this.request.callsArgWith(
1,
null,
{ statusCode: 500, attempts: 42 },
''
)
this.WebApiManager.getHistoryId(this.projectId, this.callback)
})
it('should return the callback with an error', function () {
this.callback
.calledWith(
sinon.match.has(
'message',
'web returned a non-success status code: 500 (attempts: 42)'
)
)
.should.equal(true)
})
})
})
})