From 34ae228d9965e9789dd79e4fdb3db7f2498fa461 Mon Sep 17 00:00:00 2001 From: Andrew Rumble Date: Thu, 24 Apr 2025 09:18:42 +0100 Subject: [PATCH] Promisify tests GitOrigin-RevId: 6f413f4c5ef8d034b4e94afacdf2d7b43c3a8830 --- .../Project/ProjectEntityUpdateHandler.js | 1 + .../AuthorizationMiddlewareTests.js | 2 +- .../BrandVariationsHandlerTests.js | 73 +- .../test/unit/src/Chat/ChatManagerTests.js | 104 +- .../src/Compile/ClsiFormatCheckerTests.js | 149 +- .../unit/src/Compile/CompileManagerTests.js | 50 +- .../unit/src/Cooldown/CooldownManagerTests.js | 191 +-- .../unit/src/Docstore/DocstoreManagerTests.js | 481 +++--- .../DocumentUpdaterHandlerTests.js | 1520 ++++++++--------- .../src/FileStore/FileStoreHandlerTests.js | 621 +++---- .../src/Institutions/InstitutionsAPITests.js | 475 +++--- .../Institutions/InstitutionsFeaturesTests.js | 133 +- .../NotificationsBuilderTests.js | 158 +- .../NotificationsHandlerTests.js | 166 +- .../src/Project/ProjectEntityHandlerTests.js | 17 +- .../ProjectEntityUpdateHandlerTests.js | 1370 ++++++++------- .../unit/src/Project/ProjectLocatorTests.js | 618 +++---- .../src/Project/ProjectRootDocManagerTests.js | 367 ++-- .../src/Publishers/PublishersGetterTests.js | 13 +- .../src/Security/LoginRateLimiterTests.js | 57 +- .../src/Subscription/RecurlyWrapperTests.js | 58 +- .../Subscription/TeamInvitesHandlerTests.js | 510 +++--- .../test/unit/src/Tags/TagsHandlerTests.js | 215 +-- .../unit/src/Uploads/FileTypeManagerTests.js | 465 ++--- .../User/ThirdPartyIdentityManagerTests.js | 26 +- 25 files changed, 3676 insertions(+), 4164 deletions(-) diff --git a/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js b/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js index a3ae3e0bfa..585c2d2698 100644 --- a/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js +++ b/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js @@ -1283,6 +1283,7 @@ const ProjectEntityUpdateHandler = { upsertFile, upsertFileWithPath, appendToDocWithPath: appendToDoc, + setMainBibliographyDoc, }, async _addDocAndSendToTpds(projectId, folderId, doc, userId) { diff --git a/services/web/test/unit/src/Authorization/AuthorizationMiddlewareTests.js b/services/web/test/unit/src/Authorization/AuthorizationMiddlewareTests.js index 33ff719e32..16514fbe63 100644 --- a/services/web/test/unit/src/Authorization/AuthorizationMiddlewareTests.js +++ b/services/web/test/unit/src/Authorization/AuthorizationMiddlewareTests.js @@ -206,7 +206,7 @@ describe('AuthorizationMiddleware', function () { expectForbidden() }) - describe('for a regular user', function (done) { + describe('for a regular user', function () { setupPermission('isRestrictedUserForProject', false) invokeMiddleware('blockRestrictedUserFromProject') expectNext() diff --git a/services/web/test/unit/src/BrandVariations/BrandVariationsHandlerTests.js b/services/web/test/unit/src/BrandVariations/BrandVariationsHandlerTests.js index 08491cc511..47d96406f9 100644 --- a/services/web/test/unit/src/BrandVariations/BrandVariationsHandlerTests.js +++ b/services/web/test/unit/src/BrandVariations/BrandVariationsHandlerTests.js @@ -59,47 +59,34 @@ describe('BrandVariationsHandler', function () { }) describe('getBrandVariationById', function () { - it('should call the callback with an error when the branding variation id is not provided', function (done) { - return this.BrandVariationsHandler.getBrandVariationById( - null, - (err, brandVariationDetails) => { - expect(err).to.be.instanceof(Error) - return done() - } - ) + it('should reject with an error when the branding variation id is not provided', async function () { + await expect( + this.BrandVariationsHandler.promises.getBrandVariationById(null) + ).to.be.rejected }) - it('should call the callback with an error when the request errors', function (done) { + it('should reject with an error when the request errors', async function () { this.V1Api.request.callsArgWith(1, new Error()) - return this.BrandVariationsHandler.getBrandVariationById( - '12', - (err, brandVariationDetails) => { - expect(err).to.be.instanceof(Error) - return done() - } - ) + await expect( + this.BrandVariationsHandler.promises.getBrandVariationById('12') + ).to.be.rejected }) - it('should call the callback with branding details when request succeeds', function (done) { + it('should return branding details when request succeeds', async function () { this.V1Api.request.callsArgWith( 1, null, { statusCode: 200 }, this.mockedBrandVariationDetails ) - return this.BrandVariationsHandler.getBrandVariationById( - '12', - (err, brandVariationDetails) => { - expect(err).to.not.exist - expect(brandVariationDetails).to.deep.equal( - this.mockedBrandVariationDetails - ) - return done() - } + const brandVariationDetails = + await this.BrandVariationsHandler.promises.getBrandVariationById('12') + expect(brandVariationDetails).to.deep.equal( + this.mockedBrandVariationDetails ) }) - it('should transform relative URLs in v1 absolute ones', function (done) { + it('should transform relative URLs in v1 absolute ones', async function () { this.mockedBrandVariationDetails.logo_url = '/journal-logo.png' this.V1Api.request.callsArgWith( 1, @@ -107,20 +94,16 @@ describe('BrandVariationsHandler', function () { { statusCode: 200 }, this.mockedBrandVariationDetails ) - return this.BrandVariationsHandler.getBrandVariationById( - '12', - (err, brandVariationDetails) => { - expect( - brandVariationDetails.logo_url.startsWith( - this.settings.apis.v1.publicUrl - ) - ).to.be.true - return done() - } - ) + const brandVariationDetails = + await this.BrandVariationsHandler.promises.getBrandVariationById('12') + expect( + brandVariationDetails.logo_url.startsWith( + this.settings.apis.v1.publicUrl + ) + ).to.be.true }) - it("should sanitize 'submit_button_html'", function (done) { + it("should sanitize 'submit_button_html'", async function () { this.mockedBrandVariationDetails.submit_button_html = '
AGU Journal' this.V1Api.request.callsArgWith( @@ -129,14 +112,10 @@ describe('BrandVariationsHandler', function () { { statusCode: 200 }, this.mockedBrandVariationDetails ) - return this.BrandVariationsHandler.getBrandVariationById( - '12', - (err, brandVariationDetails) => { - expect(brandVariationDetails.submit_button_html).to.equal( - '
AGU Journalhello' - ) - return done() - } + const brandVariationDetails = + await this.BrandVariationsHandler.promises.getBrandVariationById('12') + expect(brandVariationDetails.submit_button_html).to.equal( + '
AGU Journalhello' ) }) }) diff --git a/services/web/test/unit/src/Chat/ChatManagerTests.js b/services/web/test/unit/src/Chat/ChatManagerTests.js index 76d9b79c4b..bdd3042513 100644 --- a/services/web/test/unit/src/Chat/ChatManagerTests.js +++ b/services/web/test/unit/src/Chat/ChatManagerTests.js @@ -47,8 +47,8 @@ describe('ChatManager', function () { })) }) - it('should inject a user object into messaged and resolved data', function (done) { - return this.ChatManager.injectUserInfoIntoThreads( + it('should inject a user object into messaged and resolved data', async function () { + const threads = await this.ChatManager.promises.injectUserInfoIntoThreads( { thread1: { resolved: true, @@ -72,64 +72,56 @@ describe('ChatManager', function () { }, ], }, - }, - (error, threads) => { - expect(error).to.be.null - expect(threads).to.deep.equal({ - thread1: { - resolved: true, - resolved_by_user_id: 'user_id_1', - resolved_by_user: { formatted: 'user_1' }, - messages: [ - { - user_id: 'user_id_1', - user: { formatted: 'user_1' }, - content: 'foo', - }, - { - user_id: 'user_id_2', - user: { formatted: 'user_2' }, - content: 'bar', - }, - ], - }, - thread2: { - messages: [ - { - user_id: 'user_id_1', - user: { formatted: 'user_1' }, - content: 'baz', - }, - ], - }, - }) - return done() } ) + + expect(threads).to.deep.equal({ + thread1: { + resolved: true, + resolved_by_user_id: 'user_id_1', + resolved_by_user: { formatted: 'user_1' }, + messages: [ + { + user_id: 'user_id_1', + user: { formatted: 'user_1' }, + content: 'foo', + }, + { + user_id: 'user_id_2', + user: { formatted: 'user_2' }, + content: 'bar', + }, + ], + }, + thread2: { + messages: [ + { + user_id: 'user_id_1', + user: { formatted: 'user_1' }, + content: 'baz', + }, + ], + }, + }) }) - it('should only need to look up each user once', function (done) { - return this.ChatManager.injectUserInfoIntoThreads( - [ - { - messages: [ - { - user_id: 'user_id_1', - content: 'foo', - }, - { - user_id: 'user_id_1', - content: 'bar', - }, - ], - }, - ], - (error, threads) => { - expect(error).to.be.null - this.UserInfoManager.getPersonalInfo.calledOnce.should.equal(true) - return done() - } - ) + it('should only need to look up each user once', async function () { + await this.ChatManager.promises.injectUserInfoIntoThreads([ + { + messages: [ + { + user_id: 'user_id_1', + content: 'foo', + }, + { + user_id: 'user_id_1', + content: 'bar', + }, + ], + }, + ]) + + this.UserInfoManager.getPersonalInfo.calledOnce.should.equal(true) }) }) }) diff --git a/services/web/test/unit/src/Compile/ClsiFormatCheckerTests.js b/services/web/test/unit/src/Compile/ClsiFormatCheckerTests.js index 347d9ab422..aa19732b9e 100644 --- a/services/web/test/unit/src/Compile/ClsiFormatCheckerTests.js +++ b/services/web/test/unit/src/Compile/ClsiFormatCheckerTests.js @@ -47,62 +47,53 @@ describe('ClsiFormatChecker', function () { ]) }) - it('should call _checkDocsAreUnderSizeLimit and _checkForConflictingPaths', function (done) { + it('should call _checkDocsAreUnderSizeLimit and _checkForConflictingPaths', async function () { this.ClsiFormatChecker._checkForConflictingPaths = sinon .stub() .callsArgWith(1, null) this.ClsiFormatChecker._checkDocsAreUnderSizeLimit = sinon .stub() .callsArgWith(1) - return this.ClsiFormatChecker.checkRecoursesForProblems( - this.resources, - (err, problems) => { - this.ClsiFormatChecker._checkForConflictingPaths.called.should.equal( - true - ) - this.ClsiFormatChecker._checkDocsAreUnderSizeLimit.called.should.equal( - true - ) - return done() - } + const problems = + await this.ClsiFormatChecker.promises.checkRecoursesForProblems( + this.resources + ) + this.ClsiFormatChecker._checkForConflictingPaths.called.should.equal(true) + this.ClsiFormatChecker._checkDocsAreUnderSizeLimit.called.should.equal( + true ) }) - it('should remove undefined errors', function (done) { + it('should remove undefined errors', async function () { this.ClsiFormatChecker._checkForConflictingPaths = sinon .stub() .callsArgWith(1, null, []) this.ClsiFormatChecker._checkDocsAreUnderSizeLimit = sinon .stub() .callsArgWith(1, null, {}) - return this.ClsiFormatChecker.checkRecoursesForProblems( - this.resources, - (err, problems) => { - expect(problems).to.not.exist - expect(problems).to.not.exist - return done() - } - ) + const problems = + await this.ClsiFormatChecker.promises.checkRecoursesForProblems( + this.resources + ) + expect(problems).to.not.exist }) - it('should keep populated arrays', function (done) { + it('should keep populated arrays', async function () { this.ClsiFormatChecker._checkForConflictingPaths = sinon .stub() .callsArgWith(1, null, [{ path: 'somewhere/main.tex' }]) this.ClsiFormatChecker._checkDocsAreUnderSizeLimit = sinon .stub() .callsArgWith(1, null, {}) - return this.ClsiFormatChecker.checkRecoursesForProblems( - this.resources, - (err, problems) => { - problems.conflictedPaths[0].path.should.equal('somewhere/main.tex') - expect(problems.sizeCheck).to.not.exist - return done() - } - ) + const problems = + await this.ClsiFormatChecker.promises.checkRecoursesForProblems( + this.resources + ) + problems.conflictedPaths[0].path.should.equal('somewhere/main.tex') + expect(problems.sizeCheck).to.not.exist }) - it('should keep populated object', function (done) { + it('should keep populated object', async function () { this.ClsiFormatChecker._checkForConflictingPaths = sinon .stub() .callsArgWith(1, null, []) @@ -112,15 +103,13 @@ describe('ClsiFormatChecker', function () { resources: [{ 'a.tex': 'a.tex' }, { 'b.tex': 'b.tex' }], totalSize: 1000000, }) - return this.ClsiFormatChecker.checkRecoursesForProblems( - this.resources, - (err, problems) => { - problems.sizeCheck.resources.length.should.equal(2) - problems.sizeCheck.totalSize.should.equal(1000000) - expect(problems.conflictedPaths).to.not.exist - return done() - } - ) + const problems = + await this.ClsiFormatChecker.promises.checkRecoursesForProblems( + this.resources + ) + problems.sizeCheck.resources.length.should.equal(2) + problems.sizeCheck.totalSize.should.equal(1000000) + expect(problems.conflictedPaths).to.not.exist }) describe('_checkForConflictingPaths', function () { @@ -136,56 +125,50 @@ describe('ClsiFormatChecker', function () { }) }) - it('should flag up when a nested file has folder with same subpath as file elsewhere', function (done) { + it('should flag up when a nested file has folder with same subpath as file elsewhere', async function () { this.resources.push({ path: 'stuff/image', url: 'http://somwhere.com', }) - return this.ClsiFormatChecker._checkForConflictingPaths( - this.resources, - (err, conflictPathErrors) => { - conflictPathErrors.length.should.equal(1) - conflictPathErrors[0].path.should.equal('stuff/image') - return done() - } - ) + const conflictPathErrors = + await this.ClsiFormatChecker.promises._checkForConflictingPaths( + this.resources + ) + conflictPathErrors.length.should.equal(1) + conflictPathErrors[0].path.should.equal('stuff/image') }) - it('should flag up when a root level file has folder with same subpath as file elsewhere', function (done) { + it('should flag up when a root level file has folder with same subpath as file elsewhere', async function () { this.resources.push({ path: 'stuff', content: 'other stuff', }) - return this.ClsiFormatChecker._checkForConflictingPaths( - this.resources, - (err, conflictPathErrors) => { - conflictPathErrors.length.should.equal(1) - conflictPathErrors[0].path.should.equal('stuff') - return done() - } - ) + const conflictPathErrors = + await this.ClsiFormatChecker.promises._checkForConflictingPaths( + this.resources + ) + conflictPathErrors.length.should.equal(1) + conflictPathErrors[0].path.should.equal('stuff') }) - it('should not flag up when the file is a substring of a path', function (done) { + it('should not flag up when the file is a substring of a path', async function () { this.resources.push({ path: 'stuf', content: 'other stuff', }) - return this.ClsiFormatChecker._checkForConflictingPaths( - this.resources, - (err, conflictPathErrors) => { - conflictPathErrors.length.should.equal(0) - return done() - } - ) + const conflictPathErrors = + await this.ClsiFormatChecker.promises._checkForConflictingPaths( + this.resources + ) + conflictPathErrors.length.should.equal(0) }) }) describe('_checkDocsAreUnderSizeLimit', function () { - it('should error when there is more than 5mb of data', function (done) { + it('should error when there is more than 5mb of data', async function () { this.resources.push({ path: 'massive.tex', content: 'hello world'.repeat(833333), // over 5mb limit @@ -198,19 +181,17 @@ describe('ClsiFormatChecker', function () { }) } - return this.ClsiFormatChecker._checkDocsAreUnderSizeLimit( - this.resources, - (err, sizeError) => { - sizeError.totalSize.should.equal(16 + 833333 * 11) // 16 is for earlier resources - sizeError.resources.length.should.equal(10) - sizeError.resources[0].path.should.equal('massive.tex') - sizeError.resources[0].size.should.equal(833333 * 11) - return done() - } - ) + const sizeError = + await this.ClsiFormatChecker.promises._checkDocsAreUnderSizeLimit( + this.resources + ) + sizeError.totalSize.should.equal(16 + 833333 * 11) // 16 is for earlier resources + sizeError.resources.length.should.equal(10) + sizeError.resources[0].path.should.equal('massive.tex') + sizeError.resources[0].size.should.equal(833333 * 11) }) - it('should return nothing when project is correct size', function (done) { + it('should return nothing when project is correct size', async function () { this.resources.push({ path: 'massive.tex', content: 'x'.repeat(2 * 1000 * 1000), @@ -223,13 +204,11 @@ describe('ClsiFormatChecker', function () { }) } - return this.ClsiFormatChecker._checkDocsAreUnderSizeLimit( - this.resources, - (err, sizeError) => { - expect(sizeError).to.not.exist - return done() - } - ) + const sizeError = + await this.ClsiFormatChecker.promises._checkDocsAreUnderSizeLimit( + this.resources + ) + expect(sizeError).to.not.exist }) }) }) diff --git a/services/web/test/unit/src/Compile/CompileManagerTests.js b/services/web/test/unit/src/Compile/CompileManagerTests.js index 41b27cbd69..908e2fe803 100644 --- a/services/web/test/unit/src/Compile/CompileManagerTests.js +++ b/services/web/test/unit/src/Compile/CompileManagerTests.js @@ -139,7 +139,7 @@ describe('CompileManager', function () { }) describe('when the project has been recently compiled', function () { - it('should return', function (done) { + it('should return', async function () { this.CompileManager._checkIfAutoCompileLimitHasBeenHit = async ( isAutoCompile, compileGroup @@ -147,32 +147,27 @@ describe('CompileManager', function () { this.CompileManager._checkIfRecentlyCompiled = sinon .stub() .resolves(true) - this.CompileManager.promises - .compile(this.project_id, this.user_id, {}) - .then(({ status }) => { - status.should.equal('too-recently-compiled') - done() - }) - .catch(error => { - // Catch any errors and fail the test - true.should.equal(false) - done(error) - }) + const { status } = await this.CompileManager.promises.compile( + this.project_id, + this.user_id, + {} + ) + status.should.equal('too-recently-compiled') }) }) describe('should check the rate limit', function () { - it('should return', function (done) { + it('should return', async function () { this.CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon .stub() .resolves(false) - this.CompileManager.promises - .compile(this.project_id, this.user_id, {}) - .then(({ status }) => { - expect(status).to.equal('autocompile-backoff') - done() - }) - .catch(err => done(err)) + const { status } = await this.CompileManager.promises.compile( + this.project_id, + this.user_id, + {} + ) + + expect(status).to.equal('autocompile-backoff') }) }) }) @@ -250,15 +245,12 @@ describe('CompileManager', function () { beforeEach(function () { this.features.compileGroup = 'priority' }) - it('should return the default class', function (done) { - this.CompileManager.getProjectCompileLimits( - this.project_id, - (err, { compileBackendClass }) => { - if (err) return done(err) - expect(compileBackendClass).to.equal('c2d') - done() - } - ) + it('should return the default class', async function () { + const { compileBackendClass } = + await this.CompileManager.promises.getProjectCompileLimits( + this.project_id + ) + expect(compileBackendClass).to.equal('c2d') }) }) }) diff --git a/services/web/test/unit/src/Cooldown/CooldownManagerTests.js b/services/web/test/unit/src/Cooldown/CooldownManagerTests.js index ba4245ee55..7efefe1b04 100644 --- a/services/web/test/unit/src/Cooldown/CooldownManagerTests.js +++ b/services/web/test/unit/src/Cooldown/CooldownManagerTests.js @@ -1,15 +1,3 @@ -/* eslint-disable - n/handle-callback-err, - max-len, - no-return-assign, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const SandboxedModule = require('sandboxed-module') const sinon = require('sinon') const { expect } = require('chai') @@ -23,158 +11,143 @@ describe('CooldownManager', function () { this.projectId = 'abcdefg' this.rclient = { set: sinon.stub(), get: sinon.stub() } this.RedisWrapper = { client: () => this.rclient } - return (this.CooldownManager = SandboxedModule.require(modulePath, { + this.CooldownManager = SandboxedModule.require(modulePath, { requires: { '../../infrastructure/RedisWrapper': this.RedisWrapper, }, - })) + }) }) describe('_buildKey', function () { it('should build a properly formatted redis key', function () { - return expect(this.CooldownManager._buildKey('ABC')).to.equal( - 'Cooldown:{ABC}' - ) + expect(this.CooldownManager._buildKey('ABC')).to.equal('Cooldown:{ABC}') }) }) describe('isProjectOnCooldown', function () { - beforeEach(function () { - return (this.call = cb => { - return this.CooldownManager.isProjectOnCooldown(this.projectId, cb) - }) - }) - describe('when project is on cooldown', function () { beforeEach(function () { - return (this.rclient.get = sinon.stub().callsArgWith(1, null, '1')) + this.rclient.get = sinon.stub().callsArgWith(1, null, '1') }) - it('should fetch key from redis', function (done) { - return this.call((err, result) => { - this.rclient.get.callCount.should.equal(1) - this.rclient.get.calledWith('Cooldown:{abcdefg}').should.equal(true) - return done() - }) + it('should fetch key from redis', async function () { + await this.CooldownManager.promises.isProjectOnCooldown(this.projectId) + this.rclient.get.callCount.should.equal(1) + this.rclient.get.calledWith('Cooldown:{abcdefg}').should.equal(true) }) - it('should not produce an error', function (done) { - return this.call((err, result) => { - expect(err).to.equal(null) - return done() - }) - }) - - it('should produce a true result', function (done) { - return this.call((err, result) => { - expect(result).to.equal(true) - return done() - }) + it('should produce a true result', async function () { + const result = await this.CooldownManager.promises.isProjectOnCooldown( + this.projectId + ) + expect(result).to.equal(true) }) }) describe('when project is not on cooldown', function () { beforeEach(function () { - return (this.rclient.get = sinon.stub().callsArgWith(1, null, null)) + this.rclient.get = sinon.stub().callsArgWith(1, null, null) }) - it('should fetch key from redis', function (done) { - return this.call((err, result) => { - this.rclient.get.callCount.should.equal(1) - this.rclient.get.calledWith('Cooldown:{abcdefg}').should.equal(true) - return done() - }) + it('should fetch key from redis', async function () { + await this.CooldownManager.promises.isProjectOnCooldown(this.projectId) + this.rclient.get.callCount.should.equal(1) + this.rclient.get.calledWith('Cooldown:{abcdefg}').should.equal(true) }) - it('should not produce an error', function (done) { - return this.call((err, result) => { - expect(err).to.equal(null) - return done() - }) - }) - - it('should produce a false result', function (done) { - return this.call((err, result) => { - expect(result).to.equal(false) - return done() - }) + it('should produce a false result', async function () { + const result = await this.CooldownManager.promises.isProjectOnCooldown( + this.projectId + ) + expect(result).to.equal(false) }) }) describe('when rclient.get produces an error', function () { beforeEach(function () { - return (this.rclient.get = sinon - .stub() - .callsArgWith(1, new Error('woops'))) + this.rclient.get = sinon.stub().callsArgWith(1, new Error('woops')) }) - it('should fetch key from redis', function (done) { - return this.call((err, result) => { - this.rclient.get.callCount.should.equal(1) - this.rclient.get.calledWith('Cooldown:{abcdefg}').should.equal(true) - return done() - }) + it('should fetch key from redis', async function () { + try { + await this.CooldownManager.promises.isProjectOnCooldown( + this.projectId + ) + } catch { + // ignore errors - expected + } + this.rclient.get.callCount.should.equal(1) + this.rclient.get.calledWith('Cooldown:{abcdefg}').should.equal(true) }) - it('should produce an error', function (done) { - return this.call((err, result) => { - expect(err).to.not.equal(null) - expect(err).to.be.instanceof(Error) - return done() - }) + it('should produce an error', async function () { + let error + + try { + await this.CooldownManager.promises.isProjectOnCooldown( + this.projectId + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) }) describe('putProjectOnCooldown', function () { - beforeEach(function () { - return (this.call = cb => { - return this.CooldownManager.putProjectOnCooldown(this.projectId, cb) - }) - }) - describe('when rclient.set does not produce an error', function () { beforeEach(function () { - return (this.rclient.set = sinon.stub().callsArgWith(4, null)) + this.rclient.set = sinon.stub().callsArgWith(4, null) }) - it('should set a key in redis', function (done) { - return this.call(err => { - this.rclient.set.callCount.should.equal(1) - this.rclient.set.calledWith('Cooldown:{abcdefg}').should.equal(true) - return done() - }) + it('should set a key in redis', async function () { + await this.CooldownManager.promises.putProjectOnCooldown(this.projectId) + this.rclient.set.callCount.should.equal(1) + this.rclient.set.calledWith('Cooldown:{abcdefg}').should.equal(true) }) - it('should not produce an error', function (done) { - return this.call(err => { - expect(err).to.equal(null) - return done() - }) + it('should not produce an error', async function () { + let error + try { + await this.CooldownManager.promises.putProjectOnCooldown( + this.projectId + ) + } catch (err) { + error = err + } + expect(error).not.to.exist }) }) describe('when rclient.set produces an error', function () { beforeEach(function () { - return (this.rclient.set = sinon - .stub() - .callsArgWith(4, new Error('woops'))) + this.rclient.set = sinon.stub().callsArgWith(4, new Error('woops')) }) - it('should set a key in redis', function (done) { - return this.call(err => { - this.rclient.set.callCount.should.equal(1) - this.rclient.set.calledWith('Cooldown:{abcdefg}').should.equal(true) - return done() - }) + it('should set a key in redis', async function () { + try { + await this.CooldownManager.promises.putProjectOnCooldown( + this.projectId + ) + } catch { + // ignore errors - expected + } + this.rclient.set.callCount.should.equal(1) + this.rclient.set.calledWith('Cooldown:{abcdefg}').should.equal(true) }) - it('produce an error', function (done) { - return this.call(err => { - expect(err).to.not.equal(null) - expect(err).to.be.instanceof(Error) - return done() - }) + it('produce an error', async function () { + let error + try { + await this.CooldownManager.promises.putProjectOnCooldown( + this.projectId + ) + } catch (err) { + error = err + } + expect(error).to.be.instanceOf(Error) }) }) }) diff --git a/services/web/test/unit/src/Docstore/DocstoreManagerTests.js b/services/web/test/unit/src/Docstore/DocstoreManagerTests.js index 2995f90baf..2dbe6b6424 100644 --- a/services/web/test/unit/src/Docstore/DocstoreManagerTests.js +++ b/services/web/test/unit/src/Docstore/DocstoreManagerTests.js @@ -1,6 +1,7 @@ const sinon = require('sinon') const modulePath = '../../../../app/src/Features/Docstore/DocstoreManager' const SandboxedModule = require('sandboxed-module') +const { expect } = require('chai') const Errors = require('../../../../app/src/Features/Errors/Errors') const tk = require('timekeeper') @@ -26,7 +27,6 @@ describe('DocstoreManager', function () { this.project_id = 'project-id-123' this.doc_id = 'doc-id-123' - this.callback = sinon.stub() }) describe('deleteDoc', function () { @@ -39,16 +39,15 @@ describe('DocstoreManager', function () { tk.reset() }) - beforeEach(function () { + beforeEach(async function () { this.request.patch = sinon .stub() .callsArgWith(1, null, { statusCode: 204 }, '') - this.DocstoreManager.deleteDoc( + await this.DocstoreManager.promises.deleteDoc( this.project_id, this.doc_id, 'wombat.tex', - new Date(), - this.callback + new Date() ) }) @@ -61,10 +60,6 @@ describe('DocstoreManager', function () { }) .should.equal(true) }) - - it('should call the callback without an error', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('with a failed response code', function () { @@ -72,28 +67,27 @@ describe('DocstoreManager', function () { this.request.patch = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }, '') - this.DocstoreManager.deleteDoc( - this.project_id, - this.doc_id, - 'main.tex', - new Date(), - this.callback - ) }) - it('should call the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'docstore api responded with non-success code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.deleteDoc( + this.project_id, + this.doc_id, + 'main.tex', + new Date() ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'docstore api responded with non-success code: 500' + ) }) }) @@ -102,28 +96,26 @@ describe('DocstoreManager', function () { this.request.patch = sinon .stub() .callsArgWith(1, null, { statusCode: 404 }, '') - this.DocstoreManager.deleteDoc( - this.project_id, - this.doc_id, - 'main.tex', - new Date(), - this.callback - ) }) - it('should call the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Errors.NotFoundError) - .and( - sinon.match.has( - 'message', - 'tried to delete doc not in docstore' - ) - ) + it('should reject with an error', async function () { + let error + try { + await this.DocstoreManager.promises.deleteDoc( + this.project_id, + this.doc_id, + 'main.tex', + new Date() ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'tried to delete doc not in docstore' + ) }) }) }) @@ -137,8 +129,8 @@ describe('DocstoreManager', function () { this.modified = true }) - describe('with a successful response code', function () { - beforeEach(function () { + describe('with a successful response code', async function () { + beforeEach(async function () { this.request.post = sinon .stub() .callsArgWith( @@ -147,13 +139,12 @@ describe('DocstoreManager', function () { { statusCode: 204 }, { modified: this.modified, rev: this.rev } ) - this.DocstoreManager.updateDoc( + this.updateDocResponse = await this.DocstoreManager.promises.updateDoc( this.project_id, this.doc_id, this.lines, this.version, - this.ranges, - this.callback + this.ranges ) }) @@ -171,10 +162,12 @@ describe('DocstoreManager', function () { .should.equal(true) }) - it('should call the callback with the modified status and revision', function () { - this.callback - .calledWith(null, this.modified, this.rev) - .should.equal(true) + it('should return the modified status and revision', function () { + expect(this.updateDocResponse).to.haveOwnProperty( + 'modified', + this.modified + ) + expect(this.updateDocResponse).to.haveOwnProperty('rev', this.rev) }) }) @@ -183,29 +176,28 @@ describe('DocstoreManager', function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }, '') - this.DocstoreManager.updateDoc( - this.project_id, - this.doc_id, - this.lines, - this.version, - this.ranges, - this.callback - ) }) - it('should call the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'docstore api responded with non-success code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.updateDoc( + this.project_id, + this.doc_id, + this.lines, + this.version, + this.ranges ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'docstore api responded with non-success code: 500' + ) }) }) }) @@ -221,11 +213,14 @@ describe('DocstoreManager', function () { }) describe('with a successful response code', function () { - beforeEach(function () { + beforeEach(async function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 204 }, this.doc) - this.DocstoreManager.getDoc(this.project_id, this.doc_id, this.callback) + this.getDocResponse = await this.DocstoreManager.promises.getDoc( + this.project_id, + this.doc_id + ) }) it('should get the doc from the docstore api', function () { @@ -236,10 +231,13 @@ describe('DocstoreManager', function () { }) }) - it('should call the callback with the lines, version and rev', function () { - this.callback - .calledWith(null, this.lines, this.rev, this.version, this.ranges) - .should.equal(true) + it('should resolve with the lines, version and rev', function () { + expect(this.getDocResponse).to.eql({ + lines: this.lines, + rev: this.rev, + version: this.version, + ranges: this.ranges, + }) }) }) @@ -248,35 +246,37 @@ describe('DocstoreManager', function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }, '') - this.DocstoreManager.getDoc(this.project_id, this.doc_id, this.callback) }) - it('should call the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'docstore api responded with non-success code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.getDoc( + this.project_id, + this.doc_id ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'docstore api responded with non-success code: 500' + ) }) }) describe('with include_deleted=true', function () { - beforeEach(function () { + beforeEach(async function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 204 }, this.doc) - this.DocstoreManager.getDoc( + this.getDocResponse = await this.DocstoreManager.promises.getDoc( this.project_id, this.doc_id, - { include_deleted: true }, - this.callback + { include_deleted: true } ) }) @@ -289,23 +289,27 @@ describe('DocstoreManager', function () { }) }) - it('should call the callback with the lines, version and rev', function () { - this.callback - .calledWith(null, this.lines, this.rev, this.version, this.ranges) - .should.equal(true) + it('should resolve with the lines, version and rev', function () { + expect(this.getDocResponse).to.eql({ + lines: this.lines, + rev: this.rev, + version: this.version, + ranges: this.ranges, + }) }) }) describe('with peek=true', function () { - beforeEach(function () { + beforeEach(async function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 204 }, this.doc) - this.DocstoreManager.getDoc( + await this.DocstoreManager.promises.getDoc( this.project_id, this.doc_id, - { peek: true }, - this.callback + { + peek: true, + } ) }) @@ -323,24 +327,30 @@ describe('DocstoreManager', function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 404 }, '') - this.DocstoreManager.getDoc(this.project_id, this.doc_id, this.callback) }) - it('should call the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Errors.NotFoundError) - .and(sinon.match.has('message', 'doc not found in docstore')) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.getDoc( + this.project_id, + this.doc_id ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.NotFoundError) + expect(error).to.have.property('message', 'doc not found in docstore') }) }) }) describe('getAllDocs', function () { describe('with a successful response code', function () { - beforeEach(function () { + let getAllDocsResult + beforeEach(async function () { this.request.get = sinon .stub() .callsArgWith( @@ -349,7 +359,9 @@ describe('DocstoreManager', function () { { statusCode: 204 }, (this.docs = [{ _id: 'mock-doc-id' }]) ) - this.DocstoreManager.getAllDocs(this.project_id, this.callback) + getAllDocsResult = await this.DocstoreManager.promises.getAllDocs( + this.project_id + ) }) it('should get all the project docs in the docstore api', function () { @@ -362,8 +374,8 @@ describe('DocstoreManager', function () { .should.equal(true) }) - it('should call the callback with the docs', function () { - this.callback.calledWith(null, this.docs).should.equal(true) + it('should return the docs', function () { + expect(getAllDocsResult).to.eql(this.docs) }) }) @@ -372,35 +384,36 @@ describe('DocstoreManager', function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }, '') - this.DocstoreManager.getAllDocs(this.project_id, this.callback) }) - it('should call the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'docstore api responded with non-success code: 500' - ) - ) - ) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.getAllDocs(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'docstore api responded with non-success code: 500' + ) }) }) }) describe('getAllDeletedDocs', function () { describe('with a successful response code', function () { - beforeEach(function (done) { - this.callback.callsFake(done) + let getAllDeletedDocsResponse + beforeEach(async function () { this.docs = [{ _id: 'mock-doc-id', name: 'foo.tex' }] this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 200 }, this.docs) - this.DocstoreManager.getAllDeletedDocs(this.project_id, this.callback) + getAllDeletedDocsResponse = + await this.DocstoreManager.promises.getAllDeletedDocs(this.project_id) }) it('should get all the project docs in the docstore api', function () { @@ -411,48 +424,52 @@ describe('DocstoreManager', function () { }) }) - it('should call the callback with the docs', function () { - this.callback.should.have.been.calledWith(null, this.docs) + it('should resolve with the docs', function () { + expect(getAllDeletedDocsResponse).to.eql(this.docs) }) }) describe('with an error', function () { - beforeEach(function (done) { - this.callback.callsFake(() => done()) + beforeEach(async function () { this.request.get = sinon .stub() .callsArgWith(1, new Error('connect failed')) - this.DocstoreManager.getAllDocs(this.project_id, this.callback) }) - it('should call the callback with an error', function () { - this.callback.should.have.been.calledWith( - sinon.match - .instanceOf(Error) - .and(sinon.match.has('message', 'connect failed')) - ) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.getAllDocs(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property('message', 'connect failed') }) }) describe('with a failed response code', function () { - beforeEach(function (done) { - this.callback.callsFake(() => done()) + beforeEach(function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }) - this.DocstoreManager.getAllDocs(this.project_id, this.callback) }) - it('should call the callback with an error', function () { - this.callback.should.have.been.calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'docstore api responded with non-success code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.getAllDocs(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'docstore api responded with non-success code: 500' ) }) }) @@ -460,7 +477,8 @@ describe('DocstoreManager', function () { describe('getAllRanges', function () { describe('with a successful response code', function () { - beforeEach(function () { + let getAllRangesResult + beforeEach(async function () { this.request.get = sinon .stub() .callsArgWith( @@ -469,7 +487,9 @@ describe('DocstoreManager', function () { { statusCode: 204 }, (this.docs = [{ _id: 'mock-doc-id', ranges: 'mock-ranges' }]) ) - this.DocstoreManager.getAllRanges(this.project_id, this.callback) + getAllRangesResult = await this.DocstoreManager.promises.getAllRanges( + this.project_id + ) }) it('should get all the project doc ranges in the docstore api', function () { @@ -482,8 +502,8 @@ describe('DocstoreManager', function () { .should.equal(true) }) - it('should call the callback with the docs', function () { - this.callback.calledWith(null, this.docs).should.equal(true) + it('should return the docs', async function () { + expect(getAllRangesResult).to.eql(this.docs) }) }) @@ -492,22 +512,22 @@ describe('DocstoreManager', function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }, '') - this.DocstoreManager.getAllRanges(this.project_id, this.callback) }) - it('should call the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'docstore api responded with non-success code: 500' - ) - ) - ) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.getAllRanges(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'docstore api responded with non-success code: 500' + ) }) }) }) @@ -518,11 +538,12 @@ describe('DocstoreManager', function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 204 }) - this.DocstoreManager.archiveProject(this.project_id, this.callback) }) - it('should call the callback', function () { - this.callback.called.should.equal(true) + it('should resolve', async function () { + await expect( + this.DocstoreManager.promises.archiveProject(this.project_id) + ).to.eventually.be.fulfilled }) }) @@ -531,22 +552,22 @@ describe('DocstoreManager', function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }) - this.DocstoreManager.archiveProject(this.project_id, this.callback) }) - it('should call the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'docstore api responded with non-success code: 500' - ) - ) - ) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.archiveProject(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'docstore api responded with non-success code: 500' + ) }) }) }) @@ -557,11 +578,12 @@ describe('DocstoreManager', function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 204 }) - this.DocstoreManager.unarchiveProject(this.project_id, this.callback) }) - it('should call the callback', function () { - this.callback.called.should.equal(true) + it('should resolve', async function () { + await expect( + this.DocstoreManager.promises.unarchiveProject(this.project_id) + ).to.eventually.be.fulfilled }) }) @@ -570,22 +592,22 @@ describe('DocstoreManager', function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }) - this.DocstoreManager.unarchiveProject(this.project_id, this.callback) }) - it('should call the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'docstore api responded with non-success code: 500' - ) - ) - ) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.unarchiveProject(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'docstore api responded with non-success code: 500' + ) }) }) }) @@ -596,11 +618,12 @@ describe('DocstoreManager', function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 204 }) - this.DocstoreManager.destroyProject(this.project_id, this.callback) }) - it('should call the callback', function () { - this.callback.called.should.equal(true) + it('should resolve', async function () { + await expect( + this.DocstoreManager.promises.destroyProject(this.project_id) + ).to.eventually.be.fulfilled }) }) @@ -609,22 +632,22 @@ describe('DocstoreManager', function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }) - this.DocstoreManager.destroyProject(this.project_id, this.callback) }) - it('should call the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'docstore api responded with non-success code: 500' - ) - ) - ) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.DocstoreManager.promises.destroyProject(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'docstore api responded with non-success code: 500' + ) }) }) }) diff --git a/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js b/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js index 2d5a116337..fba5dc87d4 100644 --- a/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js +++ b/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js @@ -1,6 +1,7 @@ const sinon = require('sinon') const SandboxedModule = require('sandboxed-module') const path = require('path') +const { expect } = require('chai') const { ObjectId } = require('mongodb-legacy') const modulePath = path.join( __dirname, @@ -32,7 +33,6 @@ describe('DocumentUpdaterHandler', function () { } this.source = 'dropbox' - this.callback = sinon.stub() this.handler = SandboxedModule.require(modulePath, { requires: { request: { @@ -68,9 +68,9 @@ describe('DocumentUpdaterHandler', function () { describe('flushProjectToMongo', function () { describe('successfully', function () { - beforeEach(function () { + beforeEach(async function () { this.request.callsArgWith(1, null, { statusCode: 204 }, '') - this.handler.flushProjectToMongo(this.project_id, this.callback) + await this.handler.promises.flushProjectToMongo(this.project_id) }) it('should flush the document from the document updater', function () { @@ -81,10 +81,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback with no error', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -95,46 +91,50 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.flushProjectToMongo(this.project_id, this.callback) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.flushProjectToMongo(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.exist }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.flushProjectToMongo(this.project_id, this.callback) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) - ) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.flushProjectToMongo(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) describe('flushProjectToMongoAndDelete', function () { describe('successfully', function () { - beforeEach(function () { + beforeEach(async function () { this.request.callsArgWith(1, null, { statusCode: 204 }, '') - this.handler.flushProjectToMongoAndDelete( - this.project_id, - this.callback + await this.handler.promises.flushProjectToMongoAndDelete( + this.project_id ) }) @@ -146,10 +146,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback with no error', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -160,41 +156,44 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.flushProjectToMongoAndDelete( - this.project_id, - this.callback - ) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.flushProjectToMongoAndDelete( + this.project_id + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.flushProjectToMongoAndDelete( - this.project_id, - this.callback - ) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.flushProjectToMongoAndDelete( + this.project_id ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) @@ -203,14 +202,13 @@ describe('DocumentUpdaterHandler', function () { describe('successfully', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 204 }, '') - this.handler.flushDocToMongo( - this.project_id, - this.doc_id, - this.callback - ) }) - it('should flush the document from the document updater', function () { + it('should flush the document from the document updater', async function () { + await this.handler.promises.flushDocToMongo( + this.project_id, + this.doc_id + ) this.request .calledWithMatch({ url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}/flush`, @@ -218,10 +216,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback with no error', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -232,43 +226,46 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.flushDocToMongo( - this.project_id, - this.doc_id, - this.callback - ) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.flushDocToMongo( + this.project_id, + this.doc_id + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.flushDocToMongo( - this.project_id, - this.doc_id, - this.callback - ) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.flushDocToMongo( + this.project_id, + this.doc_id ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) @@ -277,10 +274,10 @@ describe('DocumentUpdaterHandler', function () { describe('successfully', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 204 }, '') - this.handler.deleteDoc(this.project_id, this.doc_id, this.callback) }) - it('should delete the document from the document updater', function () { + it('should delete the document from the document updater', async function () { + await this.handler.promises.deleteDoc(this.project_id, this.doc_id) this.request .calledWithMatch({ url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}`, @@ -288,10 +285,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback with no error', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -302,35 +295,40 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.deleteDoc(this.project_id, this.doc_id, this.callback) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.deleteDoc(this.project_id, this.doc_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.deleteDoc(this.project_id, this.doc_id, this.callback) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) - ) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.deleteDoc(this.project_id, this.doc_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) @@ -339,12 +337,11 @@ describe('DocumentUpdaterHandler', function () { this.request.callsArgWith(1, null, { statusCode: 204 }, '') }) - it('when option is true, should send a `ignore_flush_errors=true` URL query to document-updater', function () { - this.handler.deleteDoc( + it('when option is true, should send a `ignore_flush_errors=true` URL query to document-updater', async function () { + await this.handler.promises.deleteDoc( this.project_id, this.doc_id, - true, - this.callback + true ) this.request .calledWithMatch({ @@ -354,12 +351,11 @@ describe('DocumentUpdaterHandler', function () { .should.equal(true) }) - it("when option is false, shouldn't send any URL query to document-updater", function () { - this.handler.deleteDoc( + it("when option is false, shouldn't send any URL query to document-updater", async function () { + await this.handler.promises.deleteDoc( this.project_id, this.doc_id, - false, - this.callback + false ) this.request .calledWithMatch({ @@ -375,17 +371,16 @@ describe('DocumentUpdaterHandler', function () { describe('successfully', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 204 }, '') - this.handler.setDocument( + }) + + it('should set the document in the document updater', async function () { + await this.handler.promises.setDocument( this.project_id, this.doc_id, this.user_id, this.lines, - this.source, - this.callback + this.source ) - }) - - it('should set the document in the document updater', function () { this.request .calledWith({ url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}`, @@ -399,10 +394,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback with no error', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -413,49 +404,52 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.setDocument( - this.project_id, - this.doc_id, - this.user_id, - this.lines, - this.source, - this.callback - ) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.setDocument( + this.project_id, + this.doc_id, + this.user_id, + this.lines, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.setDocument( - this.project_id, - this.doc_id, - this.user_id, - this.lines, - this.source, - this.callback - ) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.setDocument( + this.project_id, + this.doc_id, + this.user_id, + this.lines, + this.source ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) @@ -468,15 +462,14 @@ describe('DocumentUpdaterHandler', function () { } this.body = this.comment this.request.callsArgWith(1, null, { statusCode: 200 }, this.body) - this.handler.getComment( - this.project_id, - this.doc_id, - this.comment.id, - this.callback - ) }) - it('should get the comment from the document updater', function () { + it('should get the comment from the document updater', async function () { + await this.handler.promises.getComment( + this.project_id, + this.doc_id, + this.comment.id + ) const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}/comment/${this.comment.id}` this.request .calledWith({ @@ -487,16 +480,13 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback with the comment', function () { - this.callback.calledWithExactly(null, this.comment).should.equal(true) - }) }) }) describe('getDocument', function () { describe('successfully', function () { - beforeEach(function () { + let getDocumentResponse + beforeEach(async function () { this.body = { lines: this.lines, version: this.version, @@ -505,11 +495,10 @@ describe('DocumentUpdaterHandler', function () { } this.fromVersion = 2 this.request.callsArgWith(1, null, { statusCode: 200 }, this.body) - this.handler.getDocument( + getDocumentResponse = await this.handler.promises.getDocument( this.project_id, this.doc_id, - this.fromVersion, - this.callback + this.fromVersion ) }) @@ -525,9 +514,12 @@ describe('DocumentUpdaterHandler', function () { }) it('should call the callback with the lines and version', function () { - this.callback - .calledWith(null, this.lines, this.version, this.ranges, this.ops) - .should.equal(true) + expect(getDocumentResponse).to.eql({ + lines: this.lines, + version: this.version, + ranges: this.ranges, + ops: this.ops, + }) }) }) @@ -539,45 +531,48 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.getDocument( - this.project_id, - this.doc_id, - this.fromVersion, - this.callback - ) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.getDocument( + this.project_id, + this.doc_id, + this.fromVersion + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.getDocument( - this.project_id, - this.doc_id, - this.fromVersion, - this.callback - ) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.getDocument( + this.project_id, + this.doc_id, + this.fromVersion ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) @@ -588,7 +583,8 @@ describe('DocumentUpdaterHandler', function () { }) describe('successfully', function () { - beforeEach(function () { + let getProjectDocsIfMatchResponse + beforeEach(async function () { this.doc0 = { _id: this.doc_id, lines: this.lines, @@ -599,11 +595,11 @@ describe('DocumentUpdaterHandler', function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 200 }, this.body) - this.handler.getProjectDocsIfMatch( - this.project_id, - this.project_state_hash, - this.callback - ) + getProjectDocsIfMatchResponse = + await this.handler.promises.getProjectDocsIfMatch( + this.project_id, + this.project_state_hash + ) }) it('should get the documents from the document updater', function () { @@ -612,7 +608,7 @@ describe('DocumentUpdaterHandler', function () { }) it('should call the callback with the documents', function () { - this.callback.calledWithExactly(null, this.docs).should.equal(true) + expect(getProjectDocsIfMatchResponse).to.eql(this.docs) }) }) @@ -621,17 +617,21 @@ describe('DocumentUpdaterHandler', function () { this.request.post = sinon .stub() .callsArgWith(1, new Error('something went wrong'), null, null) - this.handler.getProjectDocsIfMatch( - this.project_id, - this.project_state_hash, - this.callback - ) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.getProjectDocsIfMatch( + this.project_id, + this.project_state_hash + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) @@ -640,15 +640,14 @@ describe('DocumentUpdaterHandler', function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 409 }, 'Conflict') - this.handler.getProjectDocsIfMatch( - this.project_id, - this.project_state_hash, - this.callback - ) }) - it('should return the callback with no documents', function () { - this.callback.alwaysCalledWithExactly().should.equal(true) + it('should return no documents', async function () { + const response = await this.handler.promises.getProjectDocsIfMatch( + this.project_id, + this.project_state_hash + ) + expect(response).to.be.undefined }) }) }) @@ -657,10 +656,11 @@ describe('DocumentUpdaterHandler', function () { describe('successfully', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 200 }) - this.handler.clearProjectState(this.project_id, this.callback) }) - it('should clear the project state from the document updater', function () { + it('should clear the project state from the document updater', async function () { + await this.handler.promises.clearProjectState(this.project_id) + this.request .calledWithMatch({ url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/clearState`, @@ -668,10 +668,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -682,35 +678,40 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.clearProjectState(this.project_id, this.callback) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.clearProjectState(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns an error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, null) - this.handler.clearProjectState(this.project_id, this.callback) }) - it('should return the callback with no documents', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) - ) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.clearProjectState(this.project_id) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) @@ -723,15 +724,14 @@ describe('DocumentUpdaterHandler', function () { describe('successfully', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 200 }, this.body) - this.handler.acceptChanges( - this.project_id, - this.doc_id, - [this.change_id], - this.callback - ) }) - it('should accept the change in the document updater', function () { + it('should accept the change in the document updater', async function () { + await this.handler.promises.acceptChanges( + this.project_id, + this.doc_id, + [this.change_id] + ) this.request .calledWith({ url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}/change/accept`, @@ -743,10 +743,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -757,45 +753,48 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.acceptChanges( - this.project_id, - this.doc_id, - [this.change_id], - this.callback - ) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.acceptChanges( + this.project_id, + this.doc_id, + [this.change_id] + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.acceptChanges( - this.project_id, - this.doc_id, - [this.change_id], - this.callback - ) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.acceptChanges( + this.project_id, + this.doc_id, + [this.change_id] ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) @@ -808,16 +807,15 @@ describe('DocumentUpdaterHandler', function () { describe('successfully', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 200 }, this.body) - this.handler.deleteThread( + }) + + it('should delete the thread in the document updater', async function () { + await this.handler.promises.deleteThread( this.project_id, this.doc_id, this.thread_id, - this.user_id, - this.callback + this.user_id ) - }) - - it('should delete the thread in the document updater', function () { this.request .calledWithMatch({ url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}/comment/${this.thread_id}`, @@ -825,10 +823,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -839,47 +833,50 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.deleteThread( - this.project_id, - this.doc_id, - this.thread_id, - this.user_id, - this.callback - ) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.deleteThread( + this.project_id, + this.doc_id, + this.thread_id, + this.user_id + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.deleteThread( - this.project_id, - this.doc_id, - this.thread_id, - this.user_id, - this.callback - ) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.deleteThread( + this.project_id, + this.doc_id, + this.thread_id, + this.user_id ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) @@ -892,16 +889,15 @@ describe('DocumentUpdaterHandler', function () { describe('successfully', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 200 }, this.body) - this.handler.resolveThread( + }) + + it('should resolve the thread in the document updater', async function () { + await this.handler.promises.resolveThread( this.project_id, this.doc_id, this.thread_id, - this.user_id, - this.callback + this.user_id ) - }) - - it('should resolve the thread in the document updater', function () { this.request .calledWithMatch({ url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}/comment/${this.thread_id}/resolve`, @@ -909,10 +905,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -923,47 +915,50 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.resolveThread( - this.project_id, - this.doc_id, - this.thread_id, - this.user_id, - this.callback - ) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.resolveThread( + this.project_id, + this.doc_id, + this.thread_id, + this.user_id + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.resolveThread( - this.project_id, - this.doc_id, - this.thread_id, - this.user_id, - this.callback - ) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.resolveThread( + this.project_id, + this.doc_id, + this.thread_id, + this.user_id ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) @@ -976,16 +971,15 @@ describe('DocumentUpdaterHandler', function () { describe('successfully', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 200 }, this.body) - this.handler.reopenThread( + }) + + it('should reopen the thread in the document updater', async function () { + await this.handler.promises.reopenThread( this.project_id, this.doc_id, this.thread_id, - this.user_id, - this.callback + this.user_id ) - }) - - it('should reopen the thread in the document updater', function () { this.request .calledWithMatch({ url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}/comment/${this.thread_id}/reopen`, @@ -993,10 +987,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -1007,47 +997,50 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.reopenThread( - this.project_id, - this.doc_id, - this.thread_id, - this.user_id, - this.callback - ) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.reopenThread( + this.project_id, + this.doc_id, + this.thread_id, + this.user_id + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.reopenThread( - this.project_id, - this.doc_id, - this.thread_id, - this.user_id, - this.callback - ) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.reopenThread( + this.project_id, + this.doc_id, + this.thread_id, + this.user_id ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) @@ -1061,23 +1054,18 @@ describe('DocumentUpdaterHandler', function () { describe('with project history disabled', function () { beforeEach(function () { this.settings.apis.project_history.sendProjectStructureOps = false - this.handler.updateProjectStructure( + }) + + it('does not make a web request', async function () { + await this.handler.promises.updateProjectStructure( this.project_id, this.projectHistoryId, this.user_id, {}, - this.source, - this.callback + this.source ) - }) - - it('does not make a web request', function () { this.request.called.should.equal(false) }) - - it('calls the callback', function () { - this.callback.called.should.equal(true) - }) }) describe('with project history enabled', function () { @@ -1088,7 +1076,7 @@ describe('DocumentUpdaterHandler', function () { }) describe('when an entity has changed name', function () { - it('should send the structure update to the document updater', function (done) { + it('should send the structure update to the document updater', async function () { this.docIdA = new ObjectId() this.docIdB = new ObjectId() this.changes = { @@ -1119,35 +1107,33 @@ describe('DocumentUpdaterHandler', function () { }, ] - this.handler.updateProjectStructure( + await this.handler.promises.updateProjectStructure( this.project_id, this.projectHistoryId, this.user_id, this.changes, - this.source, - () => { - this.request - .calledWith({ - url: this.url, - method: 'POST', - json: { - updates, - userId: this.user_id, - version: this.version, - projectHistoryId: this.projectHistoryId, - source: this.source, - }, - timeout: 30 * 1000, - }) - .should.equal(true) - done() - } + this.source ) + + this.request + .calledWith({ + url: this.url, + method: 'POST', + json: { + updates, + userId: this.user_id, + version: this.version, + projectHistoryId: this.projectHistoryId, + source: this.source, + }, + timeout: 30 * 1000, + }) + .should.equal(true) }) }) describe('when a doc has been added', function () { - it('should send the structure update to the document updater', function (done) { + it('should send the structure update to the document updater', async function () { this.docId = new ObjectId() this.changes = { newDocs: [ @@ -1171,33 +1157,31 @@ describe('DocumentUpdaterHandler', function () { }, ] - this.handler.updateProjectStructure( + await this.handler.promises.updateProjectStructure( this.project_id, this.projectHistoryId, this.user_id, this.changes, - this.source, - () => { - this.request.should.have.been.calledWith({ - url: this.url, - method: 'POST', - json: { - updates, - userId: this.user_id, - version: this.version, - projectHistoryId: this.projectHistoryId, - source: this.source, - }, - timeout: 30 * 1000, - }) - done() - } + this.source ) + + this.request.should.have.been.calledWith({ + url: this.url, + method: 'POST', + json: { + updates, + userId: this.user_id, + version: this.version, + projectHistoryId: this.projectHistoryId, + source: this.source, + }, + timeout: 30 * 1000, + }) }) }) describe('when a file has been added', function () { - it('should send the structure update to the document updater', function (done) { + it('should send the structure update to the document updater', async function () { this.fileId = new ObjectId() this.changes = { newFiles: [ @@ -1225,33 +1209,31 @@ describe('DocumentUpdaterHandler', function () { }, ] - this.handler.updateProjectStructure( + await this.handler.promises.updateProjectStructure( this.project_id, this.projectHistoryId, this.user_id, this.changes, - this.source, - () => { - this.request.should.have.been.calledWith({ - url: this.url, - method: 'POST', - json: { - updates, - userId: this.user_id, - version: this.version, - projectHistoryId: this.projectHistoryId, - source: this.source, - }, - timeout: 30 * 1000, - }) - done() - } + this.source ) + + this.request.should.have.been.calledWith({ + url: this.url, + method: 'POST', + json: { + updates, + userId: this.user_id, + version: this.version, + projectHistoryId: this.projectHistoryId, + source: this.source, + }, + timeout: 30 * 1000, + }) }) }) describe('when an entity has been deleted', function () { - it('should end the structure update to the document updater', function (done) { + it('should end the structure update to the document updater', async function () { this.docId = new ObjectId() this.changes = { oldDocs: [ @@ -1269,33 +1251,31 @@ describe('DocumentUpdaterHandler', function () { }, ] - this.handler.updateProjectStructure( + await this.handler.promises.updateProjectStructure( this.project_id, this.projectHistoryId, this.user_id, this.changes, - this.source, - () => { - this.request.should.have.been.calledWith({ - url: this.url, - method: 'POST', - json: { - updates, - userId: this.user_id, - version: this.version, - projectHistoryId: this.projectHistoryId, - source: this.source, - }, - timeout: 30 * 1000, - }) - done() - } + this.source ) + + this.request.should.have.been.calledWith({ + url: this.url, + method: 'POST', + json: { + updates, + userId: this.user_id, + version: this.version, + projectHistoryId: this.projectHistoryId, + source: this.source, + }, + timeout: 30 * 1000, + }) }) }) describe('when a file is converted to a doc', function () { - it('should send the delete first', function (done) { + it('should send the delete first', async function () { this.docId = new ObjectId() this.fileId = new ObjectId() this.changes = { @@ -1337,33 +1317,31 @@ describe('DocumentUpdaterHandler', function () { }, ] - this.handler.updateProjectStructure( + await this.handler.promises.updateProjectStructure( this.project_id, this.projectHistoryId, this.user_id, this.changes, - this.source, - () => { - this.request.should.have.been.calledWith({ - url: this.url, - method: 'POST', - json: { - updates, - userId: this.user_id, - version: this.version, - projectHistoryId: this.projectHistoryId, - source: this.source, - }, - timeout: 30 * 1000, - }) - done() - } + this.source ) + + this.request.should.have.been.calledWith({ + url: this.url, + method: 'POST', + json: { + updates, + userId: this.user_id, + version: this.version, + projectHistoryId: this.projectHistoryId, + source: this.source, + }, + timeout: 30 * 1000, + }) }) }) describe('when the project version is missing', function () { - it('should call the callback with an error', function () { + it('should call the callback with an error', async function () { this.docId = new ObjectId() this.changes = { oldDocs: [ @@ -1371,20 +1349,23 @@ describe('DocumentUpdaterHandler', function () { ], } - this.handler.updateProjectStructure( - this.project_id, - this.projectHistoryId, - this.user_id, - this.changes, - this.source, - this.callback - ) + let error - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) - const firstCallArgs = this.callback.args[0] - firstCallArgs[0].message.should.equal( + try { + await this.handler.promises.updateProjectStructure( + this.project_id, + this.projectHistoryId, + this.user_id, + this.changes, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', 'did not receive project version in changes' ) }) @@ -1424,7 +1405,7 @@ describe('DocumentUpdaterHandler', function () { } }) - it('should forward ranges', function (done) { + it('should forward ranges', async function () { const updates = [ { type: 'add-doc', @@ -1440,31 +1421,29 @@ describe('DocumentUpdaterHandler', function () { }, ] - this.handler.updateProjectStructure( + await this.handler.promises.updateProjectStructure( this.project_id, this.projectHistoryId, this.user_id, this.changes, - this.source, - () => { - this.request.should.have.been.calledWith({ - url: this.url, - method: 'POST', - json: { - updates, - userId: this.user_id, - version: this.version, - projectHistoryId: this.projectHistoryId, - source: this.source, - }, - timeout: 30 * 1000, - }) - done() - } + this.source ) + + this.request.should.have.been.calledWith({ + url: this.url, + method: 'POST', + json: { + updates, + userId: this.user_id, + version: this.version, + projectHistoryId: this.projectHistoryId, + source: this.source, + }, + timeout: 30 * 1000, + }) }) - it('should include flag when history ranges support is enabled', function (done) { + it('should include flag when history ranges support is enabled', async function () { this.ProjectGetter.getProjectWithoutLock .withArgs(this.project_id) .yields(null, { @@ -1487,28 +1466,26 @@ describe('DocumentUpdaterHandler', function () { }, ] - this.handler.updateProjectStructure( + await this.handler.promises.updateProjectStructure( this.project_id, this.projectHistoryId, this.user_id, this.changes, - this.source, - () => { - this.request.should.have.been.calledWith({ - url: this.url, - method: 'POST', - json: { - updates, - userId: this.user_id, - version: this.version, - projectHistoryId: this.projectHistoryId, - source: this.source, - }, - timeout: 30 * 1000, - }) - done() - } + this.source ) + + this.request.should.have.been.calledWith({ + url: this.url, + method: 'POST', + json: { + updates, + userId: this.user_id, + version: this.version, + projectHistoryId: this.projectHistoryId, + source: this.source, + }, + timeout: 30 * 1000, + }) }) }) @@ -1516,7 +1493,7 @@ describe('DocumentUpdaterHandler', function () { beforeEach(function () { this.settings.disableFilestore = true }) - it('should add files without URL and with createdBlob', function (done) { + it('should add files without URL and with createdBlob', async function () { this.fileId = new ObjectId() this.changes = { newFiles: [ @@ -1544,30 +1521,28 @@ describe('DocumentUpdaterHandler', function () { }, ] - this.handler.updateProjectStructure( + await this.handler.promises.updateProjectStructure( this.project_id, this.projectHistoryId, this.user_id, this.changes, - this.source, - () => { - this.request.should.have.been.calledWith({ - url: this.url, - method: 'POST', - json: { - updates, - userId: this.user_id, - version: this.version, - projectHistoryId: this.projectHistoryId, - source: this.source, - }, - timeout: 30 * 1000, - }) - done() - } + this.source ) + + this.request.should.have.been.calledWith({ + url: this.url, + method: 'POST', + json: { + updates, + userId: this.user_id, + version: this.version, + projectHistoryId: this.projectHistoryId, + source: this.source, + }, + timeout: 30 * 1000, + }) }) - it('should flag files without hash', function (done) { + it('should flag files without hash', async function () { this.fileId = new ObjectId() this.changes = { newFiles: [ @@ -1580,25 +1555,26 @@ describe('DocumentUpdaterHandler', function () { newProject: { version: this.version }, } - this.handler.updateProjectStructure( - this.project_id, - this.projectHistoryId, - this.user_id, - this.changes, - this.source, - err => { - err.should.match(/found file with missing hash/) - this.request.should.not.have.been.called - done() - } - ) + let error + try { + await this.handler.promises.updateProjectStructure( + this.project_id, + this.projectHistoryId, + this.user_id, + this.changes, + this.source + ) + } catch (err) { + error = err + } + expect(error).to.exist }) }) }) }) describe('resyncProjectHistory', function () { - it('should add docs', function (done) { + it('should add docs', async function () { const docId1 = new ObjectId() const docId2 = new ObjectId() const docs = [ @@ -1609,31 +1585,28 @@ describe('DocumentUpdaterHandler', function () { this.request.yields(null, { statusCode: 200 }) const projectId = new ObjectId() const projectHistoryId = 99 - this.handler.resyncProjectHistory( + await this.handler.promises.resyncProjectHistory( projectId, projectHistoryId, docs, files, - {}, - () => { - this.request.should.have.been.calledWith({ - url: `${this.settings.apis.documentupdater.url}/project/${projectId}/history/resync`, - method: 'POST', - json: { - docs: [ - { doc: docId1, path: 'main.tex' }, - { doc: docId2, path: 'references.bib' }, - ], - files: [], - projectHistoryId, - }, - timeout: 6 * 60 * 1000, - }) - done() - } + {} ) + this.request.should.have.been.calledWith({ + url: `${this.settings.apis.documentupdater.url}/project/${projectId}/history/resync`, + method: 'POST', + json: { + docs: [ + { doc: docId1, path: 'main.tex' }, + { doc: docId2, path: 'references.bib' }, + ], + files: [], + projectHistoryId, + }, + timeout: 6 * 60 * 1000, + }) }) - it('should add files', function (done) { + it('should add files', async function () { const fileId1 = new ObjectId() const fileId2 = new ObjectId() const fileId3 = new ObjectId() @@ -1673,66 +1646,63 @@ describe('DocumentUpdaterHandler', function () { this.request.yields(null, { statusCode: 200 }) const projectId = new ObjectId() const projectHistoryId = 99 - this.handler.resyncProjectHistory( + await this.handler.promises.resyncProjectHistory( projectId, projectHistoryId, docs, files, - {}, - () => { - this.request.should.have.been.calledWith({ - url: `${this.settings.apis.documentupdater.url}/project/${projectId}/history/resync`, - method: 'POST', - json: { - docs: [], - files: [ - { - file: fileId1, - _hash: '42', - path: '1.png', - url: `http://filestore/project/${projectId}/file/${fileId1}`, - createdBlob: false, - metadata: undefined, - }, - { - file: fileId2, - _hash: '1337', - path: '1.bib', - url: `http://filestore/project/${projectId}/file/${fileId2}`, - createdBlob: false, - metadata: { - importedAt: fileCreated2, - provider: 'references-provider', - }, - }, - { - file: fileId3, - _hash: '21', - path: 'bar.txt', - url: `http://filestore/project/${projectId}/file/${fileId3}`, - createdBlob: false, - metadata: { - importedAt: fileCreated3, - provider: 'project_output_file', - source_project_id: otherProjectId, - source_output_file_path: 'foo/bar.txt', - // build_id and clsiServerId are omitted - }, - }, - ], - projectHistoryId, - }, - timeout: 6 * 60 * 1000, - }) - done() - } + {} ) + this.request.should.have.been.calledWith({ + url: `${this.settings.apis.documentupdater.url}/project/${projectId}/history/resync`, + method: 'POST', + json: { + docs: [], + files: [ + { + file: fileId1, + _hash: '42', + path: '1.png', + url: `http://filestore/project/${projectId}/file/${fileId1}`, + createdBlob: false, + metadata: undefined, + }, + { + file: fileId2, + _hash: '1337', + path: '1.bib', + url: `http://filestore/project/${projectId}/file/${fileId2}`, + createdBlob: false, + metadata: { + importedAt: fileCreated2, + provider: 'references-provider', + }, + }, + { + file: fileId3, + _hash: '21', + path: 'bar.txt', + url: `http://filestore/project/${projectId}/file/${fileId3}`, + createdBlob: false, + metadata: { + importedAt: fileCreated3, + provider: 'project_output_file', + source_project_id: otherProjectId, + source_output_file_path: 'foo/bar.txt', + // build_id and clsiServerId are omitted + }, + }, + ], + projectHistoryId, + }, + timeout: 6 * 60 * 1000, + }) }) describe('with filestore disabled', function () { beforeEach(function () { this.settings.disableFilestore = true }) - it('should add files without URL', function (done) { + it('should add files without URL', async function () { const fileId1 = new ObjectId() const fileId2 = new ObjectId() const fileId3 = new ObjectId() @@ -1772,62 +1742,59 @@ describe('DocumentUpdaterHandler', function () { this.request.yields(null, { statusCode: 200 }) const projectId = new ObjectId() const projectHistoryId = 99 - this.handler.resyncProjectHistory( + await this.handler.promises.resyncProjectHistory( projectId, projectHistoryId, docs, files, - {}, - () => { - this.request.should.have.been.calledWith({ - url: `${this.settings.apis.documentupdater.url}/project/${projectId}/history/resync`, - method: 'POST', - json: { - docs: [], - files: [ - { - file: fileId1, - _hash: '42', - path: '1.png', - url: undefined, - createdBlob: true, - metadata: undefined, - }, - { - file: fileId2, - _hash: '1337', - path: '1.bib', - url: undefined, - createdBlob: true, - metadata: { - importedAt: fileCreated2, - provider: 'references-provider', - }, - }, - { - file: fileId3, - _hash: '21', - path: 'bar.txt', - url: undefined, - createdBlob: true, - metadata: { - importedAt: fileCreated3, - provider: 'project_output_file', - source_project_id: otherProjectId, - source_output_file_path: 'foo/bar.txt', - // build_id and clsiServerId are omitted - }, - }, - ], - projectHistoryId, - }, - timeout: 6 * 60 * 1000, - }) - done() - } + {} ) + this.request.should.have.been.calledWith({ + url: `${this.settings.apis.documentupdater.url}/project/${projectId}/history/resync`, + method: 'POST', + json: { + docs: [], + files: [ + { + file: fileId1, + _hash: '42', + path: '1.png', + url: undefined, + createdBlob: true, + metadata: undefined, + }, + { + file: fileId2, + _hash: '1337', + path: '1.bib', + url: undefined, + createdBlob: true, + metadata: { + importedAt: fileCreated2, + provider: 'references-provider', + }, + }, + { + file: fileId3, + _hash: '21', + path: 'bar.txt', + url: undefined, + createdBlob: true, + metadata: { + importedAt: fileCreated3, + provider: 'project_output_file', + source_project_id: otherProjectId, + source_output_file_path: 'foo/bar.txt', + // build_id and clsiServerId are omitted + }, + }, + ], + projectHistoryId, + }, + timeout: 6 * 60 * 1000, + }) }) - it('should flag files with missing hashes', function (done) { + it('should flag files with missing hashes', async function () { const fileId1 = new ObjectId() const fileId2 = new ObjectId() const fileId3 = new ObjectId() @@ -1866,18 +1833,15 @@ describe('DocumentUpdaterHandler', function () { this.request.yields(null, { statusCode: 200 }) const projectId = new ObjectId() const projectHistoryId = 99 - this.handler.resyncProjectHistory( - projectId, - projectHistoryId, - docs, - files, - {}, - err => { - err.should.match(/found file with missing hash/) - this.request.should.not.have.been.called - done() - } - ) + await expect( + this.handler.promises.resyncProjectHistory( + projectId, + projectHistoryId, + docs, + files, + {} + ) + ).to.be.rejected }) }) }) @@ -1889,17 +1853,16 @@ describe('DocumentUpdaterHandler', function () { rev: 1, } this.request.callsArgWith(1, null, { statusCode: 200 }, this.body) - this.handler.appendToDocument( + }) + + it('should append to the document in the document updater', async function () { + await this.handler.promises.appendToDocument( this.project_id, this.doc_id, this.user_id, this.lines, - this.source, - this.callback + this.source ) - }) - - it('should append to the document in the document updater', function () { this.request .calledWith({ url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}/append`, @@ -1913,10 +1876,6 @@ describe('DocumentUpdaterHandler', function () { }) .should.equal(true) }) - - it('should call the callback with no error', function () { - this.callback.calledWith(null).should.equal(true) - }) }) describe('when the document updater API returns an error', function () { @@ -1927,49 +1886,52 @@ describe('DocumentUpdaterHandler', function () { null, null ) - this.handler.appendToDocument( - this.project_id, - this.doc_id, - this.user_id, - this.lines, - this.source, - this.callback - ) }) - it('should return an error to the callback', function () { - this.callback - .calledWith(sinon.match.instanceOf(Error)) - .should.equal(true) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.appendToDocument( + this.project_id, + this.doc_id, + this.user_id, + this.lines, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('when the document updater returns a failure error code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') - this.handler.appendToDocument( - this.project_id, - this.doc_id, - this.user_id, - this.lines, - this.source, - this.callback - ) }) - it('should return the callback with an error', function () { - this.callback - .calledWith( - sinon.match - .instanceOf(Error) - .and( - sinon.match.has( - 'message', - 'document updater returned a failure status code: 500' - ) - ) + it('should reject with an error', async function () { + let error + + try { + await this.handler.promises.appendToDocument( + this.project_id, + this.doc_id, + this.user_id, + this.lines, + this.source ) - .should.equal(true) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'document updater returned a failure status code: 500' + ) }) }) }) diff --git a/services/web/test/unit/src/FileStore/FileStoreHandlerTests.js b/services/web/test/unit/src/FileStore/FileStoreHandlerTests.js index bf9fe6c900..3a66f275a7 100644 --- a/services/web/test/unit/src/FileStore/FileStoreHandlerTests.js +++ b/services/web/test/unit/src/FileStore/FileStoreHandlerTests.js @@ -95,7 +95,7 @@ describe('FileStoreHandler', function () { this.request.returns(this.writeStream) }) - it('should get the project details', function (done) { + it('should get the project details', async function () { this.fs.createReadStream.returns({ pipe() {}, on(type, cb) { @@ -104,20 +104,17 @@ describe('FileStoreHandler', function () { } }, }) - this.handler.uploadFileFromDisk( + await this.handler.promises.uploadFileFromDisk( this.projectId, this.fileArgs, - this.fsPath, - () => { - this.ProjectDetailsHandler.getDetails - .calledWith(this.projectId) - .should.equal(true) - done() - } + this.fsPath ) + this.ProjectDetailsHandler.getDetails + .calledWith(this.projectId) + .should.equal(true) }) - it('should compute the file hash', function (done) { + it('should compute the file hash', async function () { this.fs.createReadStream.returns({ pipe() {}, on(type, cb) { @@ -126,21 +123,18 @@ describe('FileStoreHandler', function () { } }, }) - this.handler.uploadFileFromDisk( + await this.handler.promises.uploadFileFromDisk( this.projectId, this.fileArgs, - this.fsPath, - () => { - this.FileHashManager.computeHash - .calledWith(this.fsPath) - .should.equal(true) - done() - } + this.fsPath ) + this.FileHashManager.computeHash + .calledWith(this.fsPath) + .should.equal(true) }) describe('when project-history-blobs feature is enabled', function () { - it('should upload the file to the history store as a blob', function (done) { + it('should upload the file to the history store as a blob', async function () { this.fs.createReadStream.returns({ pipe() {}, on(type, cb) { @@ -150,26 +144,23 @@ describe('FileStoreHandler', function () { }, }) this.Features.hasFeature.withArgs('project-history-blobs').returns(true) - this.handler.uploadFileFromDisk( + await this.handler.promises.uploadFileFromDisk( this.projectId, this.fileArgs, - this.fsPath, - () => { - this.HistoryManager.uploadBlobFromDisk - .calledWith( - this.historyId, - this.hashValue, - this.fileSize, - this.fsPath - ) - .should.equal(true) - done() - } + this.fsPath ) + this.HistoryManager.uploadBlobFromDisk + .calledWith( + this.historyId, + this.hashValue, + this.fileSize, + this.fsPath + ) + .should.equal(true) }) }) describe('when project-history-blobs feature is disabled', function () { - it('should not upload the file to the history store as a blob', function (done) { + it('should not upload the file to the history store as a blob', async function () { this.fs.createReadStream.returns({ pipe() {}, on(type, cb) { @@ -178,15 +169,12 @@ describe('FileStoreHandler', function () { } }, }) - this.handler.uploadFileFromDisk( + await this.handler.promises.uploadFileFromDisk( this.projectId, this.fileArgs, - this.fsPath, - () => { - this.HistoryManager.uploadBlobFromDisk.called.should.equal(false) - done() - } + this.fsPath ) + this.HistoryManager.uploadBlobFromDisk.called.should.equal(false) }) }) @@ -214,71 +202,63 @@ describe('FileStoreHandler', function () { ) }) - it('should pipe the read stream to request', function (done) { + it('should pipe the read stream to request', function () { this.request.returns(this.writeStream) - this.fs.createReadStream.returns({ - on(type, cb) { - if (type === 'open') { - cb() - } - }, - pipe: o => { - this.writeStream.should.equal(o) - done() - }, + return new Promise((resolve, reject) => { + this.fs.createReadStream.returns({ + on(type, cb) { + if (type === 'open') { + cb() + } + }, + pipe: o => { + this.writeStream.should.equal(o) + resolve() + }, + }) + this.handler.promises + .uploadFileFromDisk(this.projectId, this.fileArgs, this.fsPath) + .catch(reject) }) - this.handler.uploadFileFromDisk( - this.projectId, - this.fileArgs, - this.fsPath, - () => {} - ) }) - it('should pass the correct options to request', function (done) { + it('should pass the correct options to request', async function () { const fileUrl = this.getFileUrl(this.projectId, this.fileId) this.fs.createReadStream.returns({ - pipe() {}, - on(type, cb) { + pipe: sinon.stub(), + on: sinon.stub((type, cb) => { if (type === 'open') { cb() } - }, + }), }) - this.handler.uploadFileFromDisk( + await this.handler.promises.uploadFileFromDisk( this.projectId, this.fileArgs, - this.fsPath, - () => { - this.request.args[0][0].method.should.equal('post') - this.request.args[0][0].uri.should.equal(fileUrl) - done() - } + this.fsPath ) + this.request.args[0][0].method.should.equal('post') + this.request.args[0][0].uri.should.equal(fileUrl) }) - it('should callback with the url and fileRef', function (done) { + it('should resolve with the url and fileRef', async function () { const fileUrl = this.getFileUrl(this.projectId, this.fileId) this.fs.createReadStream.returns({ - pipe() {}, - on(type, cb) { + pipe: sinon.stub(), + on: sinon.stub((type, cb) => { if (type === 'open') { cb() } - }, + }), }) - this.handler.uploadFileFromDisk( + const { url, fileRef } = await this.handler.promises.uploadFileFromDisk( this.projectId, this.fileArgs, - this.fsPath, - (err, url, fileRef) => { - expect(err).to.not.exist - expect(url).to.equal(fileUrl) - expect(fileRef._id).to.equal(this.fileId) - expect(fileRef.hash).to.equal(this.hashValue) - done() - } + this.fsPath ) + expect(url).to.equal(fileUrl) + expect(fileRef._id).to.equal(this.fileId) + expect(fileRef.hash).to.equal(this.hashValue) }) describe('when upload to filestore fails', function () { beforeEach(function () { @@ -289,28 +269,32 @@ describe('FileStoreHandler', function () { } }) - it('should callback with an error', function (done) { + it('should reject with an error', async function () { this.fs.createReadStream.callCount = 0 this.fs.createReadStream.returns({ - pipe() {}, - on(type, cb) { + pipe: sinon.stub(), + on: sinon.stub((type, cb) => { if (type === 'open') { cb() } - }, + }), }) - this.handler.uploadFileFromDisk( - this.projectId, - this.fileArgs, - this.fsPath, - err => { - expect(err).to.exist - expect(err).to.be.instanceof(Error) - expect(this.fs.createReadStream.callCount).to.equal( - this.handler.RETRY_ATTEMPTS - ) - done() - } + let error + + try { + await this.handler.promises.uploadFileFromDisk( + this.projectId, + this.fileArgs, + this.fsPath + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + + expect(this.fs.createReadStream.callCount).to.equal( + this.handler.RETRY_ATTEMPTS ) }) }) @@ -319,49 +303,40 @@ describe('FileStoreHandler', function () { beforeEach(function () { this.Features.hasFeature.withArgs('filestore').returns(false) }) - it('should not open file handle', function (done) { - this.handler.uploadFileFromDisk( + it('should not open file handle', async function () { + await this.handler.promises.uploadFileFromDisk( this.projectId, this.fileArgs, - this.fsPath, - () => { - expect(this.fs.createReadStream).to.not.have.been.called - done() - } + this.fsPath ) + expect(this.fs.createReadStream).to.not.have.been.called }) - it('should not talk to filestore', function (done) { - this.handler.uploadFileFromDisk( + it('should not talk to filestore', async function () { + await this.handler.promises.uploadFileFromDisk( this.projectId, this.fileArgs, - this.fsPath, - () => { - expect(this.request).to.not.have.been.called - done() - } + this.fsPath ) + + expect(this.request).to.not.have.been.called }) - it('should callback with the url and fileRef', function (done) { + it('should resolve with the url and fileRef', async function () { const fileUrl = this.getFileUrl(this.projectId, this.fileId) - this.handler.uploadFileFromDisk( + const { url, fileRef } = await this.handler.promises.uploadFileFromDisk( this.projectId, this.fileArgs, - this.fsPath, - (err, url, fileRef) => { - expect(err).to.not.exist - expect(url).to.equal(fileUrl) - expect(fileRef._id).to.equal(this.fileId) - expect(fileRef.hash).to.equal(this.hashValue) - done() - } + this.fsPath ) + expect(url).to.equal(fileUrl) + expect(fileRef._id).to.equal(this.fileId) + expect(fileRef.hash).to.equal(this.hashValue) }) }) describe('symlink', function () { - it('should not read file if it is symlink', function (done) { + it('should not read file if it is symlink', async function () { this.fs.lstat = sinon.stub().callsArgWith(1, null, { isFile() { return false @@ -371,28 +346,40 @@ describe('FileStoreHandler', function () { }, }) - this.handler.uploadFileFromDisk( - this.projectId, - this.fileArgs, - this.fsPath, - () => { - this.fs.createReadStream.called.should.equal(false) - done() - } - ) + let error + + try { + await this.handler.promises.uploadFileFromDisk( + this.projectId, + this.fileArgs, + this.fsPath + ) + } catch (err) { + error = err + } + + expect(error).to.exist + + this.fs.createReadStream.called.should.equal(false) }) - it('should not read file stat returns nothing', function (done) { + it('should not read file stat returns nothing', async function () { this.fs.lstat = sinon.stub().callsArgWith(1, null, null) - this.handler.uploadFileFromDisk( - this.projectId, - this.fileArgs, - this.fsPath, - () => { - this.fs.createReadStream.called.should.equal(false) - done() - } - ) + let error + + try { + await this.handler.promises.uploadFileFromDisk( + this.projectId, + this.fileArgs, + this.fsPath + ) + } catch (err) { + error = err + } + + expect(error).to.exist + + this.fs.createReadStream.called.should.equal(false) }) }) }) @@ -410,13 +397,18 @@ describe('FileStoreHandler', function () { }) }) - it('should return the error if there is one', function (done) { - const error = 'my error' - this.request.callsArgWith(1, error) - this.handler.deleteFile(this.projectId, this.fileId, err => { - assert.equal(err, error) - done() - }) + it('should reject with the error if there is one', async function () { + const expectedError = 'my error' + this.request.callsArgWith(1, expectedError) + let error + + try { + await this.handler.promises.deleteFile(this.projectId, this.fileId) + } catch (err) { + error = err + } + + expect(error).to.equal(expectedError) }) }) @@ -425,40 +417,43 @@ describe('FileStoreHandler', function () { beforeEach(function () { this.Features.hasFeature.withArgs('filestore').returns(true) }) - it('should send a delete request to filestore api', function (done) { + it('should send a delete request to filestore api', async function () { const projectUrl = this.getProjectUrl(this.projectId) this.request.callsArgWith(1, null) - this.handler.deleteProject(this.projectId, err => { - assert.equal(err, undefined) - this.request.args[0][0].method.should.equal('delete') - this.request.args[0][0].uri.should.equal(projectUrl) - done() - }) + await this.handler.promises.deleteProject(this.projectId) + this.request.args[0][0].method.should.equal('delete') + this.request.args[0][0].uri.should.equal(projectUrl) }) - it('should wrap the error if there is one', function (done) { - const error = new Error('my error') - this.request.callsArgWith(1, error) - this.handler.deleteProject(this.projectId, err => { - expect(OError.getFullStack(err)).to.match( - /something went wrong deleting a project in filestore/ - ) - expect(OError.getFullStack(err)).to.match(/my error/) - done() - }) + it('should wrap the error if there is one', async function () { + const expectedError = new Error('my error') + this.request.callsArgWith(1, expectedError) + const promise = this.handler.promises.deleteProject(this.projectId) + let error + + try { + await promise + } catch (err) { + error = err + } + + expect(error).to.exist + + expect(OError.getFullStack(error)).to.match( + /something went wrong deleting a project in filestore/ + ) + expect(OError.getFullStack(error)).to.match(/my error/) }) }) describe('when filestore is disabled', function () { beforeEach(function () { this.Features.hasFeature.withArgs('filestore').returns(false) }) - it('should not send a delete request to filestore api', function (done) { - this.handler.deleteProject(this.projectId, err => { - assert.equal(err, undefined) - this.request.called.should.equal(false) - done() - }) + it('should not send a delete request to filestore api', async function () { + await this.handler.promises.deleteProject(this.projectId) + + this.request.called.should.equal(false) }) }) }) @@ -469,18 +464,37 @@ describe('FileStoreHandler', function () { this.Features.hasFeature.withArgs('filestore').returns(false) }) - it('should callback with a NotFoundError', function (done) { - this.handler.getFileStream(this.projectId, this.fileId, {}, err => { - expect(err).to.be.instanceof(Errors.NotFoundError) - done() - }) + it('should callback with a NotFoundError', async function () { + let error + + try { + await this.handler.promises.getFileStream( + this.projectId, + this.fileId, + {} + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.NotFoundError) }) - it('should not call request', function (done) { - this.handler.getFileStream(this.projectId, this.fileId, {}, () => { - this.request.called.should.equal(false) - done() - }) + it('should not call request', async function () { + let error + + try { + await this.handler.promises.getFileStream( + this.projectId, + this.fileId, + {} + ) + } catch (err) { + error = err + } + + expect(error).to.exist + this.request.called.should.equal(false) }) }) describe('when filestore is enabled', function () { @@ -490,53 +504,36 @@ describe('FileStoreHandler', function () { this.Features.hasFeature.withArgs('filestore').returns(true) }) - it('should get the stream with the correct params', function (done) { + it('should get the stream with the correct params', async function () { const fileUrl = this.getFileUrl(this.projectId, this.fileId) - this.handler.getFileStream( + await this.handler.promises.getFileStream( this.projectId, this.fileId, - this.query, - (err, stream) => { - if (err) { - return done(err) - } - this.request.args[0][0].method.should.equal('get') - this.request.args[0][0].uri.should.equal( - fileUrl + '?from=getFileStream' - ) - done() - } + this.query + ) + this.request.args[0][0].method.should.equal('get') + this.request.args[0][0].uri.should.equal( + fileUrl + '?from=getFileStream' ) }) - it('should get stream from request', function (done) { - this.handler.getFileStream( + it('should get stream from request', async function () { + const stream = await this.handler.promises.getFileStream( this.projectId, this.fileId, - this.query, - (err, stream) => { - if (err) { - return done(err) - } - stream.should.equal(this.readStream) - done() - } + this.query ) + + stream.should.equal(this.readStream) }) - it('should add an error handler', function (done) { - this.handler.getFileStream( + it('should add an error handler', async function () { + const stream = await this.handler.promises.getFileStream( this.projectId, this.fileId, - this.query, - (err, stream) => { - if (err) { - return done(err) - } - stream.on.calledWith('error').should.equal(true) - done() - } + this.query ) + stream.on.calledWith('error').should.equal(true) }) describe('when range is specified in query', function () { @@ -544,22 +541,17 @@ describe('FileStoreHandler', function () { this.query = { range: '0-10' } }) - it('should add a range header', function (done) { - this.handler.getFileStream( + it('should add a range header', async function () { + await this.handler.promises.getFileStream( this.projectId, this.fileId, - this.query, - (err, stream) => { - if (err) { - return done(err) - } - this.request.callCount.should.equal(1) - const { headers } = this.request.firstCall.args[0] - expect(headers).to.have.keys('range') - expect(headers.range).to.equal('bytes=0-10') - done() - } + this.query ) + + this.request.callCount.should.equal(1) + const { headers } = this.request.firstCall.args[0] + expect(headers).to.have.keys('range') + expect(headers.range).to.equal('bytes=0-10') }) describe('when range is invalid', function () { @@ -568,21 +560,15 @@ describe('FileStoreHandler', function () { this.query = { range: `${r}` } }) - it(`should not add a range header for '${r}'`, function (done) { - this.handler.getFileStream( + it(`should not add a range header for '${r}'`, async function () { + await this.handler.promises.getFileStream( this.projectId, this.fileId, - this.query, - (err, stream) => { - if (err) { - return done(err) - } - this.request.callCount.should.equal(1) - const { headers } = this.request.firstCall.args[0] - expect(headers).to.not.have.keys('range') - done() - } + this.query ) + this.request.callCount.should.equal(1) + const { headers } = this.request.firstCall.args[0] + expect(headers).to.not.have.keys('range') }) }) }) @@ -591,7 +577,7 @@ describe('FileStoreHandler', function () { }) describe('getFileSize', function () { - it('returns the file size reported by filestore', function (done) { + it('returns the file size reported by filestore', async function () { const expectedFileSize = 32432 const fileUrl = this.getFileUrl(this.projectId, this.fileId) + '?from=getFileSize' @@ -605,40 +591,54 @@ describe('FileStoreHandler', function () { }, }) - this.handler.getFileSize(this.projectId, this.fileId, (err, fileSize) => { - if (err) { - return done(err) - } - expect(fileSize).to.equal(expectedFileSize) - done() - }) + const fileSize = await this.handler.promises.getFileSize( + this.projectId, + this.fileId + ) + expect(fileSize).to.equal(expectedFileSize) }) - it('throws a NotFoundError on a 404 from filestore', function (done) { + it('throws a NotFoundError on a 404 from filestore', async function () { this.request.head.yields(null, { statusCode: 404 }) - this.handler.getFileSize(this.projectId, this.fileId, err => { - expect(err).to.be.instanceof(Errors.NotFoundError) - done() - }) + let error + + try { + await this.handler.promises.getFileSize(this.projectId, this.fileId) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.NotFoundError) }) - it('throws an error on a non-200 from filestore', function (done) { + it('throws an error on a non-200 from filestore', async function () { this.request.head.yields(null, { statusCode: 500 }) - this.handler.getFileSize(this.projectId, this.fileId, err => { - expect(err).to.be.instanceof(Error) - done() - }) + let error + + try { + await this.handler.promises.getFileSize(this.projectId, this.fileId) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) - it('rethrows errors from filestore', function (done) { - this.request.head.yields(new Error()) + it('rethrows errors from filestore', async function () { + const expectedError = new Error('from filestore') + this.request.head.yields(expectedError) - this.handler.getFileSize(this.projectId, this.fileId, err => { - expect(err).to.be.instanceof(Error) - done() - }) + let error + + try { + await this.handler.promises.getFileSize(this.projectId, this.fileId) + } catch (err) { + error = err + } + + expect(error).to.equal(expectedError) }) }) @@ -648,74 +648,75 @@ describe('FileStoreHandler', function () { this.newFileId = 'new file id' }) - it('should post json', function (done) { + it('should post json', async function () { const newFileUrl = this.getFileUrl(this.newProjectId, this.newFileId) this.request.callsArgWith(1, null, { statusCode: 200 }) - this.handler.copyFile( + await this.handler.promises.copyFile( this.projectId, this.fileId, this.newProjectId, - this.newFileId, - () => { - this.request.args[0][0].method.should.equal('put') - this.request.args[0][0].uri.should.equal(newFileUrl) - this.request.args[0][0].json.source.project_id.should.equal( - this.projectId - ) - this.request.args[0][0].json.source.file_id.should.equal(this.fileId) - done() - } + this.newFileId ) + this.request.args[0][0].method.should.equal('put') + this.request.args[0][0].uri.should.equal(newFileUrl) + this.request.args[0][0].json.source.project_id.should.equal( + this.projectId + ) + this.request.args[0][0].json.source.file_id.should.equal(this.fileId) }) - it('returns the url', function (done) { + it('returns the url', async function () { const expectedUrl = this.getFileUrl(this.newProjectId, this.newFileId) this.request.callsArgWith(1, null, { statusCode: 200 }) - this.handler.copyFile( + const url = await this.handler.promises.copyFile( this.projectId, this.fileId, this.newProjectId, - this.newFileId, - (err, url) => { - if (err) { - return done(err) - } - url.should.equal(expectedUrl) - done() - } + this.newFileId ) + + url.should.equal(expectedUrl) }) - it('should return the err', function (done) { - const error = new Error('error') - this.request.callsArgWith(1, error) - this.handler.copyFile( - this.projectId, - this.fileId, - this.newProjectId, - this.newFileId, - err => { - err.should.equal(error) - done() - } - ) + it('should return the err', async function () { + const expectedError = new Error('error') + this.request.callsArgWith(1, expectedError) + let error + + try { + await this.handler.promises.copyFile( + this.projectId, + this.fileId, + this.newProjectId, + this.newFileId + ) + } catch (err) { + error = err + } + + expect(error).to.equal(expectedError) }) - it('should return an error for a non-success statusCode', function (done) { + it('should return an error for a non-success statusCode', async function () { this.request.callsArgWith(1, null, { statusCode: 500 }) - this.handler.copyFile( - this.projectId, - this.fileId, - this.newProjectId, - this.newFileId, - err => { - err.should.be.an('error') - err.message.should.equal( - 'non-ok response from filestore for copyFile: 500' - ) - done() - } + let error + + try { + await this.handler.promises.copyFile( + this.projectId, + this.fileId, + this.newProjectId, + this.newFileId + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + 'non-ok response from filestore for copyFile: 500' ) }) }) diff --git a/services/web/test/unit/src/Institutions/InstitutionsAPITests.js b/services/web/test/unit/src/Institutions/InstitutionsAPITests.js index eb98115ec2..f3458f51f7 100644 --- a/services/web/test/unit/src/Institutions/InstitutionsAPITests.js +++ b/services/web/test/unit/src/Institutions/InstitutionsAPITests.js @@ -45,48 +45,43 @@ describe('InstitutionsAPI', function () { }) describe('getInstitutionAffiliations', function () { - it('get affiliations', function (done) { + it('get affiliations', async function () { this.institutionId = 123 const responseBody = ['123abc', '456def'] this.request.yields(null, { statusCode: 200 }, responseBody) - this.InstitutionsAPI.getInstitutionAffiliations( - this.institutionId, - (err, body) => { - expect(err).not.to.exist - this.request.calledOnce.should.equal(true) - const requestOptions = this.request.lastCall.args[0] - const expectedUrl = `v1.url/api/v2/institutions/${this.institutionId}/affiliations` - requestOptions.url.should.equal(expectedUrl) - requestOptions.method.should.equal('GET') - requestOptions.maxAttempts.should.exist - requestOptions.maxAttempts.should.not.equal(0) - requestOptions.retryDelay.should.exist - expect(requestOptions.body).not.to.exist - body.should.equal(responseBody) - done() - } - ) + const body = + await this.InstitutionsAPI.promises.getInstitutionAffiliations( + this.institutionId + ) + + this.request.calledOnce.should.equal(true) + const requestOptions = this.request.lastCall.args[0] + const expectedUrl = `v1.url/api/v2/institutions/${this.institutionId}/affiliations` + requestOptions.url.should.equal(expectedUrl) + requestOptions.method.should.equal('GET') + requestOptions.maxAttempts.should.exist + requestOptions.maxAttempts.should.not.equal(0) + requestOptions.retryDelay.should.exist + expect(requestOptions.body).not.to.exist + body.should.equal(responseBody) }) - it('handle empty response', function (done) { + it('handle empty response', async function () { this.settings.apis.v1.url = '' - this.InstitutionsAPI.getInstitutionAffiliations( - this.institutionId, - (err, body) => { - expect(err).not.to.exist - expect(body).to.be.a('Array') - body.length.should.equal(0) - done() - } - ) + const body = + await this.InstitutionsAPI.promises.getInstitutionAffiliations( + this.institutionId + ) + expect(body).to.be.a('Array') + body.length.should.equal(0) }) }) describe('getLicencesForAnalytics', function () { const lag = 'daily' const queryDate = '2017-01-07:00:00.000Z' - it('should send the request to v1', function (done) { + it('should send the request to v1', async function () { const v1Result = { lag: 'daily', date: queryDate, @@ -96,100 +91,91 @@ describe('InstitutionsAPI', function () { }, } this.request.callsArgWith(1, null, { statusCode: 201 }, v1Result) - this.InstitutionsAPI.getLicencesForAnalytics( + await this.InstitutionsAPI.promises.getLicencesForAnalytics( lag, - queryDate, - (error, result) => { - expect(error).not.to.exist - const requestOptions = this.request.lastCall.args[0] - expect(requestOptions.body.query_date).to.equal(queryDate) - expect(requestOptions.body.lag).to.equal(lag) - requestOptions.method.should.equal('GET') - done() - } + queryDate ) + const requestOptions = this.request.lastCall.args[0] + expect(requestOptions.body.query_date).to.equal(queryDate) + expect(requestOptions.body.lag).to.equal(lag) + requestOptions.method.should.equal('GET') }) - it('should handle errors', function (done) { + it('should handle errors', async function () { this.request.callsArgWith(1, null, { statusCode: 500 }) - this.InstitutionsAPI.getLicencesForAnalytics( - lag, - queryDate, - (error, result) => { - expect(error).to.be.instanceof(Errors.V1ConnectionError) - done() - } - ) + let error + + try { + await this.InstitutionsAPI.promises.getLicencesForAnalytics( + lag, + queryDate + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.V1ConnectionError) }) }) describe('getUserAffiliations', function () { - it('get affiliations', function (done) { + it('get affiliations', async function () { const responseBody = [{ foo: 'bar' }] this.request.callsArgWith(1, null, { statusCode: 201 }, responseBody) - this.InstitutionsAPI.getUserAffiliations( - this.stubbedUser._id, - (err, body) => { - expect(err).not.to.exist - this.request.calledOnce.should.equal(true) - const requestOptions = this.request.lastCall.args[0] - const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations` - requestOptions.url.should.equal(expectedUrl) - requestOptions.method.should.equal('GET') - requestOptions.maxAttempts.should.equal(3) - expect(requestOptions.body).not.to.exist - body.should.equal(responseBody) - done() - } + const body = await this.InstitutionsAPI.promises.getUserAffiliations( + this.stubbedUser._id ) + this.request.calledOnce.should.equal(true) + const requestOptions = this.request.lastCall.args[0] + const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations` + requestOptions.url.should.equal(expectedUrl) + requestOptions.method.should.equal('GET') + requestOptions.maxAttempts.should.equal(3) + expect(requestOptions.body).not.to.exist + body.should.equal(responseBody) }) - it('handle error', function (done) { + it('handle error', async function () { const body = { errors: 'affiliation error message' } this.request.callsArgWith(1, null, { statusCode: 503 }, body) - this.InstitutionsAPI.getUserAffiliations(this.stubbedUser._id, err => { - expect(err).to.be.instanceof(Errors.V1ConnectionError) - done() - }) + let error + + try { + await this.InstitutionsAPI.promises.getUserAffiliations( + this.stubbedUser._id + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.V1ConnectionError) }) - it('handle empty response', function (done) { + it('handle empty response', async function () { this.settings.apis.v1.url = '' - this.InstitutionsAPI.getUserAffiliations( - this.stubbedUser._id, - (err, body) => { - expect(err).not.to.exist - expect(body).to.be.a('Array') - body.length.should.equal(0) - done() - } + const body = await this.InstitutionsAPI.promises.getUserAffiliations( + this.stubbedUser._id ) + expect(body).to.be.a('Array') + body.length.should.equal(0) }) }) describe('getUsersNeedingReconfirmationsLapsedProcessed', function () { - it('get the list of users', function (done) { + it('get the list of users', async function () { this.fetchJson.resolves({ statusCode: 200 }) - this.InstitutionsAPI.getUsersNeedingReconfirmationsLapsedProcessed( - error => { - expect(error).not.to.exist - this.fetchJson.calledOnce.should.equal(true) - const requestOptions = this.fetchJson.lastCall.args[1] - const expectedUrl = `v1.url/api/v2/institutions/need_reconfirmation_lapsed_processed` - this.fetchJson.lastCall.args[0].should.equal(expectedUrl) - requestOptions.method.should.equal('GET') - done() - } - ) + await this.InstitutionsAPI.promises.getUsersNeedingReconfirmationsLapsedProcessed() + this.fetchJson.calledOnce.should.equal(true) + const requestOptions = this.fetchJson.lastCall.args[1] + const expectedUrl = `v1.url/api/v2/institutions/need_reconfirmation_lapsed_processed` + this.fetchJson.lastCall.args[0].should.equal(expectedUrl) + requestOptions.method.should.equal('GET') }) - it('handle error', function (done) { + it('handle error', async function () { this.fetchJson.throws({ info: { statusCode: 500 } }) - this.InstitutionsAPI.getUsersNeedingReconfirmationsLapsedProcessed( - error => { - expect(error).to.exist - done() - } - ) + await expect( + this.InstitutionsAPI.promises.getUsersNeedingReconfirmationsLapsedProcessed() + ).to.be.rejected }) }) @@ -198,7 +184,7 @@ describe('InstitutionsAPI', function () { this.fetchNothing.resolves({ status: 201 }) }) - it('add affiliation', function (done) { + it('add affiliation', async function () { const affiliationOptions = { university: { id: 1 }, department: 'Math', @@ -206,95 +192,104 @@ describe('InstitutionsAPI', function () { confirmedAt: new Date(), entitlement: true, } - this.InstitutionsAPI.addAffiliation( + await this.InstitutionsAPI.promises.addAffiliation( this.stubbedUser._id, this.newEmail, - affiliationOptions, - err => { - expect(err).not.to.exist - this.fetchNothing.calledOnce.should.equal(true) - const requestOptions = this.fetchNothing.lastCall.args[1] - const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations` - expect(this.fetchNothing.lastCall.args[0]).to.equal(expectedUrl) - requestOptions.method.should.equal('POST') - - const { json } = requestOptions - Object.keys(json).length.should.equal(7) - expect(json).to.deep.equal( - Object.assign( - { email: this.newEmail, rejectIfBlocklisted: undefined }, - affiliationOptions - ) - ) - this.markAsReadIpMatcher.calledOnce.should.equal(true) - done() - } + affiliationOptions ) + this.fetchNothing.calledOnce.should.equal(true) + const requestOptions = this.fetchNothing.lastCall.args[1] + const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations` + expect(this.fetchNothing.lastCall.args[0]).to.equal(expectedUrl) + requestOptions.method.should.equal('POST') + + const { json } = requestOptions + Object.keys(json).length.should.equal(7) + expect(json).to.deep.equal( + Object.assign( + { email: this.newEmail, rejectIfBlocklisted: undefined }, + affiliationOptions + ) + ) + this.markAsReadIpMatcher.calledOnce.should.equal(true) }) - it('handles 422 error', function (done) { + it('handles 422 error', async function () { const messageFromApi = 'affiliation error message' const body = JSON.stringify({ errors: messageFromApi }) this.fetchNothing.throws({ response: { status: 422 }, body }) - this.InstitutionsAPI.addAffiliation( - this.stubbedUser._id, - this.newEmail, - {}, - err => { - expect(err).to.exist - expect(err).to.be.instanceOf(Errors.InvalidInstitutionalEmailError) - err.message.should.have.string(422) - err.message.should.have.string(messageFromApi) - done() - } - ) + let error + + try { + await this.InstitutionsAPI.promises.addAffiliation( + this.stubbedUser._id, + this.newEmail, + {} + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidInstitutionalEmailError) + expect(error).to.have.property('message', `422: ${messageFromApi}`) }) - it('handles 500 error', function (done) { + it('handles 500 error', async function () { const body = { errors: 'affiliation error message' } this.fetchNothing.throws({ response: { status: 500 }, body }) - this.InstitutionsAPI.addAffiliation( - this.stubbedUser._id, - this.newEmail, - {}, - err => { - expect(err).to.be.instanceOf(Errors.V1ConnectionError) - expect(err.message).to.equal('error getting affiliations from v1') - expect(err.info).to.deep.equal({ status: 500, body }) - done() - } - ) + let error + + try { + await this.InstitutionsAPI.promises.addAffiliation( + this.stubbedUser._id, + this.newEmail, + {} + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.V1ConnectionError) + expect(error.message).to.equal('error getting affiliations from v1') + expect(error.info).to.eql({ + status: 500, + body: { errors: 'affiliation error message' }, + }) }) - it('uses default error message when no error body in response', function (done) { + it('uses default error message when no error body in response', async function () { this.fetchNothing.throws({ response: { status: 429 } }) - this.InstitutionsAPI.addAffiliation( - this.stubbedUser._id, - this.newEmail, - {}, - err => { - expect(err).to.exist - expect(err.message).to.equal("Couldn't create affiliation: 429") - done() - } + let error + + try { + await this.InstitutionsAPI.promises.addAffiliation( + this.stubbedUser._id, + this.newEmail, + {} + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) + expect(error).to.have.property( + 'message', + "Couldn't create affiliation: 429" ) }) - it('does not try to mark IP matcher notifications as read if no university passed', function (done) { + it('does not try to mark IP matcher notifications as read if no university passed', async function () { const affiliationOptions = { confirmedAt: new Date(), } - this.InstitutionsAPI.addAffiliation( + await this.InstitutionsAPI.promises.addAffiliation( this.stubbedUser._id, this.newEmail, - affiliationOptions, - err => { - expect(err).not.to.exist - expect(this.markAsReadIpMatcher.callCount).to.equal(0) - done() - } + affiliationOptions ) + + expect(this.markAsReadIpMatcher.callCount).to.equal(0) }) }) @@ -303,58 +298,64 @@ describe('InstitutionsAPI', function () { this.fetchNothing.throws({ response: { status: 404 } }) }) - it('remove affiliation', function (done) { - this.InstitutionsAPI.removeAffiliation( + it('remove affiliation', async function () { + await this.InstitutionsAPI.promises.removeAffiliation( this.stubbedUser._id, - this.newEmail, - err => { - expect(err).not.to.exist - this.fetchNothing.calledOnce.should.equal(true) - const requestOptions = this.fetchNothing.lastCall.args[1] - const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations/remove` - this.fetchNothing.lastCall.args[0].should.equal(expectedUrl) - requestOptions.method.should.equal('POST') - expect(requestOptions.json).to.deep.equal({ email: this.newEmail }) - done() - } + this.newEmail ) + this.fetchNothing.calledOnce.should.equal(true) + const requestOptions = this.fetchNothing.lastCall.args[1] + const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations/remove` + this.fetchNothing.lastCall.args[0].should.equal(expectedUrl) + requestOptions.method.should.equal('POST') + expect(requestOptions.json).to.deep.equal({ email: this.newEmail }) }) - it('handle error', function (done) { + it('handle error', async function () { this.fetchNothing.throws({ response: { status: 500 } }) - this.InstitutionsAPI.removeAffiliation( - this.stubbedUser._id, - this.newEmail, - err => { - expect(err).to.exist - err.message.should.exist - done() - } - ) + let error + + try { + await this.InstitutionsAPI.promises.removeAffiliation( + this.stubbedUser._id, + this.newEmail + ) + } catch (err) { + error = err + } + + expect(error).to.exist + expect(error).to.have.property('message') }) }) describe('deleteAffiliations', function () { - it('delete affiliations', function (done) { + it('delete affiliations', async function () { this.request.callsArgWith(1, null, { statusCode: 200 }) - this.InstitutionsAPI.deleteAffiliations(this.stubbedUser._id, err => { - expect(err).not.to.exist - this.request.calledOnce.should.equal(true) - const requestOptions = this.request.lastCall.args[0] - const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations` - requestOptions.url.should.equal(expectedUrl) - requestOptions.method.should.equal('DELETE') - done() - }) + await this.InstitutionsAPI.promises.deleteAffiliations( + this.stubbedUser._id + ) + this.request.calledOnce.should.equal(true) + const requestOptions = this.request.lastCall.args[0] + const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations` + requestOptions.url.should.equal(expectedUrl) + requestOptions.method.should.equal('DELETE') }) - it('handle error', function (done) { + it('handle error', async function () { const body = { errors: 'affiliation error message' } this.request.callsArgWith(1, null, { statusCode: 518 }, body) - this.InstitutionsAPI.deleteAffiliations(this.stubbedUser._id, err => { - expect(err).to.be.instanceof(Errors.V1ConnectionError) - done() - }) + let error + + try { + await this.InstitutionsAPI.promises.deleteAffiliations( + this.stubbedUser._id + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.V1ConnectionError) }) }) @@ -363,61 +364,57 @@ describe('InstitutionsAPI', function () { this.request.callsArgWith(1, null, { statusCode: 204 }) }) - it('endorse affiliation', function (done) { - this.InstitutionsAPI.endorseAffiliation( + it('endorse affiliation', async function () { + await this.InstitutionsAPI.promises.endorseAffiliation( this.stubbedUser._id, this.newEmail, 'Student', - 'Physics', - err => { - expect(err).not.to.exist - this.request.calledOnce.should.equal(true) - const requestOptions = this.request.lastCall.args[0] - const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations/endorse` - requestOptions.url.should.equal(expectedUrl) - requestOptions.method.should.equal('POST') - - const { body } = requestOptions - Object.keys(body).length.should.equal(3) - body.email.should.equal(this.newEmail) - body.role.should.equal('Student') - body.department.should.equal('Physics') - done() - } + 'Physics' ) + this.request.calledOnce.should.equal(true) + const requestOptions = this.request.lastCall.args[0] + const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations/endorse` + requestOptions.url.should.equal(expectedUrl) + requestOptions.method.should.equal('POST') + + const { body } = requestOptions + Object.keys(body).length.should.equal(3) + body.email.should.equal(this.newEmail) + body.role.should.equal('Student') + body.department.should.equal('Physics') }) }) describe('sendUsersWithReconfirmationsLapsedProcessed', function () { const users = ['abc123', 'def456'] - it('sends the list of users', function (done) { + it('sends the list of users', async function () { this.request.callsArgWith(1, null, { statusCode: 200 }) - this.InstitutionsAPI.sendUsersWithReconfirmationsLapsedProcessed( - users, - error => { - expect(error).not.to.exist - this.request.calledOnce.should.equal(true) - const requestOptions = this.request.lastCall.args[0] - const expectedUrl = - 'v1.url/api/v2/institutions/reconfirmation_lapsed_processed' - requestOptions.url.should.equal(expectedUrl) - requestOptions.method.should.equal('POST') - expect(requestOptions.body).to.deep.equal({ users }) - done() - } + await this.InstitutionsAPI.promises.sendUsersWithReconfirmationsLapsedProcessed( + users ) + this.request.calledOnce.should.equal(true) + const requestOptions = this.request.lastCall.args[0] + const expectedUrl = + 'v1.url/api/v2/institutions/reconfirmation_lapsed_processed' + requestOptions.url.should.equal(expectedUrl) + requestOptions.method.should.equal('POST') + expect(requestOptions.body).to.deep.equal({ users }) }) - it('handle error', function (done) { + it('handle error', async function () { this.request.callsArgWith(1, null, { statusCode: 500 }) - this.InstitutionsAPI.sendUsersWithReconfirmationsLapsedProcessed( - users, - error => { - expect(error).to.exist - done() - } - ) + let error + + try { + await this.InstitutionsAPI.promises.sendUsersWithReconfirmationsLapsedProcessed( + users + ) + } catch (err) { + error = err + } + + expect(error).to.exist }) }) }) diff --git a/services/web/test/unit/src/Institutions/InstitutionsFeaturesTests.js b/services/web/test/unit/src/Institutions/InstitutionsFeaturesTests.js index 69def3076f..6f4a1e9715 100644 --- a/services/web/test/unit/src/Institutions/InstitutionsFeaturesTests.js +++ b/services/web/test/unit/src/Institutions/InstitutionsFeaturesTests.js @@ -1,17 +1,4 @@ -/* eslint-disable - max-len, - no-return-assign, - no-unused-vars, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const SandboxedModule = require('sandboxed-module') -const assert = require('assert') const { expect } = require('chai') const sinon = require('sinon') const modulePath = require('path').join( @@ -41,45 +28,39 @@ describe('InstitutionsFeatures', function () { }) describe('hasLicence', function () { - it('should handle error', function (done) { + it('should handle error', async function () { this.UserGetter.promises.getUserFullEmails.rejects(new Error('Nope')) - return this.InstitutionsFeatures.hasLicence( - this.userId, - (error, hasLicence) => { - expect(error).to.exist - return done() - } - ) + let error + + try { + await this.InstitutionsFeatures.promises.hasLicence(this.userId) + } catch (err) { + error = err + } + + expect(error).to.exist }) - it('should return false if user has no paid affiliations', function (done) { + it('should return false if user has no paid affiliations', async function () { this.UserGetter.promises.getUserFullEmails.resolves( this.emailDataWithoutLicense ) - return this.InstitutionsFeatures.hasLicence( - this.userId, - (error, hasLicence) => { - expect(error).to.not.exist - expect(hasLicence).to.be.false - return done() - } + const hasLicence = await this.InstitutionsFeatures.promises.hasLicence( + this.userId ) + expect(hasLicence).to.be.false }) - it('should return true if user has confirmed paid affiliation', function (done) { + it('should return true if user has confirmed paid affiliation', async function () { const emailData = [ { emailHasInstitutionLicence: true }, { emailHasInstitutionLicence: false }, ] this.UserGetter.promises.getUserFullEmails.resolves(emailData) - return this.InstitutionsFeatures.hasLicence( - this.userId, - (error, hasLicence) => { - expect(error).to.not.exist - expect(hasLicence).to.be.true - return done() - } + const hasLicence = await this.InstitutionsFeatures.promises.hasLicence( + this.userId ) + expect(hasLicence).to.be.true }) }) @@ -91,84 +72,62 @@ describe('InstitutionsFeatures', function () { .returns(this.testFeatures) }) - it('should handle error', function (done) { + it('should handle error', async function () { this.UserGetter.promises.getUserFullEmails.rejects(new Error('Nope')) - return this.InstitutionsFeatures.getInstitutionsFeatures( - this.userId, - (error, features) => { - expect(error).to.exist - return done() - } - ) + await expect( + this.InstitutionsFeatures.promises.getInstitutionsFeatures(this.userId) + ).to.be.rejected }) - it('should return no feaures if user has no plan code', function (done) { + it('should return no feaures if user has no plan code', async function () { this.UserGetter.promises.getUserFullEmails.resolves( this.emailDataWithoutLicense ) - return this.InstitutionsFeatures.getInstitutionsFeatures( - this.userId, - (error, features) => { - expect(error).to.not.exist - expect(features).to.deep.equal({}) - return done() - } - ) + const features = + await this.InstitutionsFeatures.promises.getInstitutionsFeatures( + this.userId + ) + expect(features).to.deep.equal({}) }) - it('should return feaures if user has affiliations plan code', function (done) { + it('should return feaures if user has affiliations plan code', async function () { this.UserGetter.promises.getUserFullEmails.resolves( this.emailDataWithLicense ) - return this.InstitutionsFeatures.getInstitutionsFeatures( - this.userId, - (error, features) => { - expect(error).to.not.exist - expect(features).to.deep.equal(this.testFeatures.features) - return done() - } - ) + const features = + await this.InstitutionsFeatures.promises.getInstitutionsFeatures( + this.userId + ) + expect(features).to.deep.equal(this.testFeatures.features) }) }) describe('getInstitutionsPlan', function () { - it('should handle error', function (done) { + it('should handle error', async function () { this.UserGetter.promises.getUserFullEmails.rejects(new Error('Nope')) - return this.InstitutionsFeatures.getInstitutionsPlan( - this.userId, - error => { - expect(error).to.exist - return done() - } - ) + await expect( + this.InstitutionsFeatures.promises.getInstitutionsPlan(this.userId) + ).to.be.rejected }) - it('should return no plan if user has no licence', function (done) { + it('should return no plan if user has no licence', async function () { this.UserGetter.promises.getUserFullEmails.resolves( this.emailDataWithoutLicense ) - return this.InstitutionsFeatures.getInstitutionsPlan( - this.userId, - (error, plan) => { - expect(error).to.not.exist - expect(plan).to.equal(null) - return done() - } + const plan = await this.InstitutionsFeatures.promises.getInstitutionsPlan( + this.userId ) + expect(plan).to.equal(null) }) - it('should return plan if user has licence', function (done) { + it('should return plan if user has licence', async function () { this.UserGetter.promises.getUserFullEmails.resolves( this.emailDataWithLicense ) - return this.InstitutionsFeatures.getInstitutionsPlan( - this.userId, - (error, plan) => { - expect(error).to.not.exist - expect(plan).to.equal(this.institutionPlanCode) - return done() - } + const plan = await this.InstitutionsFeatures.promises.getInstitutionsPlan( + this.userId ) + expect(plan).to.equal(this.institutionPlanCode) }) }) }) diff --git a/services/web/test/unit/src/Notifications/NotificationsBuilderTests.js b/services/web/test/unit/src/Notifications/NotificationsBuilderTests.js index 234cf7547e..b668fffd2a 100644 --- a/services/web/test/unit/src/Notifications/NotificationsBuilderTests.js +++ b/services/web/test/unit/src/Notifications/NotificationsBuilderTests.js @@ -22,22 +22,19 @@ describe('NotificationsBuilder', function () { }) }) - describe('dropboxUnlinkedDueToLapsedReconfirmation', function (done) { - it('should create the notification', function (done) { - this.controller + describe('dropboxUnlinkedDueToLapsedReconfirmation', function () { + it('should create the notification', async function () { + await this.controller.promises .dropboxUnlinkedDueToLapsedReconfirmation(userId) - .create(error => { - expect(error).to.not.exist - expect(this.handler.createNotification).to.have.been.calledWith( - userId, - 'drobox-unlinked-due-to-lapsed-reconfirmation', - 'notification_dropbox_unlinked_due_to_lapsed_reconfirmation', - {}, - null, - true - ) - done() - }) + .create() + expect(this.handler.createNotification).to.have.been.calledWith( + userId, + 'drobox-unlinked-due-to-lapsed-reconfirmation', + 'notification_dropbox_unlinked_due_to_lapsed_reconfirmation', + {}, + null, + true + ) }) describe('NotificationsHandler error', function () { let anError @@ -45,19 +42,23 @@ describe('NotificationsBuilder', function () { anError = new Error('oops') this.handler.createNotification.yields(anError) }) - it('should return errors from NotificationsHandler', function (done) { - this.controller - .dropboxUnlinkedDueToLapsedReconfirmation(userId) - .create(error => { - expect(error).to.exist - expect(error).to.deep.equal(anError) - done() - }) + it('should return errors from NotificationsHandler', async function () { + let error + + try { + await this.controller.promises + .dropboxUnlinkedDueToLapsedReconfirmation(userId) + .create() + } catch (err) { + error = err + } + + expect(error).to.equal(anError) }) }) }) - describe('groupInvitation', function (done) { + describe('groupInvitation', function () { const subscriptionId = '123123bcabca' beforeEach(function () { this.invite = { @@ -67,29 +68,26 @@ describe('NotificationsBuilder', function () { } }) - it('should create the notification', function (done) { - this.controller + it('should create the notification', async function () { + await this.controller.promises .groupInvitation( userId, subscriptionId, this.invite.managedUsersEnabled ) - .create(this.invite, error => { - expect(error).to.not.exist - expect(this.handler.createNotification).to.have.been.calledWith( - userId, - `groupInvitation-${subscriptionId}-${userId}`, - 'notification_group_invitation', - { - token: this.invite.token, - inviterName: this.invite.inviterName, - managedUsersEnabled: this.invite.managedUsersEnabled, - }, - null, - true - ) - done() - }) + .create(this.invite) + expect(this.handler.createNotification).to.have.been.calledWith( + userId, + `groupInvitation-${subscriptionId}-${userId}`, + 'notification_group_invitation', + { + token: this.invite.token, + inviterName: this.invite.inviterName, + managedUsersEnabled: this.invite.managedUsersEnabled, + }, + null, + true + ) }) }) @@ -107,27 +105,25 @@ describe('NotificationsBuilder', function () { this.request.callsArgWith(1, null, { statusCode: 200 }, this.body) }) - it('should call v1 and create affiliation notifications', function (done) { + it('should call v1 and create affiliation notifications', async function () { const ip = '192.168.0.1' - this.controller.ipMatcherAffiliation(userId).create(ip, callback => { - this.request.calledOnce.should.equal(true) - const expectedOpts = { - institutionId: this.body.id, - university_name: this.body.name, - content: this.body.enrolment_ad_html, - ssoEnabled: false, - portalPath: undefined, - } - this.handler.createNotification - .calledWith( - userId, - `ip-matched-affiliation-${this.body.id}`, - 'notification_ip_matched_affiliation', - expectedOpts - ) - .should.equal(true) - done() - }) + await this.controller.promises.ipMatcherAffiliation(userId).create(ip) + this.request.calledOnce.should.equal(true) + const expectedOpts = { + institutionId: this.body.id, + university_name: this.body.name, + content: this.body.enrolment_ad_html, + ssoEnabled: false, + portalPath: undefined, + } + this.handler.createNotification + .calledWith( + userId, + `ip-matched-affiliation-${this.body.id}`, + 'notification_ip_matched_affiliation', + expectedOpts + ) + .should.equal(true) }) }) describe('without portal and without SSO', function () { @@ -143,27 +139,25 @@ describe('NotificationsBuilder', function () { this.request.callsArgWith(1, null, { statusCode: 200 }, this.body) }) - it('should call v1 and create affiliation notifications', function (done) { + it('should call v1 and create affiliation notifications', async function () { const ip = '192.168.0.1' - this.controller.ipMatcherAffiliation(userId).create(ip, callback => { - this.request.calledOnce.should.equal(true) - const expectedOpts = { - institutionId: this.body.id, - university_name: this.body.name, - content: this.body.enrolment_ad_html, - ssoEnabled: true, - portalPath: '/edu/stanford', - } - this.handler.createNotification - .calledWith( - userId, - `ip-matched-affiliation-${this.body.id}`, - 'notification_ip_matched_affiliation', - expectedOpts - ) - .should.equal(true) - done() - }) + await this.controller.promises.ipMatcherAffiliation(userId).create(ip) + this.request.calledOnce.should.equal(true) + const expectedOpts = { + institutionId: this.body.id, + university_name: this.body.name, + content: this.body.enrolment_ad_html, + ssoEnabled: true, + portalPath: '/edu/stanford', + } + this.handler.createNotification + .calledWith( + userId, + `ip-matched-affiliation-${this.body.id}`, + 'notification_ip_matched_affiliation', + expectedOpts + ) + .should.equal(true) }) }) }) diff --git a/services/web/test/unit/src/Notifications/NotificationsHandlerTests.js b/services/web/test/unit/src/Notifications/NotificationsHandlerTests.js index 5eb431f522..0ed0001d65 100644 --- a/services/web/test/unit/src/Notifications/NotificationsHandlerTests.js +++ b/services/web/test/unit/src/Notifications/NotificationsHandlerTests.js @@ -1,16 +1,3 @@ -/* eslint-disable - n/handle-callback-err, - max-len, - no-return-assign, - no-unused-vars, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const SandboxedModule = require('sandboxed-module') const { assert } = require('chai') const sinon = require('sinon') @@ -18,7 +5,6 @@ const modulePath = require('path').join( __dirname, '../../../../app/src/Features/Notifications/NotificationsHandler.js' ) -const _ = require('lodash') describe('NotificationsHandler', function () { const userId = '123nd3ijdks' @@ -27,18 +13,18 @@ describe('NotificationsHandler', function () { beforeEach(function () { this.request = sinon.stub().callsArgWith(1) - return (this.handler = SandboxedModule.require(modulePath, { + this.handler = SandboxedModule.require(modulePath, { requires: { '@overleaf/settings': { apis: { notifications: { url: notificationUrl } }, }, request: this.request, }, - })) + }) }) describe('getUserNotifications', function () { - it('should get unread notifications', function (done) { + it('should get unread notifications', async function () { const stubbedNotifications = [{ _id: notificationId, user_id: userId }] this.request.callsArgWith( 1, @@ -46,51 +32,42 @@ describe('NotificationsHandler', function () { { statusCode: 200 }, stubbedNotifications ) - return this.handler.getUserNotifications( - userId, - (err, unreadNotifications) => { - stubbedNotifications.should.deep.equal(unreadNotifications) - const getOpts = { - uri: `${notificationUrl}/user/${userId}`, - json: true, - timeout: 1000, - method: 'GET', - } - this.request.calledWith(getOpts).should.equal(true) - return done() - } - ) + const unreadNotifications = + await this.handler.promises.getUserNotifications(userId) + stubbedNotifications.should.deep.equal(unreadNotifications) + const getOpts = { + uri: `${notificationUrl}/user/${userId}`, + json: true, + timeout: 1000, + method: 'GET', + } + this.request.calledWith(getOpts).should.equal(true) }) - it('should return empty arrays if there are no notifications', function () { + it('should return empty arrays if there are no notifications', async function () { this.request.callsArgWith(1, null, { statusCode: 200 }, null) - return this.handler.getUserNotifications( - userId, - (err, unreadNotifications) => { - return unreadNotifications.length.should.equal(0) - } - ) + const unreadNotifications = + await this.handler.promises.getUserNotifications(userId) + unreadNotifications.length.should.equal(0) }) }) describe('markAsRead', function () { beforeEach(function () { - return (this.key = 'some key here') + this.key = 'some key here' }) - it('should send a delete request when a delete has been received to mark a notification', function (done) { - return this.handler.markAsReadWithKey(userId, this.key, () => { - const opts = { - uri: `${notificationUrl}/user/${userId}`, - json: { - key: this.key, - }, - timeout: 1000, - method: 'DELETE', - } - this.request.calledWith(opts).should.equal(true) - return done() - }) + it('should send a delete request when a delete has been received to mark a notification', async function () { + await this.handler.promises.markAsReadWithKey(userId, this.key) + const opts = { + uri: `${notificationUrl}/user/${userId}`, + json: { + key: this.key, + }, + timeout: 1000, + method: 'DELETE', + } + this.request.calledWith(opts).should.equal(true) }) }) @@ -99,30 +76,27 @@ describe('NotificationsHandler', function () { this.key = 'some key here' this.messageOpts = { value: 12344 } this.templateKey = 'renderThisHtml' - return (this.expiry = null) + this.expiry = null }) - it('should post the message over', function (done) { - return this.handler.createNotification( + it('should post the message over', async function () { + await this.handler.promises.createNotification( userId, this.key, this.templateKey, this.messageOpts, - this.expiry, - () => { - const args = this.request.args[0][0] - args.uri.should.equal(`${notificationUrl}/user/${userId}`) - args.timeout.should.equal(1000) - const expectedJson = { - key: this.key, - templateKey: this.templateKey, - messageOpts: this.messageOpts, - forceCreate: true, - } - assert.deepEqual(args.json, expectedJson) - return done() - } + this.expiry ) + const args = this.request.args[0][0] + args.uri.should.equal(`${notificationUrl}/user/${userId}`) + args.timeout.should.equal(1000) + const expectedJson = { + key: this.key, + templateKey: this.templateKey, + messageOpts: this.messageOpts, + forceCreate: true, + } + assert.deepEqual(args.json, expectedJson) }) describe('when expiry date is supplied', function () { @@ -130,50 +104,46 @@ describe('NotificationsHandler', function () { this.key = 'some key here' this.messageOpts = { value: 12344 } this.templateKey = 'renderThisHtml' - return (this.expiry = new Date()) + this.expiry = new Date() }) - it('should post the message over with expiry field', function (done) { - return this.handler.createNotification( + it('should post the message over with expiry field', async function () { + await this.handler.promises.createNotification( userId, this.key, this.templateKey, this.messageOpts, - this.expiry, - () => { - const args = this.request.args[0][0] - args.uri.should.equal(`${notificationUrl}/user/${userId}`) - args.timeout.should.equal(1000) - const expectedJson = { - key: this.key, - templateKey: this.templateKey, - messageOpts: this.messageOpts, - expires: this.expiry, - forceCreate: true, - } - assert.deepEqual(args.json, expectedJson) - return done() - } + this.expiry ) + + const args = this.request.args[0][0] + args.uri.should.equal(`${notificationUrl}/user/${userId}`) + args.timeout.should.equal(1000) + const expectedJson = { + key: this.key, + templateKey: this.templateKey, + messageOpts: this.messageOpts, + expires: this.expiry, + forceCreate: true, + } + assert.deepEqual(args.json, expectedJson) }) }) }) describe('markAsReadByKeyOnly', function () { beforeEach(function () { - return (this.key = 'some key here') + this.key = 'some key here' }) - it('should send a delete request when a delete has been received to mark a notification', function (done) { - return this.handler.markAsReadByKeyOnly(this.key, () => { - const opts = { - uri: `${notificationUrl}/key/${this.key}`, - timeout: 1000, - method: 'DELETE', - } - this.request.calledWith(opts).should.equal(true) - return done() - }) + it('should send a delete request when a delete has been received to mark a notification', async function () { + await this.handler.promises.markAsReadByKeyOnly(this.key) + const opts = { + uri: `${notificationUrl}/key/${this.key}`, + timeout: 1000, + method: 'DELETE', + } + this.request.calledWith(opts).should.equal(true) }) }) }) diff --git a/services/web/test/unit/src/Project/ProjectEntityHandlerTests.js b/services/web/test/unit/src/Project/ProjectEntityHandlerTests.js index f3cc6e0c06..8221ae6c7e 100644 --- a/services/web/test/unit/src/Project/ProjectEntityHandlerTests.js +++ b/services/web/test/unit/src/Project/ProjectEntityHandlerTests.js @@ -430,14 +430,15 @@ describe('ProjectEntityHandler', function () { }) }) - it('should call the callback with the lines, version and rev', function (done) { - this.ProjectEntityHandler.getDoc(projectId, docId, doc => { - this.DocstoreManager.promises.getDoc - .calledWith(projectId, docId) - .should.equal(true) - expect(doc).to.exist - done() - }) + it('should call the callback with the lines, version and rev', async function () { + const doc = await this.ProjectEntityHandler.promises.getDoc( + projectId, + docId + ) + this.DocstoreManager.promises.getDoc + .calledWith(projectId, docId) + .should.equal(true) + expect(doc).to.exist }) }) diff --git a/services/web/test/unit/src/Project/ProjectEntityUpdateHandlerTests.js b/services/web/test/unit/src/Project/ProjectEntityUpdateHandlerTests.js index e8ed371c91..6cfe01e206 100644 --- a/services/web/test/unit/src/Project/ProjectEntityUpdateHandlerTests.js +++ b/services/web/test/unit/src/Project/ProjectEntityUpdateHandlerTests.js @@ -100,7 +100,7 @@ describe('ProjectEntityUpdateHandler', function () { withTimeout: sinon.stub().returns(this.LockManager), } this.ProjectModel = { - updateOne: sinon.stub(), + updateOne: sinon.stub().returns({ exec: sinon.stub().resolves() }), } this.ProjectGetter = { promises: { @@ -225,26 +225,20 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('when the doc has been modified', function () { - beforeEach(function (done) { + beforeEach(async function () { this.DocstoreManager.promises.updateDoc.resolves({ modified: true, rev: (this.rev = 5), }) - const callback = (...args) => { - this.callback(...args) - done() - } - - this.ProjectEntityUpdateHandler.updateDocLines( + await this.ProjectEntityUpdateHandler.promises.updateDocLines( projectId, docId, this.docLines, this.version, this.ranges, this.lastUpdatedAt, - this.lastUpdatedBy, - callback + this.lastUpdatedBy ) }) @@ -288,7 +282,7 @@ describe('ProjectEntityUpdateHandler', function () { ) }) - it('should send the doc the to the TPDS', function () { + it('should send the doc to the TPDS', function () { this.TpdsUpdateSender.promises.addDoc.should.have.been.calledWith({ projectId, projectName: this.project.name, @@ -298,33 +292,23 @@ describe('ProjectEntityUpdateHandler', function () { folderId: this.parentFolder._id, }) }) - - it('should call the callback', function () { - this.callback.called.should.equal(true) - }) }) describe('when the doc has not been modified', function () { - beforeEach(function (done) { + beforeEach(async function () { this.DocstoreManager.promises.updateDoc.resolves({ modified: false, rev: (this.rev = 5), }) - const callback = () => { - this.callback() - done() - } - - this.ProjectEntityUpdateHandler.updateDocLines( + await this.ProjectEntityUpdateHandler.promises.updateDocLines( projectId, docId, this.docLines, this.version, this.ranges, this.lastUpdatedAt, - this.lastUpdatedBy, - callback + this.lastUpdatedBy ) }) @@ -335,33 +319,25 @@ describe('ProjectEntityUpdateHandler', function () { it('should not send the doc the to the TPDS', function () { this.TpdsUpdateSender.promises.addDoc.called.should.equal(false) }) - - it('should call the callback', function () { - this.callback.called.should.equal(true) - }) }) describe('when the doc has been deleted', function () { - beforeEach(function (done) { + beforeEach(async function () { this.ProjectGetter.promises.getProject.resolves(this.project) this.ProjectLocator.promises.findElement.rejects( new Errors.NotFoundError() ) this.DocstoreManager.promises.isDocDeleted.resolves(true) - this.DocstoreManager.promises.updateDoc.resolves() + this.DocstoreManager.promises.updateDoc.resolves({}) - this.ProjectEntityUpdateHandler.updateDocLines( + await this.ProjectEntityUpdateHandler.promises.updateDocLines( projectId, docId, this.docLines, this.version, this.ranges, this.lastUpdatedAt, - this.lastUpdatedBy, - (...args) => { - this.callback(...args) - done() - } + this.lastUpdatedBy ) }) @@ -384,14 +360,10 @@ describe('ProjectEntityUpdateHandler', function () { it('should not send the doc the to the TPDS', function () { this.TpdsUpdateSender.promises.addDoc.called.should.equal(false) }) - - it('should call the callback', function () { - this.callback.called.should.equal(true) - }) }) describe('when projects and docs collection are de-synced', function () { - beforeEach(function (done) { + beforeEach(async function () { this.ProjectGetter.promises.getProject.resolves(this.project) // The doc is not in the file-tree, but also not marked as deleted. @@ -401,21 +373,16 @@ describe('ProjectEntityUpdateHandler', function () { ) this.DocstoreManager.promises.isDocDeleted.resolves(false) - this.DocstoreManager.promises.updateDoc.resolves() - const callback = (...args) => { - this.callback(...args) - done() - } + this.DocstoreManager.promises.updateDoc.resolves({}) - this.ProjectEntityUpdateHandler.updateDocLines( + await this.ProjectEntityUpdateHandler.promises.updateDocLines( projectId, docId, this.docLines, this.version, this.ranges, this.lastUpdatedAt, - this.lastUpdatedBy, - callback + this.lastUpdatedBy ) }) @@ -438,14 +405,11 @@ describe('ProjectEntityUpdateHandler', function () { it('should not send the doc the to the TPDS', function () { this.TpdsUpdateSender.promises.addDoc.called.should.equal(false) }) - - it('should call the callback', function () { - this.callback.called.should.equal(true) - }) }) describe('when the doc is not related to the project', function () { - beforeEach(function (done) { + let updateDocLinesPromise + beforeEach(function () { this.ProjectGetter.promises.getProject.resolves(this.project) this.ProjectLocator.promises.findElement.rejects( new Errors.NotFoundError() @@ -453,65 +417,84 @@ describe('ProjectEntityUpdateHandler', function () { this.DocstoreManager.promises.isDocDeleted.rejects( new Errors.NotFoundError() ) - const callback = (...args) => { - this.callback(...args) - done() + + updateDocLinesPromise = + this.ProjectEntityUpdateHandler.promises.updateDocLines( + projectId, + docId, + this.docLines, + this.version, + this.ranges, + this.lastUpdatedAt, + this.lastUpdatedBy + ) + }) + + it('should return a not found error', async function () { + let error + + try { + await updateDocLinesPromise + } catch (err) { + error = err } - this.ProjectEntityUpdateHandler.updateDocLines( - projectId, - docId, - this.docLines, - this.version, - this.ranges, - this.lastUpdatedAt, - this.lastUpdatedBy, - callback - ) + expect(error).to.be.instanceOf(Errors.NotFoundError) }) - it('should return a not found error', function () { - this.callback - .calledWith(sinon.match.instanceOf(Errors.NotFoundError)) - .should.equal(true) - }) + it('should not update the doc', async function () { + let error - it('should not update the doc', function () { + try { + await updateDocLinesPromise + } catch (err) { + error = err + } + + expect(error).to.exist this.DocstoreManager.promises.updateDoc.called.should.equal(false) }) - it('should not send the doc the to the TPDS', function () { + it('should not send the doc the to the TPDS', async function () { + let error + + try { + await updateDocLinesPromise + } catch (err) { + error = err + } + + expect(error).to.exist this.TpdsUpdateSender.promises.addDoc.called.should.equal(false) }) }) describe('when the project is not found', function () { - beforeEach(function (done) { + let error + beforeEach(async function () { this.ProjectGetter.promises.getProject.rejects( new Errors.NotFoundError() ) - this.ProjectEntityUpdateHandler.updateDocLines( - projectId, - docId, - this.docLines, - this.version, - this.ranges, - this.lastUpdatedAt, - this.lastUpdatedBy, - (...args) => { - this.callback(...args) - done() - } - ) + try { + await this.ProjectEntityUpdateHandler.promises.updateDocLines( + projectId, + docId, + this.docLines, + this.version, + this.ranges, + this.lastUpdatedAt, + this.lastUpdatedBy + ) + } catch (err) { + error = err + } }) - it('should return a not found error', function () { - this.callback - .calledWith(sinon.match.instanceOf(Errors.NotFoundError)) - .should.equal(true) + it('should return a not found error', async function () { + expect(error).to.be.instanceOf(Errors.NotFoundError) }) - it('should not update the doc', function () { + it('should not update the doc', async function () { this.DocstoreManager.promises.updateDoc.called.should.equal(false) }) @@ -526,38 +509,42 @@ describe('ProjectEntityUpdateHandler', function () { this.rootDocId = 'root-doc-id-123123' }) - it('should call Project.updateOne when the doc exists and has a valid extension', function (done) { + it('should call Project.updateOne when the doc exists and has a valid extension', async function () { this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId.resolves( `/main.tex` ) - this.ProjectEntityUpdateHandler.setRootDoc( + await this.ProjectEntityUpdateHandler.promises.setRootDoc( projectId, - this.rootDocId, - () => { - this.ProjectModel.updateOne - .calledWith({ _id: projectId }, { rootDoc_id: this.rootDocId }) - .should.equal(true) - done() - } + this.rootDocId ) + + this.ProjectModel.updateOne + .calledWith({ _id: projectId }, { rootDoc_id: this.rootDocId }) + .should.equal(true) }) - it("should not call Project.updateOne when the doc doesn't exist", function (done) { + it("should not call Project.updateOne when the doc doesn't exist", async function () { this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId.rejects( Errors.NotFoundError ) - this.ProjectEntityUpdateHandler.setRootDoc( - projectId, - this.rootDocId, - () => { - this.ProjectModel.updateOne - .calledWith({ _id: projectId }, { rootDoc_id: this.rootDocId }) - .should.equal(false) - done() - } - ) + let error + + try { + await this.ProjectEntityUpdateHandler.promises.setRootDoc( + projectId, + this.rootDocId + ) + } catch (err) { + error = err + } + + expect(error).to.exist + + this.ProjectModel.updateOne + .calledWith({ _id: projectId }, { rootDoc_id: this.rootDocId }) + .should.equal(false) }) it('should call the callback with an UnsupportedFileTypeError when the doc has an unaccepted file extension', function () { @@ -576,19 +563,17 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('unsetRootDoc', function () { - it('should call Project.updateOne', function (done) { - this.ProjectEntityUpdateHandler.unsetRootDoc(projectId, () => { - this.ProjectModel.updateOne - .calledWith({ _id: projectId }, { $unset: { rootDoc_id: true } }) - .should.equal(true) - done() - }) + it('should call Project.updateOne', async function () { + await this.ProjectEntityUpdateHandler.promises.unsetRootDoc(projectId) + this.ProjectModel.updateOne + .calledWith({ _id: projectId }, { $unset: { rootDoc_id: true } }) + .should.equal(true) }) }) describe('addDoc', function () { describe('adding a doc', function () { - beforeEach(function (done) { + beforeEach(async function () { this.path = '/path/to/doc' this.rev = 5 @@ -607,17 +592,13 @@ describe('ProjectEntityUpdateHandler', function () { result: { path: { fileSystem: this.path } }, project: this.project, }) - this.ProjectEntityUpdateHandler.addDoc( + await this.ProjectEntityUpdateHandler.promises.addDoc( projectId, docId, this.docName, this.docLines, userId, - this.source, - (...args) => { - this.callback(...args) - done() - } + this.source ) }) @@ -650,34 +631,36 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('adding a doc with an invalid name', function () { - beforeEach(function (done) { + beforeEach(function () { this.path = '/path/to/doc' this.newDoc = { _id: docId } - this.ProjectEntityUpdateHandler.addDoc( - projectId, - folderId, - `*${this.docName}`, - this.docLines, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Errors.InvalidNameError) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.addDoc( + projectId, + folderId, + `*${this.docName}`, + this.docLines, + userId, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidNameError) }) }) }) describe('addFile', function () { describe('adding a file', function () { - beforeEach(function (done) { + beforeEach(async function () { this.path = '/path/to/file' this.newFile = { @@ -697,18 +680,14 @@ describe('ProjectEntityUpdateHandler', function () { result: { path: { fileSystem: this.path } }, project: this.project, }) - this.ProjectEntityUpdateHandler.addFile( + await this.ProjectEntityUpdateHandler.promises.addFile( projectId, folderId, this.fileName, this.fileSystemPath, this.linkedFileData, userId, - this.source, - (...args) => { - this.callback(...args) - done() - } + this.source ) }) @@ -770,7 +749,7 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('adding a file with an invalid name', function () { - beforeEach(function (done) { + beforeEach(function () { this.path = '/path/to/file' this.newFile = { @@ -785,54 +764,59 @@ describe('ProjectEntityUpdateHandler', function () { result: { path: { fileSystem: this.path } }, project: this.project, }) - this.ProjectEntityUpdateHandler.addFile( - projectId, - folderId, - `*${this.fileName}`, - this.fileSystemPath, - this.linkedFileData, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Errors.InvalidNameError) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.addFile( + projectId, + folderId, + `*${this.fileName}`, + this.fileSystemPath, + this.linkedFileData, + userId, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidNameError) }) }) }) describe('upsertDoc', function () { describe('upserting into an invalid folder', function () { - beforeEach(function (done) { + beforeEach(function () { this.ProjectLocator.promises.findElement.resolves({ element: null }) - this.ProjectEntityUpdateHandler.upsertDoc( - projectId, - folderId, - this.docName, - this.docLines, - this.source, - userId, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Error) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.upsertDoc( + projectId, + folderId, + this.docName, + this.docLines, + this.source, + userId + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('updating an existing doc', function () { - beforeEach(function (done) { + let upsertDocResponse + beforeEach(async function () { this.existingDoc = { _id: docId, name: this.docName } this.existingFile = { _id: fileId, @@ -849,18 +833,15 @@ describe('ProjectEntityUpdateHandler', function () { }) this.DocumentUpdaterHandler.promises.setDocument.resolves() - this.ProjectEntityUpdateHandler.upsertDoc( - projectId, - folderId, - this.docName, - this.docLines, - this.source, - userId, - (...args) => { - this.callback(...args) - done() - } - ) + upsertDocResponse = + await this.ProjectEntityUpdateHandler.promises.upsertDoc( + projectId, + folderId, + this.docName, + this.docLines, + this.source, + userId + ) }) it('tries to find the folder', function () { @@ -886,12 +867,15 @@ describe('ProjectEntityUpdateHandler', function () { }) it('returns the doc', function () { - this.callback.calledWith(null, this.existingDoc, false) + expect(upsertDocResponse.isNew).to.equal(false) + expect(upsertDocResponse.doc).to.eql(this.existingDoc) }) }) describe('creating a new doc', function () { - beforeEach(function (done) { + let upsertDocResponse + + beforeEach(async function () { this.folder = { _id: folderId, docs: [], fileRefs: [] } this.newDoc = { _id: docId } this.ProjectLocator.promises.findElement.resolves({ @@ -901,18 +885,15 @@ describe('ProjectEntityUpdateHandler', function () { withoutLock: sinon.stub().resolves({ doc: this.newDoc }), } - this.ProjectEntityUpdateHandler.upsertDoc( - projectId, - folderId, - this.docName, - this.docLines, - this.source, - userId, - (...args) => { - this.callback(...args) - done() - } - ) + upsertDocResponse = + await this.ProjectEntityUpdateHandler.promises.upsertDoc( + projectId, + folderId, + this.docName, + this.docLines, + this.source, + userId + ) }) it('tries to find the folder', function () { @@ -938,12 +919,13 @@ describe('ProjectEntityUpdateHandler', function () { }) it('returns the doc', function () { - this.callback.calledWith(null, this.newDoc, true) + expect(upsertDocResponse.isNew).to.equal(true) + expect(upsertDocResponse.doc).to.equal(this.newDoc) }) }) describe('upserting a new doc with an invalid name', function () { - beforeEach(function (done) { + beforeEach(function () { this.folder = { _id: folderId, docs: [], fileRefs: [] } this.newDoc = { _id: docId } this.ProjectLocator.promises.findElement.resolves({ @@ -952,29 +934,30 @@ describe('ProjectEntityUpdateHandler', function () { this.ProjectEntityUpdateHandler.promises.addDocWithRanges = { withoutLock: sinon.stub().resolves({ doc: this.newDoc }), } - - this.ProjectEntityUpdateHandler.upsertDoc( - projectId, - folderId, - `*${this.docName}`, - this.docLines, - this.source, - userId, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Errors.InvalidNameError) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.upsertDoc( + projectId, + folderId, + `*${this.docName}`, + this.docLines, + this.source, + userId + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidNameError) }) }) describe('upserting a doc on top of a file', function () { - beforeEach(function (done) { + beforeEach(async function () { this.newProject = { name: 'new project', overleaf: { history: { id: projectHistoryId } }, @@ -1003,17 +986,13 @@ describe('ProjectEntityUpdateHandler', function () { ) this.TpdsUpdateSender.promises.addDoc.resolves() - this.ProjectEntityUpdateHandler.upsertDoc( + await this.ProjectEntityUpdateHandler.promises.upsertDoc( projectId, folderId, 'foo.tex', this.docLines, this.source, - userId, - (...args) => { - this.callback(...args) - done() - } + userId ) }) @@ -1098,31 +1077,34 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('upserting into an invalid folder', function () { - beforeEach(function (done) { + beforeEach(function () { this.ProjectLocator.promises.findElement.resolves({ element: null }) - this.ProjectEntityUpdateHandler.upsertFile( - projectId, - folderId, - this.fileName, - this.fileSystemPath, - this.linkedFileData, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Error) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.upsertFile( + projectId, + folderId, + this.fileName, + this.fileSystemPath, + this.linkedFileData, + userId, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) describe('updating an existing file', function () { - beforeEach(function (done) { + let upsertFileResult + beforeEach(async function () { this.existingFile = { _id: fileId, name: this.fileName, rev: 1 } this.newFile = { _id: new ObjectId(), @@ -1144,19 +1126,16 @@ describe('ProjectEntityUpdateHandler', function () { newFileRef: this.newFile, } ) - this.ProjectEntityUpdateHandler.upsertFile( - projectId, - folderId, - this.fileName, - this.fileSystemPath, - this.linkedFileData, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) + upsertFileResult = + await this.ProjectEntityUpdateHandler.promises.upsertFile( + projectId, + folderId, + this.fileName, + this.fileSystemPath, + this.linkedFileData, + userId, + this.source + ) }) it('uploads a new version of the file', function () { @@ -1221,12 +1200,16 @@ describe('ProjectEntityUpdateHandler', function () { }) it('returns the file', function () { - this.callback.calledWith(null, this.existingFile, false) + expect(upsertFileResult.isNew).to.be.false + expect(upsertFileResult.fileRef.toString()).to.eql( + this.existingFile.toString() + ) }) }) describe('creating a new file', function () { - beforeEach(function (done) { + let upsertFileResult + beforeEach(async function () { this.folder = { _id: folderId, fileRefs: [], docs: [] } this.newFile = { _id: fileId, @@ -1244,19 +1227,16 @@ describe('ProjectEntityUpdateHandler', function () { mainTask: sinon.stub().resolves(this.newFile), } - this.ProjectEntityUpdateHandler.upsertFile( - projectId, - folderId, - this.fileName, - this.fileSystemPath, - this.linkedFileData, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) + upsertFileResult = + await this.ProjectEntityUpdateHandler.promises.upsertFile( + projectId, + folderId, + this.fileName, + this.fileSystemPath, + this.linkedFileData, + userId, + this.source + ) }) it('tries to find the folder', function () { @@ -1282,12 +1262,13 @@ describe('ProjectEntityUpdateHandler', function () { }) it('returns the file', function () { - this.callback.calledWith(null, this.newFile, true) + expect(upsertFileResult.fileRef).to.eql(this.newFile) + expect(upsertFileResult.isNew).to.be.true }) }) describe('upserting a new file with an invalid name', function () { - beforeEach(function (done) { + beforeEach(function () { this.folder = { _id: folderId, fileRefs: [] } this.newFile = { _id: fileId } this.ProjectLocator.promises.findElement.resolves({ @@ -1296,30 +1277,31 @@ describe('ProjectEntityUpdateHandler', function () { this.ProjectEntityUpdateHandler.promises.addFile = { mainTask: sinon.stub().resolves(this.newFile), } - - this.ProjectEntityUpdateHandler.upsertFile( - projectId, - folderId, - `*${this.fileName}`, - this.fileSystemPath, - this.linkedFileData, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Errors.InvalidNameError) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.upsertFile( + projectId, + folderId, + `*${this.fileName}`, + this.fileSystemPath, + this.linkedFileData, + userId, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidNameError) }) }) describe('upserting file on top of a doc', function () { - beforeEach(function (done) { + beforeEach(async function () { this.path = '/path/to/doc' this.existingDoc = { _id: new ObjectId(), name: this.fileName } this.folder = { @@ -1366,15 +1348,14 @@ describe('ProjectEntityUpdateHandler', function () { this.newProject ) - this.ProjectEntityUpdateHandler.upsertFile( + await this.ProjectEntityUpdateHandler.promises.upsertFile( projectId, folderId, this.fileName, this.fileSystemPath, this.linkedFileData, userId, - this.source, - done + this.source ) }) @@ -1435,7 +1416,8 @@ describe('ProjectEntityUpdateHandler', function () { describe('upsertDocWithPath', function () { describe('upserting a doc', function () { - beforeEach(function (done) { + let upsertDocWithPathResult + beforeEach(async function () { this.path = '/folder/doc.tex' this.newFolders = ['mock-a', 'mock-b'] this.folder = { _id: folderId } @@ -1452,17 +1434,14 @@ describe('ProjectEntityUpdateHandler', function () { .resolves({ doc: this.doc, isNew: this.isNewDoc }), } - this.ProjectEntityUpdateHandler.upsertDocWithPath( - projectId, - this.path, - this.docLines, - this.source, - userId, - (...args) => { - this.callback(...args) - done() - } - ) + upsertDocWithPathResult = + await this.ProjectEntityUpdateHandler.promises.upsertDocWithPath( + projectId, + this.path, + this.docLines, + this.source, + userId + ) }) it('creates any necessary folders', function () { @@ -1484,21 +1463,18 @@ describe('ProjectEntityUpdateHandler', function () { .should.equal(true) }) - it('calls the callback', function () { - this.callback - .calledWith( - null, - this.doc, - this.isNewDoc, - this.newFolders, - this.folder - ) - .should.equal(true) + it('returns a doc, the isNewDoc flag, newFolders and a folder', function () { + expect(upsertDocWithPathResult).to.eql({ + doc: this.doc, + isNew: this.isNewDoc, + newFolders: this.newFolders, + folder: this.folder, + }) }) }) describe('upserting a doc with an invalid path', function () { - beforeEach(function (done) { + beforeEach(function () { this.path = '/*folder/doc.tex' this.newFolders = ['mock-a', 'mock-b'] this.folder = { _id: folderId } @@ -1514,28 +1490,29 @@ describe('ProjectEntityUpdateHandler', function () { .stub() .resolves({ doc: this.doc, isNew: this.isNewDoc }), } - - this.ProjectEntityUpdateHandler.upsertDocWithPath( - projectId, - this.path, - this.docLines, - this.source, - userId, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Errors.InvalidNameError) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.upsertDocWithPath( + projectId, + this.path, + this.docLines, + this.source, + userId + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidNameError) }) }) describe('upserting a doc with an invalid name', function () { - beforeEach(function (done) { + beforeEach(function () { this.path = '/folder/*doc.tex' this.newFolders = ['mock-a', 'mock-b'] this.folder = { _id: folderId } @@ -1551,30 +1528,32 @@ describe('ProjectEntityUpdateHandler', function () { .stub() .resolves({ doc: this.doc, isNew: this.isNewDoc }), } - - this.ProjectEntityUpdateHandler.upsertDocWithPath( - projectId, - this.path, - this.docLines, - this.source, - userId, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Errors.InvalidNameError) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.upsertDocWithPath( + projectId, + this.path, + this.docLines, + this.source, + userId + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidNameError) }) }) }) describe('upsertFileWithPath', function () { describe('upserting a file', function () { - beforeEach(function (done) { + let upsertFileWithPathResult + beforeEach(async function () { this.path = '/folder/file.png' this.newFolders = ['mock-a', 'mock-b'] this.folder = { _id: folderId } @@ -1596,18 +1575,15 @@ describe('ProjectEntityUpdateHandler', function () { .resolves({ fileRef: this.file, isNew: this.isNewFile }), } - this.ProjectEntityUpdateHandler.upsertFileWithPath( - projectId, - this.path, - this.fileSystemPath, - this.linkedFileData, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) + upsertFileWithPathResult = + await this.ProjectEntityUpdateHandler.promises.upsertFileWithPath( + projectId, + this.path, + this.fileSystemPath, + this.linkedFileData, + userId, + this.source + ) }) it('creates any necessary folders', function () { @@ -1633,20 +1609,19 @@ describe('ProjectEntityUpdateHandler', function () { ) }) - it('calls the callback', function () { - this.callback.should.have.been.calledWith( - null, - this.file, - this.isNewFile, - undefined, - this.newFolders, - this.folder - ) + it('returns an object with the fileRef, isNew flag, undefined oldFileRef, newFolders, and folder', function () { + expect(upsertFileWithPathResult).to.eql({ + fileRef: this.file, + isNew: this.isNewFile, + newFolders: this.newFolders, + folder: this.folder, + oldFileRef: undefined, + }) }) }) describe('upserting a file with an invalid path', function () { - beforeEach(function (done) { + beforeEach(function () { this.path = '/*folder/file.png' this.newFolders = ['mock-a', 'mock-b'] this.folder = { _id: folderId } @@ -1662,29 +1637,30 @@ describe('ProjectEntityUpdateHandler', function () { .stub() .resolves({ doc: this.file, isNew: this.isNewFile }), } - - this.ProjectEntityUpdateHandler.upsertFileWithPath( - projectId, - this.path, - this.fileSystemPath, - this.linkedFileData, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Errors.InvalidNameError) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.upsertFileWithPath( + projectId, + this.path, + this.fileSystemPath, + this.linkedFileData, + userId, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidNameError) }) }) describe('upserting a file with an invalid name', function () { - beforeEach(function (done) { + beforeEach(function () { this.path = '/folder/*file.png' this.newFolders = ['mock-a', 'mock-b'] this.folder = { _id: folderId } @@ -1700,30 +1676,32 @@ describe('ProjectEntityUpdateHandler', function () { .stub() .resolves({ doc: this.file, isNew: this.isNewFile }), } - - this.ProjectEntityUpdateHandler.upsertFileWithPath( - projectId, - this.path, - this.fileSystemPath, - this.linkedFileData, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Errors.InvalidNameError) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.upsertFileWithPath( + projectId, + this.path, + this.fileSystemPath, + this.linkedFileData, + userId, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidNameError) }) }) }) describe('deleteEntity', function () { - beforeEach(function (done) { + let deleteEntityResult + beforeEach(async function () { this.path = '/path/to/doc.tex' this.doc = { _id: docId } this.projectBeforeDeletion = { _id: projectId, name: 'project' } @@ -1738,17 +1716,14 @@ describe('ProjectEntityUpdateHandler', function () { .stub() .resolves([{ type: 'doc', entity: this.doc, path: this.path }]) - this.ProjectEntityUpdateHandler.deleteEntity( - projectId, - docId, - 'doc', - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) + deleteEntityResult = + await this.ProjectEntityUpdateHandler.promises.deleteEntity( + projectId, + docId, + 'doc', + userId, + this.source + ) }) it('flushes the project to mongo', function () { @@ -1789,13 +1764,13 @@ describe('ProjectEntityUpdateHandler', function () { }) it('retuns the entity_id', function () { - this.callback.calledWith(null, docId).should.equal(true) + expect(deleteEntityResult).to.equal(docId) }) }) describe('deleteEntityWithPath', function () { describe('when the entity exists', function () { - beforeEach(function (done) { + beforeEach(async function () { this.doc = { _id: docId } this.ProjectLocator.promises.findElementByPath.resolves({ element: this.doc, @@ -1805,12 +1780,11 @@ describe('ProjectEntityUpdateHandler', function () { withoutLock: sinon.stub().resolves(), } this.path = '/path/to/doc.tex' - this.ProjectEntityUpdateHandler.deleteEntityWithPath( + await this.ProjectEntityUpdateHandler.promises.deleteEntityWithPath( projectId, this.path, userId, - this.source, - done + this.source ) }) @@ -1836,40 +1810,40 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('when the entity does not exist', function () { - beforeEach(function (done) { + beforeEach(function () { this.ProjectLocator.promises.findElementByPath.resolves({ element: null, }) this.path = '/doc.tex' - this.ProjectEntityUpdateHandler.deleteEntityWithPath( - projectId, - this.path, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - this.callback.should.have.been.calledWith( - sinon.match.instanceOf(Errors.NotFoundError) - ) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.deleteEntityWithPath( + projectId, + this.path, + userId, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.NotFoundError) }) }) }) describe('mkdirp', function () { - beforeEach(function (done) { + beforeEach(async function () { this.docPath = '/folder/doc.tex' this.ProjectEntityMongoUpdateHandler.promises.mkdirp.resolves({}) - this.ProjectEntityUpdateHandler.mkdirp( + await this.ProjectEntityUpdateHandler.promises.mkdirp( projectId, this.docPath, - userId, - done + userId ) }) @@ -1881,14 +1855,13 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('mkdirpWithExactCase', function () { - beforeEach(function (done) { + beforeEach(async function () { this.docPath = '/folder/doc.tex' this.ProjectEntityMongoUpdateHandler.promises.mkdirp.resolves({}) - this.ProjectEntityUpdateHandler.mkdirpWithExactCase( + await this.ProjectEntityUpdateHandler.promises.mkdirpWithExactCase( projectId, this.docPath, - userId, - done + userId ) }) @@ -1901,16 +1874,15 @@ describe('ProjectEntityUpdateHandler', function () { describe('addFolder', function () { describe('adding a folder', function () { - beforeEach(function (done) { + beforeEach(async function () { this.parentFolderId = '123asdf' this.folderName = 'new-folder' this.ProjectEntityMongoUpdateHandler.promises.addFolder.resolves({}) - this.ProjectEntityUpdateHandler.addFolder( + await this.ProjectEntityUpdateHandler.promises.addFolder( projectId, this.parentFolderId, this.folderName, - userId, - done + userId ) }) @@ -1922,30 +1894,32 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('adding a folder with an invalid name', function () { - beforeEach(function (done) { + beforeEach(function () { this.parentFolderId = '123asdf' this.folderName = '*new-folder' this.ProjectEntityMongoUpdateHandler.promises.addFolder.resolves({}) - this.ProjectEntityUpdateHandler.addFolder( - projectId, - this.parentFolderId, - this.folderName, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Errors.InvalidNameError) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.addFolder( + projectId, + this.parentFolderId, + this.folderName + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidNameError) }) }) }) describe('moveEntity', function () { - beforeEach(function (done) { + beforeEach(async function () { this.project_name = 'project name' this.startPath = '/a.tex' this.endPath = '/folder/b.tex' @@ -1959,14 +1933,13 @@ describe('ProjectEntityUpdateHandler', function () { changes: this.changes, }) - this.ProjectEntityUpdateHandler.moveEntity( + await this.ProjectEntityUpdateHandler.promises.moveEntity( projectId, docId, folderId, 'doc', userId, - this.source, - done + this.source ) }) @@ -2006,7 +1979,7 @@ describe('ProjectEntityUpdateHandler', function () { describe('renameEntity', function () { describe('renaming an entity', function () { - beforeEach(function (done) { + beforeEach(async function () { this.project_name = 'project name' this.startPath = '/folder/a.tex' this.endPath = '/folder/b.tex' @@ -2021,14 +1994,13 @@ describe('ProjectEntityUpdateHandler', function () { changes: this.changes, }) - this.ProjectEntityUpdateHandler.renameEntity( + await this.ProjectEntityUpdateHandler.promises.renameEntity( projectId, docId, 'doc', this.newDocName, userId, - this.source, - done + this.source ) }) @@ -2073,7 +2045,7 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('renaming an entity to an invalid name', function () { - beforeEach(function (done) { + beforeEach(function () { this.project_name = 'project name' this.startPath = '/folder/a.tex' this.endPath = '/folder/b.tex' @@ -2087,29 +2059,30 @@ describe('ProjectEntityUpdateHandler', function () { rev: this.rev, changes: this.changes, }) - - this.ProjectEntityUpdateHandler.renameEntity( - projectId, - docId, - 'doc', - this.newDocName, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Errors.InvalidNameError) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.renameEntity( + projectId, + docId, + 'doc', + this.newDocName, + userId, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.InvalidNameError) }) }) describe('renaming an entity with a non-string value', function () { - beforeEach(function (done) { + beforeEach(function () { this.project_name = 'project name' this.startPath = '/folder/a.tex' this.endPath = '/folder/b.tex' @@ -2123,24 +2096,25 @@ describe('ProjectEntityUpdateHandler', function () { rev: this.rev, changes: this.changes, }) - - this.ProjectEntityUpdateHandler.renameEntity( - projectId, - docId, - 'doc', - this.newDocName, - userId, - this.source, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('returns an error', function () { - const errorMatcher = sinon.match.instanceOf(Error) - this.callback.calledWithMatch(errorMatcher).should.equal(true) + it('returns an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.renameEntity( + projectId, + docId, + 'doc', + this.newDocName, + userId, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) expect( this.ProjectEntityMongoUpdateHandler.promises.renameEntity.called ).to.equal(false) @@ -2150,58 +2124,52 @@ describe('ProjectEntityUpdateHandler', function () { describe('resyncProjectHistory', function () { describe('a deleted project', function () { - beforeEach(function (done) { + beforeEach(function () { this.ProjectGetter.promises.getProject.resolves({}) - - this.ProjectEntityUpdateHandler.resyncProjectHistory( - projectId, - {}, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('should return an error', function () { - expect(this.callback).to.have.been.calledWith( - sinon.match - .instanceOf(Errors.ProjectHistoryDisabledError) - .and( - sinon.match.has( - 'message', - `project history not enabled for ${projectId}` - ) - ) + it('should return an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.resyncProjectHistory( + projectId, + {} + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.ProjectHistoryDisabledError) + expect(error).to.have.property( + 'message', + `project history not enabled for ${projectId}` ) }) }) describe('a project without project-history enabled', function () { - beforeEach(function (done) { + beforeEach(function () { this.project.overleaf = {} this.ProjectGetter.promises.getProject.resolves(this.project) - - this.ProjectEntityUpdateHandler.resyncProjectHistory( - projectId, - {}, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('should return an error', function () { - expect(this.callback).to.have.been.calledWith( - sinon.match - .instanceOf(Errors.ProjectHistoryDisabledError) - .and( - sinon.match.has( - 'message', - `project history not enabled for ${projectId}` - ) - ) + it('should return an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.resyncProjectHistory( + projectId, + {} + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.ProjectHistoryDisabledError) + expect(error).to.have.property( + 'message', + `project history not enabled for ${projectId}` ) }) }) @@ -2214,7 +2182,7 @@ describe('ProjectEntityUpdateHandler', function () { path: 'universe.png', }, ] - beforeEach(function (done) { + beforeEach(async function () { this.ProjectGetter.promises.getProject.resolves(this.project) const folders = [] this.ProjectEntityHandler.getAllEntitiesFromProject.returns({ @@ -2222,13 +2190,10 @@ describe('ProjectEntityUpdateHandler', function () { files, folders, }) - this.ProjectEntityUpdateHandler.resyncProjectHistory( + + await this.ProjectEntityUpdateHandler.promises.resyncProjectHistory( projectId, - {}, - (...args) => { - this.callback(...args) - done() - } + {} ) }) @@ -2253,14 +2218,10 @@ describe('ProjectEntityUpdateHandler', function () { .calledWith(projectId, projectHistoryId, docs, files) .should.equal(true) }) - - it('calls the callback', function () { - this.callback.called.should.equal(true) - }) }) describe('a project with duplicate filenames', function () { - beforeEach(function (done) { + beforeEach(async function () { this.ProjectGetter.promises.getProject.resolves(this.project) this.docs = [ { doc: { _id: 'doc1', name: 'main.tex' }, path: 'main.tex' }, @@ -2304,10 +2265,9 @@ describe('ProjectEntityUpdateHandler', function () { files: this.files, folders: [], }) - this.ProjectEntityUpdateHandler.resyncProjectHistory( + await this.ProjectEntityUpdateHandler.promises.resyncProjectHistory( projectId, - {}, - done + {} ) }) @@ -2371,7 +2331,7 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('a project with bad filenames', function () { - beforeEach(function (done) { + beforeEach(async function () { this.ProjectGetter.promises.getProject.resolves(this.project) this.docs = [ { @@ -2398,10 +2358,9 @@ describe('ProjectEntityUpdateHandler', function () { files: this.files, folders: [], }) - this.ProjectEntityUpdateHandler.resyncProjectHistory( + await this.ProjectEntityUpdateHandler.promises.resyncProjectHistory( projectId, - {}, - done + {} ) }) @@ -2486,17 +2445,16 @@ describe('ProjectEntityUpdateHandler', function () { }, ] const files = [] - beforeEach(function (done) { + beforeEach(async function () { this.ProjectGetter.promises.getProject.resolves(this.project) this.ProjectEntityHandler.getAllEntitiesFromProject.returns({ docs, files, folders, }) - this.ProjectEntityUpdateHandler.resyncProjectHistory( + await this.ProjectEntityUpdateHandler.promises.resyncProjectHistory( projectId, - {}, - done + {} ) }) @@ -2544,17 +2502,16 @@ describe('ProjectEntityUpdateHandler', function () { }, ] const files = [] - beforeEach(function (done) { + beforeEach(async function () { this.ProjectGetter.promises.getProject.resolves(this.project) this.ProjectEntityHandler.getAllEntitiesFromProject.returns({ docs, files, folders, }) - this.ProjectEntityUpdateHandler.resyncProjectHistory( + await this.ProjectEntityUpdateHandler.promises.resyncProjectHistory( projectId, - {}, - done + {} ) }) @@ -2585,24 +2542,25 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('a project with an invalid file tree', function () { - beforeEach(function (done) { + beforeEach(function () { this.callback = sinon.stub() this.ProjectGetter.promises.getProject.resolves(this.project) this.ProjectEntityHandler.getAllEntitiesFromProject.throws() - this.ProjectEntityUpdateHandler.resyncProjectHistory( - projectId, - {}, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('calls the callback with an error', function () { - expect(this.callback).to.have.been.calledWith( - sinon.match.instanceOf(Error) - ) + it('calls the callback with an error', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.resyncProjectHistory( + projectId, + {} + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) }) @@ -2934,13 +2892,12 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('successfully', function () { - beforeEach(function (done) { - this.ProjectEntityUpdateHandler.convertDocToFile( + beforeEach(async function () { + await this.ProjectEntityUpdateHandler.promises.convertDocToFile( this.project._id, this.doc._id, userId, - this.source, - done + this.source ) }) @@ -3026,7 +2983,7 @@ describe('ProjectEntityUpdateHandler', function () { }) describe('when the doc has ranges', function () { - it('should throw a DocHasRangesError', function (done) { + it('should throw a DocHasRangesError', async function () { this.ranges = { comments: [{ id: 123 }] } this.DocstoreManager.promises.getDoc .withArgs(this.project._id, this.doc._id) @@ -3036,16 +2993,20 @@ describe('ProjectEntityUpdateHandler', function () { version: 'version', ranges: this.ranges, }) - this.ProjectEntityUpdateHandler.convertDocToFile( - this.project._id, - this.doc._id, - this.user._id, - this.source, - err => { - expect(err).to.be.instanceof(Errors.DocHasRangesError) - done() - } - ) + let error + + try { + await this.ProjectEntityUpdateHandler.promises.convertDocToFile( + this.project._id, + this.doc._id, + this.user._id, + this.source + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.DocHasRangesError) }) }) }) @@ -3090,7 +3051,7 @@ describe('ProjectEntityUpdateHandler', function () { describe('setMainBibliographyDoc', function () { describe('on success', function () { - beforeEach(function (done) { + beforeEach(async function () { this.doc = { _id: new ObjectId(), name: 'test.bib', @@ -3100,12 +3061,9 @@ describe('ProjectEntityUpdateHandler', function () { .withArgs(this.project._id, this.doc._id) .resolves(this.path) - this.callback = sinon.stub().callsFake(() => done()) - - this.ProjectEntityUpdateHandler.setMainBibliographyDoc( + await this.ProjectEntityUpdateHandler.promises.setMainBibliographyDoc( this.project._id, - this.doc._id, - this.callback + this.doc._id ) }) @@ -3119,7 +3077,8 @@ describe('ProjectEntityUpdateHandler', function () { describe('on failure', function () { describe("when document can't be found", function () { - beforeEach(function (done) { + let setMainBibliographyDocPromise + beforeEach(function () { this.doc = { _id: new ObjectId(), name: 'test.bib', @@ -3127,29 +3086,42 @@ describe('ProjectEntityUpdateHandler', function () { this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId .withArgs(this.project._id, this.doc._id) .rejects(new Error('error')) - - this.callback = sinon.stub().callsFake(() => done()) - - this.ProjectEntityUpdateHandler.setMainBibliographyDoc( - this.project._id, - this.doc._id, - this.callback - ) + setMainBibliographyDocPromise = + this.ProjectEntityUpdateHandler.promises.setMainBibliographyDoc( + this.project._id, + this.doc._id + ) }) - it('should call the callback with an error', function () { - expect(this.callback).to.have.been.calledWith( - sinon.match.instanceOf(Error) - ) + it('should call the callback with an error', async function () { + let error + + try { + await setMainBibliographyDocPromise + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) - it('should not update the project with the new main bibliography doc', function () { + it('should not update the project with the new main bibliography doc', async function () { + let error + + try { + await setMainBibliographyDocPromise + } catch (err) { + error = err + } + + expect(error).to.exist expect(this.ProjectModel.updateOne).to.not.have.been.called }) }) describe("when path is not a bib file can't be found", function () { - beforeEach(function (done) { + let setMainBibliographyDocPromise + beforeEach(function () { this.doc = { _id: new ObjectId(), name: 'test.bib', @@ -3159,23 +3131,35 @@ describe('ProjectEntityUpdateHandler', function () { this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId .withArgs(this.project._id, this.doc._id) .resolves(this.path) - - this.callback = sinon.stub().callsFake(() => done()) - - this.ProjectEntityUpdateHandler.setMainBibliographyDoc( - this.project._id, - this.doc._id, - this.callback - ) + setMainBibliographyDocPromise = + this.ProjectEntityUpdateHandler.promises.setMainBibliographyDoc( + this.project._id, + this.doc._id + ) }) - it('should call the callback with an error', function () { - expect(this.callback).to.have.been.calledWith( - sinon.match.instanceOf(Error) - ) + it('should reject with an error', async function () { + let error + + try { + await setMainBibliographyDocPromise + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) - it('should not update the project with the new main bibliography doc', function () { + it('should not update the project with the new main bibliography doc', async function () { + let error + + try { + await setMainBibliographyDocPromise + } catch (err) { + error = err + } + + expect(error).to.exist expect(this.ProjectModel.updateOne).to.not.have.been.called }) }) @@ -3184,40 +3168,54 @@ describe('ProjectEntityUpdateHandler', function () { describe('appendToDoc', function () { describe('when document cannot be found', function () { - beforeEach(function (done) { + let appendToDocPromise + beforeEach(function () { this.appendedLines = ['5678', 'def'] this.DocumentUpdaterHandler.promises.appendToDocument = sinon.stub() this.ProjectLocator.promises.findElement = sinon.stub() this.ProjectLocator.promises.findElement .withArgs({ project_id: projectId, element_id: docId, type: 'doc' }) .rejects(new Errors.NotFoundError()) - this.ProjectEntityUpdateHandler.appendToDoc( - projectId, - docId, - this.appendedLines, - this.source, - userId, - (...args) => { - this.callback(...args) - done() - } - ) + appendToDocPromise = + this.ProjectEntityUpdateHandler.promises.appendToDocWithPath( + projectId, + docId, + this.appendedLines, + this.source, + userId + ) }) - it('should not talk to DocumentUpdaterHandler', function () { + it('should not talk to DocumentUpdaterHandler', async function () { + let error + + try { + await appendToDocPromise + } catch (err) { + error = err + } + + expect(error).to.exist this.DocumentUpdaterHandler.promises.appendToDocument.should.not.have .been.called }) - it('should throw the error', function () { - this.callback.should.have.been.calledWith( - sinon.match.instanceOf(Errors.NotFoundError) - ) + it('should throw the error', async function () { + let error + + try { + await appendToDocPromise + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.NotFoundError) }) }) describe('when document is found', function () { - beforeEach(function (done) { + let appendToDocResult + beforeEach(async function () { this.appendedLines = ['5678', 'def'] this.DocumentUpdaterHandler.promises.appendToDocument = sinon.stub() this.DocumentUpdaterHandler.promises.appendToDocument @@ -3227,17 +3225,14 @@ describe('ProjectEntityUpdateHandler', function () { this.ProjectLocator.promises.findElement .withArgs({ project_id: projectId, element_id: docId, type: 'doc' }) .resolves({ element: { _id: docId } }) - this.ProjectEntityUpdateHandler.appendToDoc( - projectId, - docId, - this.appendedLines, - this.source, - userId, - (...args) => { - this.callback(...args) - done() - } - ) + appendToDocResult = + await this.ProjectEntityUpdateHandler.promises.appendToDocWithPath( + projectId, + docId, + this.appendedLines, + this.source, + userId + ) }) it('should forward call to DocumentUpdaterHandler.appendToDocument', function () { @@ -3251,12 +3246,12 @@ describe('ProjectEntityUpdateHandler', function () { }) it('should return the response from DocumentUpdaterHandler', function () { - this.callback.should.have.been.calledWith(null, { rev: 1 }) + expect(appendToDocResult).to.eql({ rev: 1 }) }) }) describe('when DocumentUpdater throws an error', function () { - beforeEach(function (done) { + beforeEach(function () { this.appendedLines = ['5678', 'def'] this.DocumentUpdaterHandler.promises.appendToDocument = sinon.stub() this.DocumentUpdaterHandler.promises.appendToDocument.rejects( @@ -3266,21 +3261,24 @@ describe('ProjectEntityUpdateHandler', function () { this.ProjectLocator.promises.findElement .withArgs({ project_id: projectId, element_id: docId, type: 'doc' }) .resolves({ element: { _id: docId } }) - this.ProjectEntityUpdateHandler.appendToDoc( - projectId, - docId, - this.appendedLines, - this.source, - userId, - (...args) => { - this.callback(...args) - done() - } - ) }) - it('should return the response from DocumentUpdaterHandler', function () { - this.callback.should.have.been.calledWith(sinon.match.instanceOf(Error)) + it('should return the response from DocumentUpdaterHandler', async function () { + let error + + try { + await this.ProjectEntityUpdateHandler.promises.appendToDocWithPath( + projectId, + docId, + this.appendedLines, + this.source, + userId + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Error) }) }) }) diff --git a/services/web/test/unit/src/Project/ProjectLocatorTests.js b/services/web/test/unit/src/Project/ProjectLocatorTests.js index 4647dd0a55..14de550401 100644 --- a/services/web/test/unit/src/Project/ProjectLocatorTests.js +++ b/services/web/test/unit/src/Project/ProjectLocatorTests.js @@ -5,7 +5,6 @@ const sinon = require('sinon') const Errors = require('../../../../app/src/Features/Errors/Errors') const project = { _id: '1234566', rootFolder: [] } -class Project {} const rootDoc = { name: 'rootDoc', _id: 'das239djd' } const doc1 = { name: 'otherDoc.txt', _id: 'dsad2ddd' } const doc2 = { name: 'docname.txt', _id: 'dsad2ddddd' } @@ -40,9 +39,6 @@ project.rootDoc_id = rootDoc._id describe('ProjectLocator', function () { beforeEach(function () { - Project.findById = (projectId, callback) => { - callback(null, project) - } this.ProjectGetter = { getProject: sinon.stub().callsArgWith(2, null, project), } @@ -53,7 +49,6 @@ describe('ProjectLocator', function () { } this.locator = SandboxedModule.require(modulePath, { requires: { - '../../models/Project': { Project }, '../../models/User': { User: this.User }, './ProjectGetter': this.ProjectGetter, './ProjectHelper': this.ProjectHelper, @@ -62,170 +57,152 @@ describe('ProjectLocator', function () { }) describe('finding a doc', function () { - it('finds one at the root level', function (done) { - this.locator.findElement( - { project_id: project._id, element_id: doc2._id, type: 'docs' }, - (err, foundElement, path, parentFolder) => { - if (err != null) { - return done(err) - } - foundElement._id.should.equal(doc2._id) - path.fileSystem.should.equal(`/${doc2.name}`) - parentFolder._id.should.equal(project.rootFolder[0]._id) - path.mongo.should.equal('rootFolder.0.docs.1') - done() + it('finds one at the root level', async function () { + const { element, path, folder } = await this.locator.promises.findElement( + { + project_id: project._id, + element_id: doc2._id, + type: 'docs', } ) + element._id.should.equal(doc2._id) + path.fileSystem.should.equal(`/${doc2.name}`) + folder._id.should.equal(project.rootFolder[0]._id) + path.mongo.should.equal('rootFolder.0.docs.1') }) - it('when it is nested', function (done) { - this.locator.findElement( - { project_id: project._id, element_id: subSubDoc._id, type: 'doc' }, - (err, foundElement, path, parentFolder) => { - if (err != null) { - return done(err) - } - expect(foundElement._id).to.equal(subSubDoc._id) - path.fileSystem.should.equal( - `/${subFolder.name}/${secondSubFolder.name}/${subSubDoc.name}` - ) - parentFolder._id.should.equal(secondSubFolder._id) - path.mongo.should.equal('rootFolder.0.folders.1.folders.0.docs.0') - done() + it('when it is nested', async function () { + const { element, path, folder } = await this.locator.promises.findElement( + { + project_id: project._id, + element_id: subSubDoc._id, + type: 'doc', } ) + expect(element._id).to.equal(subSubDoc._id) + path.fileSystem.should.equal( + `/${subFolder.name}/${secondSubFolder.name}/${subSubDoc.name}` + ) + folder._id.should.equal(secondSubFolder._id) + path.mongo.should.equal('rootFolder.0.folders.1.folders.0.docs.0') }) - it('should give error if element could not be found', function (done) { - this.locator.findElement( - { project_id: project._id, element_id: 'ddsd432nj42', type: 'docs' }, - (err, foundElement, path, parentFolder) => { - expect(err).to.be.instanceOf(Errors.NotFoundError) - expect(err).to.have.property('message', 'entity not found') - done() - } + it('should give error if element could not be found', async function () { + await expect( + this.locator.promises.findElement({ + project_id: project._id, + element_id: 'ddsd432nj42', + type: 'docs', + }) ) + .to.eventually.be.rejectedWith(Errors.NotFoundError) + .and.eventually.have.property('message', 'entity not found') }) }) describe('finding a folder', function () { - it('should return root folder when looking for root folder', function (done) { - this.locator.findElement( - { project_id: project._id, element_id: rootFolder._id, type: 'folder' }, - (err, foundElement, path, parentFolder) => { - if (err != null) { - return done(err) - } - foundElement._id.should.equal(rootFolder._id) - done() - } - ) - }) - - it('when at root', function (done) { - this.locator.findElement( - { project_id: project._id, element_id: subFolder._id, type: 'folder' }, - (err, foundElement, path, parentFolder) => { - if (err != null) { - return done(err) - } - foundElement._id.should.equal(subFolder._id) - path.fileSystem.should.equal(`/${subFolder.name}`) - parentFolder._id.should.equal(rootFolder._id) - path.mongo.should.equal('rootFolder.0.folders.1') - done() - } - ) - }) - - it('when deeply nested', function (done) { - this.locator.findElement( + it('should return root folder when looking for root folder', async function () { + const { element: foundElement } = await this.locator.promises.findElement( { project_id: project._id, - element_id: secondSubFolder._id, + element_id: rootFolder._id, type: 'folder', - }, - (err, foundElement, path, parentFolder) => { - if (err != null) { - return done(err) - } - foundElement._id.should.equal(secondSubFolder._id) - path.fileSystem.should.equal( - `/${subFolder.name}/${secondSubFolder.name}` - ) - parentFolder._id.should.equal(subFolder._id) - path.mongo.should.equal('rootFolder.0.folders.1.folders.0') - done() } ) + foundElement._id.should.equal(rootFolder._id) + }) + + it('when at root', async function () { + const { + element: foundElement, + path, + folder: parentFolder, + } = await this.locator.promises.findElement({ + project_id: project._id, + element_id: subFolder._id, + type: 'folder', + }) + foundElement._id.should.equal(subFolder._id) + path.fileSystem.should.equal(`/${subFolder.name}`) + parentFolder._id.should.equal(rootFolder._id) + path.mongo.should.equal('rootFolder.0.folders.1') + }) + + it('when deeply nested', async function () { + const { + element: foundElement, + path, + folder: parentFolder, + } = await this.locator.promises.findElement({ + project_id: project._id, + element_id: secondSubFolder._id, + type: 'folder', + }) + + foundElement._id.should.equal(secondSubFolder._id) + path.fileSystem.should.equal(`/${subFolder.name}/${secondSubFolder.name}`) + parentFolder._id.should.equal(subFolder._id) + path.mongo.should.equal('rootFolder.0.folders.1.folders.0') }) }) describe('finding a file', function () { - it('when at root', function (done) { - this.locator.findElement( - { project_id: project._id, element_id: file1._id, type: 'fileRefs' }, - (err, foundElement, path, parentFolder) => { - if (err != null) { - return done(err) - } - foundElement._id.should.equal(file1._id) - path.fileSystem.should.equal(`/${file1.name}`) - parentFolder._id.should.equal(rootFolder._id) - path.mongo.should.equal('rootFolder.0.fileRefs.0') - done() - } - ) + it('when at root', async function () { + const { + element: foundElement, + path, + folder: parentFolder, + } = await this.locator.promises.findElement({ + project_id: project._id, + element_id: file1._id, + type: 'fileRefs', + }) + foundElement._id.should.equal(file1._id) + path.fileSystem.should.equal(`/${file1.name}`) + parentFolder._id.should.equal(rootFolder._id) + path.mongo.should.equal('rootFolder.0.fileRefs.0') }) - it('when deeply nested', function (done) { - this.locator.findElement( - { - project_id: project._id, - element_id: subSubFile._id, - type: 'fileRefs', - }, - (err, foundElement, path, parentFolder) => { - if (err != null) { - return done(err) - } - foundElement._id.should.equal(subSubFile._id) - path.fileSystem.should.equal( - `/${subFolder.name}/${secondSubFolder.name}/${subSubFile.name}` - ) - parentFolder._id.should.equal(secondSubFolder._id) - path.mongo.should.equal('rootFolder.0.folders.1.folders.0.fileRefs.0') - done() - } + it('when deeply nested', async function () { + const { + element: foundElement, + path, + folder: parentFolder, + } = await this.locator.promises.findElement({ + project_id: project._id, + element_id: subSubFile._id, + type: 'fileRefs', + }) + foundElement._id.should.equal(subSubFile._id) + path.fileSystem.should.equal( + `/${subFolder.name}/${secondSubFolder.name}/${subSubFile.name}` ) + parentFolder._id.should.equal(secondSubFolder._id) + path.mongo.should.equal('rootFolder.0.folders.1.folders.0.fileRefs.0') }) }) describe('finding an element with wrong element type', function () { - it('should add an s onto the element type', function (done) { - this.locator.findElement( - { project_id: project._id, element_id: subSubDoc._id, type: 'doc' }, - (err, foundElement, path, parentFolder) => { - if (err != null) { - return done(err) - } - foundElement._id.should.equal(subSubDoc._id) - done() + it('should add an s onto the element type', async function () { + const { element: foundElement } = await this.locator.promises.findElement( + { + project_id: project._id, + element_id: subSubDoc._id, + type: 'doc', } ) + foundElement._id.should.equal(subSubDoc._id) }) - it('should convert file to fileRefs', function (done) { - this.locator.findElement( - { project_id: project._id, element_id: file1._id, type: 'fileRefs' }, - (err, foundElement, path, parentFolder) => { - if (err != null) { - return done(err) - } - foundElement._id.should.equal(file1._id) - done() + it('should convert file to fileRefs', async function () { + const { element: foundElement } = await this.locator.promises.findElement( + { + project_id: project._id, + element_id: file1._id, + type: 'fileRefs', } ) + foundElement._id.should.equal(file1._id) }) }) @@ -242,233 +219,173 @@ describe('ProjectLocator', function () { _id: '1234566', rootFolder: [rootFolder2], } - it('should find doc in project', function (done) { - this.locator.findElement( - { project: project2, element_id: doc3._id, type: 'docs' }, - (err, foundElement, path, parentFolder) => { - if (err != null) { - return done(err) - } - foundElement._id.should.equal(doc3._id) - path.fileSystem.should.equal(`/${doc3.name}`) - parentFolder._id.should.equal(project2.rootFolder[0]._id) - path.mongo.should.equal('rootFolder.0.docs.0') - done() - } - ) + it('should find doc in project', async function () { + const { + element: foundElement, + path, + folder: parentFolder, + } = await this.locator.promises.findElement({ + project: project2, + element_id: doc3._id, + type: 'docs', + }) + foundElement._id.should.equal(doc3._id) + path.fileSystem.should.equal(`/${doc3.name}`) + parentFolder._id.should.equal(project2.rootFolder[0]._id) + path.mongo.should.equal('rootFolder.0.docs.0') }) }) describe('finding root doc', function () { - it('should return root doc when passed project', function (done) { - this.locator.findRootDoc(project, (err, doc) => { - if (err != null) { - return done(err) - } - doc._id.should.equal(rootDoc._id) - done() - }) + it('should return root doc when passed project', async function () { + const { element: doc } = await this.locator.promises.findRootDoc(project) + doc._id.should.equal(rootDoc._id) }) - it('should return root doc when passed project_id', function (done) { - this.locator.findRootDoc(project._id, (err, doc) => { - if (err != null) { - return done(err) - } - doc._id.should.equal(rootDoc._id) - done() - }) + it('should return root doc when passed project_id', async function () { + const { element: doc } = await this.locator.promises.findRootDoc( + project._id + ) + doc._id.should.equal(rootDoc._id) }) - it('should return null when the project has no rootDoc', function (done) { + it('should return null when the project has no rootDoc', async function () { project.rootDoc_id = null - this.locator.findRootDoc(project, (err, doc) => { - if (err != null) { - return done(err) - } - expect(doc).to.equal(null) - done() - }) + const { element: rootDoc } = + await this.locator.promises.findRootDoc(project) + expect(rootDoc).to.equal(null) }) - it('should return null when the rootDoc_id no longer exists', function (done) { + it('should return null when the rootDoc_id no longer exists', async function () { project.rootDoc_id = 'doesntexist' - this.locator.findRootDoc(project, (err, doc) => { - if (err != null) { - return done(err) - } - expect(doc).to.equal(null) - done() - }) + const { element: rootDoc } = + await this.locator.promises.findRootDoc(project) + expect(rootDoc).to.equal(null) }) }) describe('findElementByPath', function () { - it('should take a doc path and return the element for a root level document', function (done) { + it('should take a doc path and return the element for a root level document', async function () { const path = `${doc1.name}` - this.locator.findElementByPath( - { project, path }, - (err, element, type, folder) => { - if (err != null) { - return done(err) - } - element.should.deep.equal(doc1) - expect(type).to.equal('doc') - expect(folder).to.equal(rootFolder) - done() - } - ) + const { element, type, folder } = + await this.locator.promises.findElementByPath({ + project, + path, + }) + element.should.deep.equal(doc1) + expect(type).to.equal('doc') + expect(folder).to.equal(rootFolder) }) - it('should take a doc path and return the element for a root level document with a starting slash', function (done) { + it('should take a doc path and return the element for a root level document with a starting slash', async function () { const path = `/${doc1.name}` - this.locator.findElementByPath( - { project, path }, - (err, element, type, folder) => { - if (err != null) { - return done(err) - } - element.should.deep.equal(doc1) - expect(type).to.equal('doc') - expect(folder).to.equal(rootFolder) - done() - } - ) + const { element, type, folder } = + await this.locator.promises.findElementByPath({ + project, + path, + }) + element.should.deep.equal(doc1) + expect(type).to.equal('doc') + expect(folder).to.equal(rootFolder) }) - it('should take a doc path and return the element for a nested document', function (done) { + it('should take a doc path and return the element for a nested document', async function () { const path = `${subFolder.name}/${secondSubFolder.name}/${subSubDoc.name}` - this.locator.findElementByPath( - { project, path }, - (err, element, type, folder) => { - if (err != null) { - return done(err) - } - element.should.deep.equal(subSubDoc) - expect(type).to.equal('doc') - expect(folder).to.equal(secondSubFolder) - done() - } - ) + const { element, type, folder } = + await this.locator.promises.findElementByPath({ + project, + path, + }) + element.should.deep.equal(subSubDoc) + expect(type).to.equal('doc') + expect(folder).to.equal(secondSubFolder) }) - it('should take a file path and return the element for a root level document', function (done) { + it('should take a file path and return the element for a root level document', async function () { const path = `${file1.name}` - this.locator.findElementByPath( - { project, path }, - (err, element, type, folder) => { - if (err != null) { - return done(err) - } - element.should.deep.equal(file1) - expect(type).to.equal('file') - expect(folder).to.equal(rootFolder) - done() - } - ) + const { element, type, folder } = + await this.locator.promises.findElementByPath({ + project, + path, + }) + element.should.deep.equal(file1) + expect(type).to.equal('file') + expect(folder).to.equal(rootFolder) }) - it('should take a file path and return the element for a nested document', function (done) { + it('should take a file path and return the element for a nested document', async function () { const path = `${subFolder.name}/${secondSubFolder.name}/${subSubFile.name}` - this.locator.findElementByPath( - { project, path }, - (err, element, type, folder) => { - if (err != null) { - return done(err) - } - element.should.deep.equal(subSubFile) - expect(type).to.equal('file') - expect(folder).to.equal(secondSubFolder) - done() - } - ) + const { element, type, folder } = + await this.locator.promises.findElementByPath({ + project, + path, + }) + element.should.deep.equal(subSubFile) + expect(type).to.equal('file') + expect(folder).to.equal(secondSubFolder) }) - it('should take a file path and return the element for a nested document case insenstive', function (done) { + it('should take a file path and return the element for a nested document case insenstive', async function () { const path = `${subFolder.name.toUpperCase()}/${secondSubFolder.name.toUpperCase()}/${subSubFile.name.toUpperCase()}` - this.locator.findElementByPath( - { project, path }, - (err, element, type, folder) => { - if (err != null) { - return done(err) - } - element.should.deep.equal(subSubFile) - expect(type).to.equal('file') - expect(folder).to.equal(secondSubFolder) - done() - } - ) + const { element, type, folder } = + await this.locator.promises.findElementByPath({ + project, + path, + }) + element.should.deep.equal(subSubFile) + expect(type).to.equal('file') + expect(folder).to.equal(secondSubFolder) }) - it('should not return elements with a case-insensitive match when exactCaseMatch is true', function (done) { + it('should not return elements with a case-insensitive match when exactCaseMatch is true', async function () { const path = `${subFolder.name.toUpperCase()}/${secondSubFolder.name.toUpperCase()}/${subSubFile.name.toUpperCase()}` - this.locator.findElementByPath( - { project, path, exactCaseMatch: true }, - (err, element, type, folder) => { - err.should.not.equal(undefined) - expect(element).to.be.undefined - expect(type).to.be.undefined - done() - } - ) + await expect( + this.locator.promises.findElementByPath({ + project, + path, + exactCaseMatch: true, + }) + ).to.eventually.be.rejected }) - it('should take a file path and return the element for a nested folder', function (done) { + it('should take a file path and return the element for a nested folder', async function () { const path = `${subFolder.name}/${secondSubFolder.name}` - this.locator.findElementByPath( - { project, path }, - (err, element, type, folder) => { - if (err != null) { - return done(err) - } - element.should.deep.equal(secondSubFolder) - expect(type).to.equal('folder') - expect(folder).to.equal(subFolder) - done() - } - ) + const { element, type, folder } = + await this.locator.promises.findElementByPath({ + project, + path, + }) + element.should.deep.equal(secondSubFolder) + expect(type).to.equal('folder') + expect(folder).to.equal(subFolder) }) - it('should take a file path and return the root folder', function (done) { + it('should take a file path and return the root folder', async function () { const path = '/' - this.locator.findElementByPath( - { project, path }, - (err, element, type, folder) => { - if (err != null) { - return done(err) - } - element.should.deep.equal(rootFolder) - expect(type).to.equal('folder') - expect(folder).to.equal(null) - done() - } - ) + const { element, type, folder } = + await this.locator.promises.findElementByPath({ + project, + path, + }) + element.should.deep.equal(rootFolder) + expect(type).to.equal('folder') + expect(folder).to.equal(null) }) - it('should return an error if the file can not be found inside know folder', function (done) { + it('should return an error if the file can not be found inside know folder', async function () { const path = `${subFolder.name}/${secondSubFolder.name}/exist.txt` - this.locator.findElementByPath( - { project, path }, - (err, element, type) => { - err.should.not.equal(undefined) - expect(element).to.be.undefined - expect(type).to.be.undefined - done() - } - ) + await expect(this.locator.promises.findElementByPath({ project, path })) + .to.eventually.be.rejected }) - it('should return an error if the file can not be found inside unknown folder', function (done) { + it('should return an error if the file can not be found inside unknown folder', async function () { const path = 'this/does/not/exist.txt' - this.locator.findElementByPath( - { project, path }, - (err, element, type) => { - err.should.not.equal(undefined) - expect(element).to.be.undefined - expect(type).to.be.undefined - done() - } - ) + await expect( + this.locator.promises.findElementByPath({ + project, + path, + }) + ).to.eventually.be.rejected }) describe('where duplicate folder exists', function () { @@ -498,18 +415,20 @@ describe('ProjectLocator', function () { } }) - it('should not call the callback more than once', function (done) { + it('should not call the callback more than once', async function () { const path = `${this.duplicateFolder.name}/${this.doc.name}` - this.locator.findElementByPath({ project: this.project, path }, () => - done() - ) + await this.locator.promises.findElementByPath({ + project: this.project, + path, + }) }) // mocha will throw exception if done called multiple times - it('should not call the callback more than once when the path is longer than 1 level below the duplicate level', function (done) { + it('should not call the callback more than once when the path is longer than 1 level below the duplicate level', async function () { const path = `${this.duplicateFolder.name}/1/main.tex` - this.locator.findElementByPath({ project: this.project, path }, () => - done() - ) + await this.locator.promises.findElementByPath({ + project: this.project, + path, + }) }) }) // mocha will throw exception if done called multiple times @@ -526,18 +445,13 @@ describe('ProjectLocator', function () { } }) - it('should not crash with a null', function (done) { + it('should not crash with a null', async function () { const path = '/other.tex' - this.locator.findElementByPath( - { project: this.project, path }, - (err, element) => { - if (err != null) { - return done(err) - } - element.name.should.equal('other.tex') - done() - } - ) + const { element } = await this.locator.promises.findElementByPath({ + project: this.project, + path, + }) + element.name.should.equal('other.tex') }) }) @@ -546,35 +460,31 @@ describe('ProjectLocator', function () { this.ProjectGetter = { getProject: sinon.stub().callsArg(2) } }) - it('should not crash with a null', function (done) { + it('should not crash with a null', async function () { const path = '/other.tex' - this.locator.findElementByPath( - { project_id: project._id, path }, - (err, element) => { - expect(err).to.exist - done() - } - ) + await expect( + this.locator.promises.findElementByPath({ + project_id: project._id, + path, + }) + ).to.be.rejected }) }) describe('with a project_id', function () { - it('should take a doc path and return the element for a root level document', function (done) { + it('should take a doc path and return the element for a root level document', async function () { const path = `${doc1.name}` - this.locator.findElementByPath( - { project_id: project._id, path }, - (err, element, type) => { - if (err != null) { - return done(err) - } - this.ProjectGetter.getProject - .calledWith(project._id, { rootFolder: true, rootDoc_id: true }) - .should.equal(true) - element.should.deep.equal(doc1) - expect(type).to.equal('doc') - done() + const { element, type } = await this.locator.promises.findElementByPath( + { + project_id: project._id, + path, } ) + this.ProjectGetter.getProject + .calledWith(project._id, { rootFolder: true, rootDoc_id: true }) + .should.equal(true) + element.should.deep.equal(doc1) + expect(type).to.equal('doc') }) }) }) diff --git a/services/web/test/unit/src/Project/ProjectRootDocManagerTests.js b/services/web/test/unit/src/Project/ProjectRootDocManagerTests.js index 41b7516baa..ec4ea57778 100644 --- a/services/web/test/unit/src/Project/ProjectRootDocManagerTests.js +++ b/services/web/test/unit/src/Project/ProjectRootDocManagerTests.js @@ -1,16 +1,3 @@ -/* eslint-disable - max-len, - no-return-assign, - no-unused-vars, - no-useless-escape, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const { expect } = require('chai') const { ObjectId } = require('mongodb-legacy') const sinon = require('sinon') @@ -33,16 +20,13 @@ describe('ProjectRootDocManager', function () { this.sl_req_id = 'sl-req-id-123' this.callback = sinon.stub() this.globbyFiles = ['a.tex', 'b.tex', 'main.tex'] - this.globby = sinon.stub().returns( - new Promise(resolve => { - return resolve(this.globbyFiles) - }) - ) + this.globby = sinon.stub().resolves(this.globbyFiles) + this.fs = { readFile: sinon.stub().callsArgWith(2, new Error('file not found')), stat: sinon.stub().callsArgWith(1, null, { size: 100 }), } - return (this.ProjectRootDocManager = SandboxedModule.require(modulePath, { + this.ProjectRootDocManager = SandboxedModule.require(modulePath, { requires: { './ProjectEntityHandler': (this.ProjectEntityHandler = {}), './ProjectEntityUpdateHandler': (this.ProjectEntityUpdateHandler = {}), @@ -56,7 +40,7 @@ describe('ProjectRootDocManager', function () { globby: this.globby, fs: this.fs, }, - })) + }) }) describe('setRootDocAutomatically', function () { @@ -67,7 +51,7 @@ describe('ProjectRootDocManager', function () { .returns(true) }) describe('when there is a suitable root doc', function () { - beforeEach(function (done) { + beforeEach(async function () { this.docs = { '/chapter1.tex': { _id: this.docId1, @@ -98,27 +82,26 @@ describe('ProjectRootDocManager', function () { this.ProjectEntityHandler.getAllDocs = sinon .stub() .callsArgWith(1, null, this.docs) - this.ProjectRootDocManager.setRootDocAutomatically( - this.project_id, - done + await this.ProjectRootDocManager.promises.setRootDocAutomatically( + this.project_id ) }) it('should check the docs of the project', function () { - return this.ProjectEntityHandler.getAllDocs + this.ProjectEntityHandler.getAllDocs .calledWith(this.project_id) .should.equal(true) }) it('should set the root doc to the doc containing a documentclass', function () { - return this.ProjectEntityUpdateHandler.setRootDoc + this.ProjectEntityUpdateHandler.setRootDoc .calledWith(this.project_id, this.docId2) .should.equal(true) }) }) describe('when the root doc is an Rtex file', function () { - beforeEach(function (done) { + beforeEach(async function () { this.docs = { '/chapter1.tex': { _id: this.docId1, @@ -132,21 +115,20 @@ describe('ProjectRootDocManager', function () { this.ProjectEntityHandler.getAllDocs = sinon .stub() .callsArgWith(1, null, this.docs) - return this.ProjectRootDocManager.setRootDocAutomatically( - this.project_id, - done + await this.ProjectRootDocManager.promises.setRootDocAutomatically( + this.project_id ) }) it('should set the root doc to the doc containing a documentclass', function () { - return this.ProjectEntityUpdateHandler.setRootDoc + this.ProjectEntityUpdateHandler.setRootDoc .calledWith(this.project_id, this.docId2) .should.equal(true) }) }) describe('when there is no suitable root doc', function () { - beforeEach(function (done) { + beforeEach(async function () { this.docs = { '/chapter1.tex': { _id: this.docId1, @@ -160,16 +142,13 @@ describe('ProjectRootDocManager', function () { this.ProjectEntityHandler.getAllDocs = sinon .stub() .callsArgWith(1, null, this.docs) - return this.ProjectRootDocManager.setRootDocAutomatically( - this.project_id, - done + await this.ProjectRootDocManager.promises.setRootDocAutomatically( + this.project_id ) }) it('should not set the root doc to the doc containing a documentclass', function () { - return this.ProjectEntityUpdateHandler.setRootDoc.called.should.equal( - false - ) + this.ProjectEntityUpdateHandler.setRootDoc.called.should.equal(false) }) }) }) @@ -191,13 +170,13 @@ describe('ProjectRootDocManager', function () { this.fs.readFile .withArgs('/foo/a/a.tex') .callsArgWith(2, null, 'Potato? Potahto. Potootee!') - return (this.documentclassContent = '% test\n\\documentclass\n% test') + this.documentclassContent = '% test\n\\documentclass\n% test' }) describe('when there is a file in a subfolder', function () { beforeEach(function () { // have to splice globbyFiles weirdly because of the way the stubbed globby method handles references - return this.globbyFiles.splice( + this.globbyFiles.splice( 0, this.globbyFiles.length, 'c.tex', @@ -207,70 +186,56 @@ describe('ProjectRootDocManager', function () { ) }) - it('processes the root folder files first, and then the subfolder, in alphabetical order', function (done) { - return this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - (error, path) => { - expect(error).not.to.exist - expect(path).to.equal('a.tex') - sinon.assert.callOrder( - this.fs.readFile.withArgs('/foo/a.tex'), - this.fs.readFile.withArgs('/foo/b.tex'), - this.fs.readFile.withArgs('/foo/c.tex'), - this.fs.readFile.withArgs('/foo/a/a.tex') - ) - return done() - } + it('processes the root folder files first, and then the subfolder, in alphabetical order', async function () { + const { path } = + await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory( + '/foo' + ) + expect(path).to.equal('a.tex') + sinon.assert.callOrder( + this.fs.readFile.withArgs('/foo/a.tex'), + this.fs.readFile.withArgs('/foo/b.tex'), + this.fs.readFile.withArgs('/foo/c.tex'), + this.fs.readFile.withArgs('/foo/a/a.tex') ) }) - it('processes smaller files first', function (done) { + it('processes smaller files first', async function () { this.fs.stat.withArgs('/foo/c.tex').callsArgWith(1, null, { size: 1 }) - return this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - (error, path) => { - expect(error).not.to.exist - expect(path).to.equal('c.tex') - sinon.assert.callOrder( - this.fs.readFile.withArgs('/foo/c.tex'), - this.fs.readFile.withArgs('/foo/a.tex'), - this.fs.readFile.withArgs('/foo/b.tex'), - this.fs.readFile.withArgs('/foo/a/a.tex') - ) - return done() - } + const { path } = + await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory( + '/foo' + ) + expect(path).to.equal('c.tex') + sinon.assert.callOrder( + this.fs.readFile.withArgs('/foo/c.tex'), + this.fs.readFile.withArgs('/foo/a.tex'), + this.fs.readFile.withArgs('/foo/b.tex'), + this.fs.readFile.withArgs('/foo/a/a.tex') ) }) }) describe('when main.tex contains a documentclass', function () { beforeEach(function () { - return this.fs.readFile + this.fs.readFile .withArgs('/foo/main.tex') .callsArgWith(2, null, this.documentclassContent) }) - it('returns main.tex', function (done) { - return this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - (error, path, content) => { - expect(error).not.to.exist - expect(path).to.equal('main.tex') - expect(content).to.equal(this.documentclassContent) - return done() - } - ) + it('returns main.tex', async function () { + const { path, content } = + await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory( + '/foo' + ) + expect(path).to.equal('main.tex') + expect(content).to.equal(this.documentclassContent) }) - it('processes main.text first and stops processing when it finds the content', function (done) { - return this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - () => { - expect(this.fs.readFile).to.be.calledWith('/foo/main.tex') - expect(this.fs.readFile).not.to.be.calledWith('/foo/a.tex') - return done() - } - ) + it('processes main.text first and stops processing when it finds the content', async function () { + await this.ProjectRootDocManager.findRootDocFileFromDirectory('/foo') + expect(this.fs.readFile).to.be.calledWith('/foo/main.tex') + expect(this.fs.readFile).not.to.be.calledWith('/foo/a.tex') }) }) @@ -284,7 +249,7 @@ describe('ProjectRootDocManager', function () { .callsArgWith(2, null, 'foo') }) - it('returns the first .tex file from the root folder', function (done) { + it('returns the first .tex file from the root folder', async function () { this.globbyFiles.splice( 0, this.globbyFiles.length, @@ -293,18 +258,15 @@ describe('ProjectRootDocManager', function () { 'nested/chapter1a.tex' ) - this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - (error, path, content) => { - expect(error).not.to.exist - expect(path).to.equal('a.tex') - expect(content).to.equal('foo') - return done() - } - ) + const { path, content } = + await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory( + '/foo' + ) + expect(path).to.equal('a.tex') + expect(content).to.equal('foo') }) - it('returns main.tex file from the root folder', function (done) { + it('returns main.tex file from the root folder', async function () { this.globbyFiles.splice( 0, this.globbyFiles.length, @@ -314,227 +276,206 @@ describe('ProjectRootDocManager', function () { 'nested/chapter1a.tex' ) - this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - (error, path, content) => { - expect(error).not.to.exist - expect(path).to.equal('main.tex') - expect(content).to.equal('foo') - return done() - } - ) + const { path, content } = + await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory( + '/foo' + ) + expect(path).to.equal('main.tex') + expect(content).to.equal('foo') }) }) describe('when a.tex contains a documentclass', function () { beforeEach(function () { - return this.fs.readFile + this.fs.readFile .withArgs('/foo/a.tex') .callsArgWith(2, null, this.documentclassContent) }) - it('returns a.tex', function (done) { - return this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - (error, path, content) => { - expect(error).not.to.exist - expect(path).to.equal('a.tex') - expect(content).to.equal(this.documentclassContent) - return done() - } - ) + it('returns a.tex', async function () { + const { path, content } = + await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory( + '/foo' + ) + expect(path).to.equal('a.tex') + expect(content).to.equal(this.documentclassContent) }) - it('processes main.text first and stops processing when it finds the content', function (done) { - return this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - () => { - expect(this.fs.readFile).to.be.calledWith('/foo/main.tex') - expect(this.fs.readFile).to.be.calledWith('/foo/a.tex') - expect(this.fs.readFile).not.to.be.calledWith('/foo/b.tex') - return done() - } + it('processes main.text first and stops processing when it finds the content', async function () { + await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory( + '/foo' ) + expect(this.fs.readFile).to.be.calledWith('/foo/main.tex') + expect(this.fs.readFile).to.be.calledWith('/foo/a.tex') + expect(this.fs.readFile).not.to.be.calledWith('/foo/b.tex') }) }) describe('when there is no documentclass', function () { - it('returns with no error', function (done) { - return this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - error => { - expect(error).not.to.exist - return done() - } + it('returns with no error', async function () { + await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory( + '/foo' ) }) - it('processes all the files', function (done) { - return this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - () => { - expect(this.fs.readFile).to.be.calledWith('/foo/main.tex') - expect(this.fs.readFile).to.be.calledWith('/foo/a.tex') - expect(this.fs.readFile).to.be.calledWith('/foo/b.tex') - return done() - } + it('processes all the files', async function () { + await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory( + '/foo' ) + expect(this.fs.readFile).to.be.calledWith('/foo/main.tex') + expect(this.fs.readFile).to.be.calledWith('/foo/a.tex') + expect(this.fs.readFile).to.be.calledWith('/foo/b.tex') }) }) describe('when there is an error reading a file', function () { beforeEach(function () { - return this.fs.readFile + this.fs.readFile .withArgs('/foo/a.tex') .callsArgWith(2, new Error('something went wrong')) }) - it('returns an error', function (done) { - return this.ProjectRootDocManager.findRootDocFileFromDirectory( - '/foo', - (error, path, content) => { - expect(error).to.exist - expect(path).not.to.exist - expect(content).not.to.exist - return done() - } - ) + it('returns an error', async function () { + let error + + try { + await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory( + '/foo' + ) + } catch (err) { + error = err + } + + expect(error).to.exist }) }) }) describe('setRootDocFromName', function () { describe('when there is a suitable root doc', function () { - beforeEach(function (done) { + beforeEach(async function () { this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon .stub() .callsArgWith(1, null, this.docPaths) this.ProjectEntityUpdateHandler.setRootDoc = sinon .stub() .callsArgWith(2) - return this.ProjectRootDocManager.setRootDocFromName( + await this.ProjectRootDocManager.promises.setRootDocFromName( this.project_id, - '/main.tex', - done + '/main.tex' ) }) it('should check the docs of the project', function () { - return this.ProjectEntityHandler.getAllDocPathsFromProjectById + this.ProjectEntityHandler.getAllDocPathsFromProjectById .calledWith(this.project_id) .should.equal(true) }) it('should set the root doc to main.tex', function () { - return this.ProjectEntityUpdateHandler.setRootDoc + this.ProjectEntityUpdateHandler.setRootDoc .calledWith(this.project_id, this.docId2.toString()) .should.equal(true) }) }) describe('when there is a suitable root doc but the leading slash is missing', function () { - beforeEach(function (done) { + beforeEach(async function () { this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon .stub() .callsArgWith(1, null, this.docPaths) this.ProjectEntityUpdateHandler.setRootDoc = sinon .stub() .callsArgWith(2) - return this.ProjectRootDocManager.setRootDocFromName( + await this.ProjectRootDocManager.promises.setRootDocFromName( this.project_id, - 'main.tex', - done + 'main.tex' ) }) it('should check the docs of the project', function () { - return this.ProjectEntityHandler.getAllDocPathsFromProjectById + this.ProjectEntityHandler.getAllDocPathsFromProjectById .calledWith(this.project_id) .should.equal(true) }) it('should set the root doc to main.tex', function () { - return this.ProjectEntityUpdateHandler.setRootDoc + this.ProjectEntityUpdateHandler.setRootDoc .calledWith(this.project_id, this.docId2.toString()) .should.equal(true) }) }) describe('when there is a suitable root doc with a basename match', function () { - beforeEach(function (done) { + beforeEach(async function () { this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon .stub() .callsArgWith(1, null, this.docPaths) this.ProjectEntityUpdateHandler.setRootDoc = sinon .stub() .callsArgWith(2) - return this.ProjectRootDocManager.setRootDocFromName( + await this.ProjectRootDocManager.promises.setRootDocFromName( this.project_id, - 'chapter1a.tex', - done + 'chapter1a.tex' ) }) it('should check the docs of the project', function () { - return this.ProjectEntityHandler.getAllDocPathsFromProjectById + this.ProjectEntityHandler.getAllDocPathsFromProjectById .calledWith(this.project_id) .should.equal(true) }) it('should set the root doc using the basename', function () { - return this.ProjectEntityUpdateHandler.setRootDoc + this.ProjectEntityUpdateHandler.setRootDoc .calledWith(this.project_id, this.docId3.toString()) .should.equal(true) }) }) describe('when there is a suitable root doc but the filename is in quotes', function () { - beforeEach(function (done) { + beforeEach(async function () { this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon .stub() .callsArgWith(1, null, this.docPaths) this.ProjectEntityUpdateHandler.setRootDoc = sinon .stub() .callsArgWith(2) - return this.ProjectRootDocManager.setRootDocFromName( + await this.ProjectRootDocManager.promises.setRootDocFromName( this.project_id, - "'main.tex'", - done + "'main.tex'" ) }) it('should check the docs of the project', function () { - return this.ProjectEntityHandler.getAllDocPathsFromProjectById + this.ProjectEntityHandler.getAllDocPathsFromProjectById .calledWith(this.project_id) .should.equal(true) }) it('should set the root doc to main.tex', function () { - return this.ProjectEntityUpdateHandler.setRootDoc + this.ProjectEntityUpdateHandler.setRootDoc .calledWith(this.project_id, this.docId2.toString()) .should.equal(true) }) }) describe('when there is no suitable root doc', function () { - beforeEach(function (done) { + beforeEach(async function () { this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon .stub() .callsArgWith(1, null, this.docPaths) this.ProjectEntityUpdateHandler.setRootDoc = sinon .stub() .callsArgWith(2) - return this.ProjectRootDocManager.setRootDocFromName( + await this.ProjectRootDocManager.promises.setRootDocFromName( this.project_id, - 'other.tex', - done + 'other.tex' ) }) it('should not set the root doc', function () { - return this.ProjectEntityUpdateHandler.setRootDoc.called.should.equal( - false - ) + this.ProjectEntityUpdateHandler.setRootDoc.called.should.equal(false) }) }) }) @@ -545,73 +486,73 @@ describe('ProjectRootDocManager', function () { this.ProjectGetter.getProject = sinon .stub() .callsArgWith(2, null, this.project) - return (this.ProjectRootDocManager.setRootDocAutomatically = sinon + this.ProjectRootDocManager.setRootDocAutomatically = sinon .stub() - .callsArgWith(1, null)) + .callsArgWith(1, null) }) describe('when the root doc is set', function () { beforeEach(function () { this.project.rootDoc_id = this.docId2 - return this.ProjectRootDocManager.ensureRootDocumentIsSet( + this.ProjectRootDocManager.ensureRootDocumentIsSet( this.project_id, this.callback ) }) it('should find the project fetching only the rootDoc_id field', function () { - return this.ProjectGetter.getProject + this.ProjectGetter.getProject .calledWith(this.project_id, { rootDoc_id: 1 }) .should.equal(true) }) it('should not try to update the project rootDoc_id', function () { - return this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal( + this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal( false ) }) it('should call the callback', function () { - return this.callback.called.should.equal(true) + this.callback.called.should.equal(true) }) }) describe('when the root doc is not set', function () { beforeEach(function () { - return this.ProjectRootDocManager.ensureRootDocumentIsSet( + this.ProjectRootDocManager.ensureRootDocumentIsSet( this.project_id, this.callback ) }) it('should find the project with only the rootDoc_id field', function () { - return this.ProjectGetter.getProject + this.ProjectGetter.getProject .calledWith(this.project_id, { rootDoc_id: 1 }) .should.equal(true) }) it('should update the project rootDoc_id', function () { - return this.ProjectRootDocManager.setRootDocAutomatically + this.ProjectRootDocManager.setRootDocAutomatically .calledWith(this.project_id) .should.equal(true) }) it('should call the callback', function () { - return this.callback.called.should.equal(true) + this.callback.called.should.equal(true) }) }) describe('when the project does not exist', function () { beforeEach(function () { this.ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, null) - return this.ProjectRootDocManager.ensureRootDocumentIsSet( + this.ProjectRootDocManager.ensureRootDocumentIsSet( this.project_id, this.callback ) }) it('should call the callback with an error', function () { - return this.callback + this.callback .calledWith( sinon.match .instanceOf(Error) @@ -645,26 +586,26 @@ describe('ProjectRootDocManager', function () { this.ProjectEntityHandler.getDocPathFromProjectByDocId = sinon .stub() .callsArgWith(2, null, this.docPaths[this.docId2]) - return this.ProjectRootDocManager.ensureRootDocumentIsValid( + this.ProjectRootDocManager.ensureRootDocumentIsValid( this.project_id, this.callback ) }) it('should find the project without doc lines', function () { - return this.ProjectGetter.getProjectWithoutDocLines + this.ProjectGetter.getProjectWithoutDocLines .calledWith(this.project_id) .should.equal(true) }) it('should not try to update the project rootDoc_id', function () { - return this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal( + this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal( false ) }) it('should call the callback', function () { - return this.callback.called.should.equal(true) + this.callback.called.should.equal(true) }) }) @@ -674,58 +615,58 @@ describe('ProjectRootDocManager', function () { this.ProjectEntityHandler.getDocPathFromProjectByDocId = sinon .stub() .callsArgWith(2, null, null) - return this.ProjectRootDocManager.ensureRootDocumentIsValid( + this.ProjectRootDocManager.ensureRootDocumentIsValid( this.project_id, this.callback ) }) it('should find the project without doc lines', function () { - return this.ProjectGetter.getProjectWithoutDocLines + this.ProjectGetter.getProjectWithoutDocLines .calledWith(this.project_id) .should.equal(true) }) it('should unset the root doc', function () { - return this.ProjectEntityUpdateHandler.unsetRootDoc + this.ProjectEntityUpdateHandler.unsetRootDoc .calledWith(this.project_id) .should.equal(true) }) it('should try to find a new rootDoc', function () { - return this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal( + this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal( true ) }) it('should call the callback', function () { - return this.callback.called.should.equal(true) + this.callback.called.should.equal(true) }) }) }) describe('when the root doc is not set', function () { beforeEach(function () { - return this.ProjectRootDocManager.ensureRootDocumentIsValid( + this.ProjectRootDocManager.ensureRootDocumentIsValid( this.project_id, this.callback ) }) it('should find the project without doc lines', function () { - return this.ProjectGetter.getProjectWithoutDocLines + this.ProjectGetter.getProjectWithoutDocLines .calledWith(this.project_id) .should.equal(true) }) it('should update the project rootDoc_id', function () { - return this.ProjectRootDocManager.setRootDocAutomatically + this.ProjectRootDocManager.setRootDocAutomatically .calledWith(this.project_id) .should.equal(true) }) it('should call the callback', function () { - return this.callback.called.should.equal(true) + this.callback.called.should.equal(true) }) }) @@ -734,14 +675,14 @@ describe('ProjectRootDocManager', function () { this.ProjectGetter.getProjectWithoutDocLines = sinon .stub() .callsArgWith(1, null, null) - return this.ProjectRootDocManager.ensureRootDocumentIsValid( + this.ProjectRootDocManager.ensureRootDocumentIsValid( this.project_id, this.callback ) }) it('should call the callback with an error', function () { - return this.callback + this.callback .calledWith( sinon.match .instanceOf(Error) diff --git a/services/web/test/unit/src/Publishers/PublishersGetterTests.js b/services/web/test/unit/src/Publishers/PublishersGetterTests.js index bad46039de..b5a3124436 100644 --- a/services/web/test/unit/src/Publishers/PublishersGetterTests.js +++ b/services/web/test/unit/src/Publishers/PublishersGetterTests.js @@ -42,15 +42,10 @@ describe('PublishersGetter', function () { }) describe('getManagedPublishers', function () { - it('fetches v1 data before returning publisher list', function (done) { - this.PublishersGetter.getManagedPublishers( - this.userId, - (error, publishers) => { - expect(error).to.be.null - publishers.length.should.equal(1) - done() - } - ) + it('fetches v1 data before returning publisher list', async function () { + const publishers = + await this.PublishersGetter.promises.getManagedPublishers(this.userId) + expect(publishers.length).to.equal(1) }) }) }) diff --git a/services/web/test/unit/src/Security/LoginRateLimiterTests.js b/services/web/test/unit/src/Security/LoginRateLimiterTests.js index 645002400e..6dadaad8c2 100644 --- a/services/web/test/unit/src/Security/LoginRateLimiterTests.js +++ b/services/web/test/unit/src/Security/LoginRateLimiterTests.js @@ -25,23 +25,17 @@ describe('LoginRateLimiter', function () { }) describe('processLoginRequest', function () { - it('should consume points', function (done) { - this.LoginRateLimiter.processLoginRequest(this.email, (err, allow) => { - if (err) { - return done(err) - } - expect(this.rateLimiter.consume).to.have.been.calledWith(this.email) - done() - }) + it('should consume points', async function () { + await this.LoginRateLimiter.promises.processLoginRequest(this.email) + expect(this.rateLimiter.consume).to.have.been.calledWith(this.email) }) describe('when login is allowed', function () { - it('should call pass allow=true', function (done) { - this.LoginRateLimiter.processLoginRequest(this.email, (err, allow) => { - expect(err).to.equal(null) - expect(allow).to.equal(true) - done() - }) + it('should call pass allow=true', async function () { + const allow = await this.LoginRateLimiter.promises.processLoginRequest( + this.email + ) + expect(allow).to.equal(true) }) }) @@ -50,12 +44,11 @@ describe('LoginRateLimiter', function () { this.rateLimiter.consume.rejects({ remainingPoints: 0 }) }) - it('should call pass allow=false', function (done) { - this.LoginRateLimiter.processLoginRequest(this.email, (err, allow) => { - expect(err).to.equal(null) - expect(allow).to.equal(false) - done() - }) + it('should call pass allow=false', async function () { + const allow = await this.LoginRateLimiter.promises.processLoginRequest( + this.email + ) + expect(allow).to.equal(false) }) }) @@ -64,22 +57,24 @@ describe('LoginRateLimiter', function () { this.rateLimiter.consume.rejects(new Error('woops')) }) - it('should produce an error', function (done) { - this.LoginRateLimiter.processLoginRequest(this.email, (err, allow) => { - expect(err).to.not.equal(null) - expect(err).to.be.instanceof(Error) - done() - }) + it('should produce an error', async function () { + let error + + try { + await this.LoginRateLimiter.promises.processLoginRequest(this.email) + } catch (err) { + error = err + } + + expect(error).to.exist }) }) }) describe('recordSuccessfulLogin', function () { - it('should clear the rate limit', function (done) { - this.LoginRateLimiter.recordSuccessfulLogin(this.email, () => { - expect(this.rateLimiter.delete).to.have.been.calledWith(this.email) - done() - }) + it('should clear the rate limit', async function () { + await this.LoginRateLimiter.promises.recordSuccessfulLogin(this.email) + expect(this.rateLimiter.delete).to.have.been.calledWith(this.email) }) }) }) diff --git a/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js b/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js index d4bdd9d9b4..5fd2d11bc3 100644 --- a/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js +++ b/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js @@ -294,7 +294,7 @@ describe('RecurlyWrapper', function () { }) describe('updateAccountEmailAddress, with invalid XML', function () { - beforeEach(async function (done) { + beforeEach(async function () { this.recurlyAccountId = 'account-id-123' this.newEmail = '\uD800@example.com' this.apiRequest = sinon @@ -307,22 +307,24 @@ describe('RecurlyWrapper', function () { body: fixtures['accounts/104'], } }) - done() }) afterEach(function () { this.RecurlyWrapper.promises.apiRequest.restore() }) - it('should produce an error', function (done) { - this.RecurlyWrapper.promises - .updateAccountEmailAddress(this.recurlyAccountId, this.newEmail) - .catch(error => { - expect(error).to.exist - expect(error.message.startsWith('Invalid character')).to.equal(true) - expect(this.apiRequest.called).to.equal(false) - done() - }) + it('should produce an error', async function () { + try { + await this.RecurlyWrapper.promises.updateAccountEmailAddress( + this.recurlyAccountId, + this.newEmail + ) + expect(false).to.equal(true) // Fail if we don't have an error + } catch (error) { + expect(error).to.have.property('message') + expect(error.message.startsWith('Invalid character')).to.be.true + expect(this.apiRequest.called).to.equal(false) + } }) }) @@ -702,19 +704,27 @@ describe('RecurlyWrapper', function () { }) }) - it('should produce an error', function (done) { - this.call().catch(err => { - expect(err).to.be.instanceof( - SubscriptionErrors.RecurlyTransactionError - ) - expect(err.info.public.message).to.be.equal( - 'Your card must be authenticated with 3D Secure before continuing.' - ) - expect(err.info.public.threeDSecureActionTokenId).to.be.equal( - 'mock_three_d_secure_action_token' - ) - done() - }) + it('should produce an error', async function () { + const promise = this.call() + let error + + try { + await promise + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf( + SubscriptionErrors.RecurlyTransactionError + ) + expect(error).to.have.nested.property( + 'info.public.message', + 'Your card must be authenticated with 3D Secure before continuing.' + ) + expect(error).to.have.nested.property( + 'info.public.threeDSecureActionTokenId', + 'mock_three_d_secure_action_token' + ) }) }) diff --git a/services/web/test/unit/src/Subscription/TeamInvitesHandlerTests.js b/services/web/test/unit/src/Subscription/TeamInvitesHandlerTests.js index 0b762823a4..fdd247bf96 100644 --- a/services/web/test/unit/src/Subscription/TeamInvitesHandlerTests.js +++ b/services/web/test/unit/src/Subscription/TeamInvitesHandlerTests.js @@ -131,174 +131,149 @@ describe('TeamInvitesHandler', function () { }) describe('getInvite', function () { - it("returns the invite if there's one", function (done) { - this.TeamInvitesHandler.getInvite( - this.token, - (err, invite, subscription) => { - expect(err).to.eq(null) - expect(invite).to.deep.eq(this.teamInvite) - expect(subscription).to.deep.eq(this.subscription) - done() - } - ) + it("returns the invite if there's one", async function () { + const { invite, subscription } = + await this.TeamInvitesHandler.promises.getInvite(this.token) + + expect(invite).to.deep.eq(this.teamInvite) + expect(subscription).to.deep.eq(this.subscription) }) - it("returns teamNotFound if there's none", function (done) { + it("returns teamNotFound if there's none", async function () { this.Subscription.findOne = sinon.stub().resolves(null) - this.TeamInvitesHandler.getInvite( - this.token, - (err, invite, subscription) => { - expect(err).to.be.instanceof(Errors.NotFoundError) - done() - } - ) + let error + try { + await this.TeamInvitesHandler.promises.getInvite(this.token) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(Errors.NotFoundError) }) }) describe('createInvite', function () { - it('adds the team invite to the subscription', function (done) { - this.TeamInvitesHandler.createInvite( + it('adds the team invite to the subscription', async function () { + const invite = await this.TeamInvitesHandler.promises.createInvite( this.manager._id, this.subscription, - 'John.Snow@example.com', - (err, invite) => { - expect(err).to.eq(null) - expect(invite.token).to.eq(this.newToken) - expect(invite.email).to.eq('john.snow@example.com') - expect(invite.inviterName).to.eq( - 'Daenerys Targaryen (daenerys@example.com)' - ) - expect(invite.invite).to.be.true - expect(this.subscription.teamInvites).to.deep.include(invite) - done() - } + 'John.Snow@example.com' ) + expect(invite.token).to.eq(this.newToken) + expect(invite.email).to.eq('john.snow@example.com') + expect(invite.inviterName).to.eq( + 'Daenerys Targaryen (daenerys@example.com)' + ) + expect(invite.invite).to.be.true + expect(this.subscription.teamInvites).to.deep.include(invite) }) - it('sends an email', function (done) { - this.TeamInvitesHandler.createInvite( + it('sends an email', async function () { + await this.TeamInvitesHandler.promises.createInvite( this.manager._id, this.subscription, - 'John.Snow@example.com', - (err, invite) => { - this.EmailHandler.promises.sendEmail - .calledWith( - 'verifyEmailToJoinTeam', - sinon.match({ - to: 'john.snow@example.com', - inviter: this.manager, - acceptInviteUrl: `http://example.com/subscription/invites/${this.newToken}/`, - }) - ) - .should.equal(true) - done(err) - } + 'John.Snow@example.com' ) + + this.EmailHandler.promises.sendEmail + .calledWith( + 'verifyEmailToJoinTeam', + sinon.match({ + to: 'john.snow@example.com', + inviter: this.manager, + acceptInviteUrl: `http://example.com/subscription/invites/${this.newToken}/`, + }) + ) + .should.equal(true) }) - it('refreshes the existing invite if the email has already been invited', function (done) { + it('refreshes the existing invite if the email has already been invited', async function () { const originalInvite = Object.assign({}, this.teamInvite) - this.TeamInvitesHandler.createInvite( + const invite = await this.TeamInvitesHandler.promises.createInvite( this.manager._id, this.subscription, - originalInvite.email, - (err, invite) => { - expect(err).to.eq(null) - expect(invite).to.exist - - expect(this.subscription.teamInvites.length).to.eq(1) - expect(this.subscription.teamInvites).to.deep.include(invite) - - expect(invite.email).to.eq(originalInvite.email) - - this.subscription.save.calledOnce.should.eq(true) - - done() - } + originalInvite.email ) + expect(invite).to.exist + + expect(this.subscription.teamInvites.length).to.eq(1) + expect(this.subscription.teamInvites).to.deep.include(invite) + + expect(invite.email).to.eq(originalInvite.email) + + this.subscription.save.calledOnce.should.eq(true) }) - it('removes any legacy invite from the subscription', function (done) { - this.TeamInvitesHandler.createInvite( + it('removes any legacy invite from the subscription', async function () { + await this.TeamInvitesHandler.promises.createInvite( this.manager._id, this.subscription, - 'John.Snow@example.com', - (err, invite) => { - this.Subscription.updateOne - .calledWith( - { _id: new ObjectId('55153a8014829a865bbf700d') }, - { $pull: { invited_emails: 'john.snow@example.com' } } - ) - .should.eq(true) - done(err) - } + 'John.Snow@example.com' ) + + this.Subscription.updateOne + .calledWith( + { _id: new ObjectId('55153a8014829a865bbf700d') }, + { $pull: { invited_emails: 'john.snow@example.com' } } + ) + .should.eq(true) }) - it('add user to subscription if inviting self', function (done) { - this.TeamInvitesHandler.createInvite( + it('add user to subscription if inviting self', async function () { + const invite = await this.TeamInvitesHandler.promises.createInvite( this.manager._id, this.subscription, - this.manager.email, - (err, invite) => { - sinon.assert.calledWith( - this.SubscriptionUpdater.promises.addUserToGroup, - this.subscription._id, - this.manager._id - ) - sinon.assert.notCalled(this.subscription.save) - expect(invite.token).to.not.exist - expect(invite.email).to.eq(this.manager.email) - expect(invite.first_name).to.eq(this.manager.first_name) - expect(invite.last_name).to.eq(this.manager.last_name) - expect(invite.invite).to.be.false - done(err) - } + this.manager.email ) + sinon.assert.calledWith( + this.SubscriptionUpdater.promises.addUserToGroup, + this.subscription._id, + this.manager._id + ) + sinon.assert.notCalled(this.subscription.save) + expect(invite.token).to.not.exist + expect(invite.email).to.eq(this.manager.email) + expect(invite.first_name).to.eq(this.manager.first_name) + expect(invite.last_name).to.eq(this.manager.last_name) + expect(invite.invite).to.be.false }) - it('sends an SSO invite if SSO is enabled and inviting self', function (done) { + it('sends an SSO invite if SSO is enabled and inviting self', async function () { this.subscription.ssoConfig = new ObjectId('abc123abc123abc123abc123') this.SSOConfig.findById .withArgs(this.subscription.ssoConfig) .resolves({ enabled: true }) - this.TeamInvitesHandler.createInvite( + await this.TeamInvitesHandler.promises.createInvite( this.manager._id, this.subscription, - this.manager.email, - (err, invite) => { - sinon.assert.calledWith( - this.Modules.promises.hooks.fire, - 'sendGroupSSOReminder', - this.manager._id, - this.subscription._id - ) - done(err) - } + this.manager.email + ) + sinon.assert.calledWith( + this.Modules.promises.hooks.fire, + 'sendGroupSSOReminder', + this.manager._id, + this.subscription._id ) }) - it('does not send an SSO invite if SSO is disabled and inviting self', function (done) { + it('does not send an SSO invite if SSO is disabled and inviting self', async function () { this.subscription.ssoConfig = new ObjectId('abc123abc123abc123abc123') this.SSOConfig.findById .withArgs(this.subscription.ssoConfig) .resolves({ enabled: false }) - this.TeamInvitesHandler.createInvite( + await this.TeamInvitesHandler.promises.createInvite( this.manager._id, this.subscription, - this.manager.email, - (err, invite) => { - sinon.assert.notCalled(this.Modules.promises.hooks.fire) - done(err) - } + this.manager.email ) + sinon.assert.notCalled(this.Modules.promises.hooks.fire) }) - it('sends a notification if inviting registered user', function (done) { + it('sends a notification if inviting registered user', async function () { const id = new ObjectId('6a6b3a8014829a865bbf700d') const managedUsersEnabled = false @@ -308,22 +283,19 @@ describe('TeamInvitesHandler', function () { _id: id, }) - this.TeamInvitesHandler.createInvite( + const invite = await this.TeamInvitesHandler.promises.createInvite( this.manager._id, this.subscription, - 'John.Snow@example.com', - (err, invite) => { - this.NotificationsBuilder.promises - .groupInvitation( - id.toString(), - this.subscription._id, - managedUsersEnabled - ) - .create.calledWith(invite) - .should.eq(true) - done(err) - } + 'John.Snow@example.com' ) + this.NotificationsBuilder.promises + .groupInvitation( + id.toString(), + this.subscription._id, + managedUsersEnabled + ) + .create.calledWith(invite) + .should.eq(true) }) }) @@ -375,112 +347,115 @@ describe('TeamInvitesHandler', function () { }) describe('with standard group', function () { - it('adds the user to the team', function (done) { - this.TeamInvitesHandler.acceptInvite('dddddddd', this.user.id, () => { - this.SubscriptionUpdater.promises.addUserToGroup - .calledWith(this.subscription._id, this.user.id) - .should.eq(true) - done() - }) + it('adds the user to the team', async function () { + await this.TeamInvitesHandler.promises.acceptInvite( + 'dddddddd', + this.user.id + ) + this.SubscriptionUpdater.promises.addUserToGroup + .calledWith(this.subscription._id, this.user.id) + .should.eq(true) }) - it('removes the invite from the subscription', function (done) { - this.TeamInvitesHandler.acceptInvite('dddddddd', this.user.id, () => { - this.Subscription.updateOne - .calledWith( - { _id: new ObjectId('55153a8014829a865bbf700d') }, - { $pull: { teamInvites: { email: 'john.snow@example.com' } } } - ) - .should.eq(true) - done() - }) + it('removes the invite from the subscription', async function () { + await this.TeamInvitesHandler.promises.acceptInvite( + 'dddddddd', + this.user.id + ) + this.Subscription.updateOne + .calledWith( + { _id: new ObjectId('55153a8014829a865bbf700d') }, + { $pull: { teamInvites: { email: 'john.snow@example.com' } } } + ) + .should.eq(true) }) - it('removes dashboard notification after they accepted group invitation', function (done) { + it('removes dashboard notification after they accepted group invitation', async function () { const managedUsersEnabled = false - this.TeamInvitesHandler.acceptInvite('dddddddd', this.user.id, () => { - sinon.assert.called( - this.NotificationsBuilder.promises.groupInvitation( - this.user.id, - this.subscription._id, - managedUsersEnabled - ).read - ) - done() - }) + await this.TeamInvitesHandler.promises.acceptInvite( + 'dddddddd', + this.user.id + ) + sinon.assert.called( + this.NotificationsBuilder.promises.groupInvitation( + this.user.id, + this.subscription._id, + managedUsersEnabled + ).read + ) }) - it('should not schedule an SSO invite reminder', function (done) { - this.TeamInvitesHandler.acceptInvite('dddddddd', this.user.id, () => { - sinon.assert.notCalled(this.Modules.promises.hooks.fire) - done() - }) + it('should not schedule an SSO invite reminder', async function () { + await this.TeamInvitesHandler.promises.acceptInvite( + 'dddddddd', + this.user.id + ) + sinon.assert.notCalled(this.Modules.promises.hooks.fire) }) }) describe('with managed group', function () { - it('should enroll the group member', function (done) { + it('should enroll the group member', async function () { this.subscription.managedUsersEnabled = true - this.TeamInvitesHandler.acceptInvite('dddddddd', this.user.id, () => { - sinon.assert.calledWith( - this.Modules.promises.hooks.fire, - 'enrollInManagedSubscription', - this.user.id, - this.subscription - ) - done() - }) + await this.TeamInvitesHandler.promises.acceptInvite( + 'dddddddd', + this.user.id + ) + sinon.assert.calledWith( + this.Modules.promises.hooks.fire, + 'enrollInManagedSubscription', + this.user.id, + this.subscription + ) }) }) describe('with group SSO enabled', function () { - it('should schedule an SSO invite reminder', function (done) { + it('should schedule an SSO invite reminder', async function () { this.subscription.ssoConfig = 'ssoconfig1' this.SSOConfig.findById .withArgs('ssoconfig1') .resolves({ enabled: true }) - this.TeamInvitesHandler.acceptInvite('dddddddd', this.user.id, () => { - sinon.assert.calledWith( - this.Modules.promises.hooks.fire, - 'scheduleGroupSSOReminder', - this.user.id, - this.subscription._id - ) - done() - }) + await this.TeamInvitesHandler.promises.acceptInvite( + 'dddddddd', + this.user.id + ) + sinon.assert.calledWith( + this.Modules.promises.hooks.fire, + 'scheduleGroupSSOReminder', + this.user.id, + this.subscription._id + ) }) }) }) describe('revokeInvite', function () { - it('removes the team invite from the subscription', function (done) { - this.TeamInvitesHandler.revokeInvite( + it('removes the team invite from the subscription', async function () { + await this.TeamInvitesHandler.promises.revokeInvite( this.manager._id, this.subscription, - 'jorah@example.com', - () => { - this.Subscription.updateOne - .calledWith( - { _id: new ObjectId('55153a8014829a865bbf700d') }, - { $pull: { teamInvites: { email: 'jorah@example.com' } } } - ) - .should.eq(true) - - this.Subscription.updateOne - .calledWith( - { _id: new ObjectId('55153a8014829a865bbf700d') }, - { $pull: { invited_emails: 'jorah@example.com' } } - ) - .should.eq(true) - done() - } + 'jorah@example.com' ) + this.Subscription.updateOne + .calledWith( + { _id: new ObjectId('55153a8014829a865bbf700d') }, + { $pull: { teamInvites: { email: 'jorah@example.com' } } } + ) + .should.eq(true) + + this.Subscription.updateOne + .calledWith( + { _id: new ObjectId('55153a8014829a865bbf700d') }, + { $pull: { invited_emails: 'jorah@example.com' } } + ) + .should.eq(true) }) - it('removes dashboard notification for pending group invitation', function (done) { + it('removes dashboard notification for pending group invitation', async function () { const managedUsersEnabled = false const pendingUser = { @@ -492,26 +467,23 @@ describe('TeamInvitesHandler', function () { .withArgs(pendingUser.email) .resolves(pendingUser) - this.TeamInvitesHandler.revokeInvite( + await this.TeamInvitesHandler.promises.revokeInvite( this.manager._id, this.subscription, - pendingUser.email, - () => { - sinon.assert.called( - this.NotificationsBuilder.promises.groupInvitation( - pendingUser.id, - this.subscription._id, - managedUsersEnabled - ).read - ) + pendingUser.email + ) - done() - } + sinon.assert.called( + this.NotificationsBuilder.promises.groupInvitation( + pendingUser.id, + this.subscription._id, + managedUsersEnabled + ).read ) }) }) - describe('createTeamInvitesForLegacyInvitedEmail', function (done) { + describe('createTeamInvitesForLegacyInvitedEmail', function () { beforeEach(function () { this.subscription.invited_emails = [ 'eddard@example.com', @@ -523,58 +495,71 @@ describe('TeamInvitesHandler', function () { .resolves([this.subscription]) }) - it('sends an invitation email to addresses in the legacy invited_emails field', function (done) { - this.TeamInvitesHandler.createTeamInvitesForLegacyInvitedEmail( - 'eddard@example.com', - (err, invites) => { - expect(err).not.to.exist - expect(invites.length).to.eq(1) + it('sends an invitation email to addresses in the legacy invited_emails field', async function () { + const invites = + await this.TeamInvitesHandler.promises.createTeamInvitesForLegacyInvitedEmail( + 'eddard@example.com' + ) - const [invite] = invites - expect(invite.token).to.eq(this.newToken) - expect(invite.email).to.eq('eddard@example.com') - expect(invite.inviterName).to.eq( - 'Daenerys Targaryen (daenerys@example.com)' - ) - expect(invite.invite).to.be.true - expect(this.subscription.teamInvites).to.deep.include(invite) + expect(invites.length).to.eq(1) - done() - } + const [invite] = invites + expect(invite.token).to.eq(this.newToken) + expect(invite.email).to.eq('eddard@example.com') + expect(invite.inviterName).to.eq( + 'Daenerys Targaryen (daenerys@example.com)' ) + expect(invite.invite).to.be.true + expect(this.subscription.teamInvites).to.deep.include(invite) }) }) describe('validation', function () { - it("doesn't create an invite if the team limit has been reached", function (done) { + it("doesn't create an invite if the team limit has been reached", async function () { this.LimitationsManager.teamHasReachedMemberLimit = sinon .stub() .returns(true) - this.TeamInvitesHandler.createInvite( - this.manager._id, - this.subscription, - 'John.Snow@example.com', - (err, invite) => { - expect(err).to.deep.equal({ limitReached: true }) - done() - } - ) + let error + + try { + await this.TeamInvitesHandler.promises.createInvite( + this.manager._id, + this.subscription, + 'John.Snow@example.com' + ) + } catch (err) { + error = err + } + + expect(error).to.exist + + expect(error).to.deep.equal({ + limitReached: true, + }) }) - it("doesn't create an invite if the subscription is not in a group plan", function (done) { + it("doesn't create an invite if the subscription is not in a group plan", async function () { this.subscription.groupPlan = false - this.TeamInvitesHandler.createInvite( - this.manager._id, - this.subscription, - 'John.Snow@example.com', - (err, invite) => { - expect(err).to.deep.equal({ wrongPlan: true }) - done() - } - ) + let error + + try { + await this.TeamInvitesHandler.promises.createInvite( + this.manager._id, + this.subscription, + 'John.Snow@example.com' + ) + } catch (err) { + error = err + } + + expect(error).to.exist + + expect(error).to.deep.equal({ + wrongPlan: true, + }) }) - it("doesn't create an invite if the user is already part of the team", function (done) { + it("doesn't create an invite if the user is already part of the team", async function () { const member = { id: '1a2b', _id: '1a2b', @@ -586,16 +571,23 @@ describe('TeamInvitesHandler', function () { .withArgs(member.email) .resolves(member) - this.TeamInvitesHandler.createInvite( - this.manager._id, - this.subscription, - 'tyrion@example.com', - (err, invite) => { - expect(err).to.deep.equal({ alreadyInTeam: true }) - expect(invite).not.to.exist - done() - } - ) + let error + + try { + await this.TeamInvitesHandler.promises.createInvite( + this.manager._id, + this.subscription, + 'tyrion@example.com' + ) + } catch (err) { + error = err + } + + expect(error).to.exist + + expect(error).to.eql({ + alreadyInTeam: true, + }) }) }) }) diff --git a/services/web/test/unit/src/Tags/TagsHandlerTests.js b/services/web/test/unit/src/Tags/TagsHandlerTests.js index f30a649681..0ea36d7e17 100644 --- a/services/web/test/unit/src/Tags/TagsHandlerTests.js +++ b/services/web/test/unit/src/Tags/TagsHandlerTests.js @@ -30,78 +30,72 @@ describe('TagsHandler', function () { }) describe('finding users tags', function () { - it('should find all the documents with that user id', function (done) { + it('should find all the documents with that user id', async function () { const stubbedTags = [{ name: 'tag1' }, { name: 'tag2' }, { name: 'tag3' }] this.TagMock.expects('find') .once() .withArgs({ user_id: this.userId }) .resolves(stubbedTags) - this.TagsHandler.getAllTags(this.userId, (err, result) => { - expect(err).to.not.exist - this.TagMock.verify() - expect(result).to.deep.equal(stubbedTags) - done() - }) + const result = await this.TagsHandler.promises.getAllTags(this.userId) + this.TagMock.verify() + expect(result).to.deep.equal(stubbedTags) }) }) describe('createTag', function () { describe('when insert succeeds', function () { - it('should call insert in mongo', function (done) { + it('should call insert in mongo', async function () { this.TagMock.expects('create') .withArgs(this.tag) .once() .resolves(this.tag) - this.TagsHandler.createTag( + const resultTag = await this.TagsHandler.promises.createTag( this.tag.user_id, this.tag.name, - this.tag.color, - (err, resultTag) => { - expect(err).to.not.exist - this.TagMock.verify() - expect(resultTag.user_id).to.equal(this.tag.user_id) - expect(resultTag.name).to.equal(this.tag.name) - expect(resultTag.color).to.equal(this.tag.color) - done() - } + this.tag.color ) + this.TagMock.verify() + expect(resultTag.user_id).to.equal(this.tag.user_id) + expect(resultTag.name).to.equal(this.tag.name) + expect(resultTag.color).to.equal(this.tag.color) }) }) describe('when truncate=true, and tag is too long', function () { - it('should truncate the tag name', function (done) { + it('should truncate the tag name', async function () { // Expect the tag to end up with this truncated name this.tag.name = 'a comically long tag that will be truncated intern' this.TagMock.expects('create') .withArgs(this.tag) .once() .resolves(this.tag) - this.TagsHandler.createTag( + const resultTag = await this.TagsHandler.promises.createTag( this.tag.user_id, // Pass this too-long name 'a comically long tag that will be truncated internally and not throw an error', this.tag.color, - { truncate: true }, - (err, resultTag) => { - expect(err).to.not.exist - expect(resultTag.name).to.equal(this.tag.name) - done() - } + { truncate: true } ) + expect(resultTag.name).to.equal(this.tag.name) }) }) describe('when tag is too long', function () { - it('should throw an error', function (done) { - this.TagsHandler.createTag( - this.tag.user_id, - 'this is a tag that is very very very very very very long', - undefined, - err => { - expect(err.message).to.equal('Exceeded max tag length') - done() - } - ) + it('should throw an error', async function () { + let error + + try { + await this.TagsHandler.promises.createTag( + this.tag.user_id, + 'this is a tag that is very very very very very very long', + undefined + ) + } catch (err) { + error = err + } + + expect(error).to.exist + expect(error).to.have.property('message', 'Exceeded max tag length') }) }) @@ -111,7 +105,7 @@ describe('TagsHandler', function () { this.duplicateKeyError.code = 11000 }) - it('should get tag with findOne and return that tag', function (done) { + it('should get tag with findOne and return that tag', async function () { this.TagMock.expects('create') .withArgs(this.tag) .once() @@ -120,26 +114,22 @@ describe('TagsHandler', function () { .withArgs({ user_id: this.tag.user_id, name: this.tag.name }) .once() .resolves(this.tag) - this.TagsHandler.createTag( + const resultTag = await this.TagsHandler.promises.createTag( this.tag.user_id, this.tag.name, - this.tag.color, - (err, resultTag) => { - expect(err).to.not.exist - this.TagMock.verify() - expect(resultTag.user_id).to.equal(this.tag.user_id) - expect(resultTag.name).to.equal(this.tag.name) - expect(resultTag.color).to.equal(this.tag.color) - done() - } + this.tag.color ) + this.TagMock.verify() + expect(resultTag.user_id).to.equal(this.tag.user_id) + expect(resultTag.name).to.equal(this.tag.name) + expect(resultTag.color).to.equal(this.tag.color) }) }) }) describe('addProjectToTag', function () { describe('with a valid tag_id', function () { - it('should call update in mongo', function (done) { + it('should call update in mongo', async function () { this.TagMock.expects('findOneAndUpdate') .once() .withArgs( @@ -147,23 +137,19 @@ describe('TagsHandler', function () { { $addToSet: { project_ids: this.projectId } } ) .resolves() - this.TagsHandler.addProjectToTag( + await this.TagsHandler.promises.addProjectToTag( this.userId, this.tagId, - this.projectId, - err => { - expect(err).to.not.exist - this.TagMock.verify() - done() - } + this.projectId ) + this.TagMock.verify() }) }) }) describe('addProjectsToTag', function () { describe('with a valid tag_id', function () { - it('should call update in mongo', function (done) { + it('should call update in mongo', async function () { this.TagMock.expects('findOneAndUpdate') .once() .withArgs( @@ -171,22 +157,18 @@ describe('TagsHandler', function () { { $addToSet: { project_ids: { $each: this.projectIds } } } ) .resolves() - this.TagsHandler.addProjectsToTag( + await this.TagsHandler.promises.addProjectsToTag( this.userId, this.tagId, - this.projectIds, - err => { - expect(err).to.not.exist - this.TagMock.verify() - done() - } + this.projectIds ) + this.TagMock.verify() }) }) }) describe('addProjectToTagName', function () { - it('should call update in mongo', function (done) { + it('should call update in mongo', async function () { this.TagMock.expects('updateOne') .once() .withArgs( @@ -195,22 +177,18 @@ describe('TagsHandler', function () { { upsert: true } ) .resolves() - this.TagsHandler.addProjectToTagName( + await this.TagsHandler.promises.addProjectToTagName( this.tag.userId, this.tag.name, - this.projectId, - err => { - expect(err).to.not.exist - this.TagMock.verify() - done() - } + this.projectId ) + this.TagMock.verify() }) }) describe('removeProjectFromTag', function () { describe('with a valid tag_id', function () { - it('should call update in mongo', function (done) { + it('should call update in mongo', async function () { this.TagMock.expects('updateOne') .once() .withArgs( @@ -223,23 +201,20 @@ describe('TagsHandler', function () { } ) .resolves() - this.TagsHandler.removeProjectFromTag( + await this.TagsHandler.promises.removeProjectFromTag( this.userId, this.tagId, - this.projectId, - err => { - expect(err).to.not.exist - this.TagMock.verify() - done() - } + this.projectId ) + + this.TagMock.verify() }) }) }) describe('removeProjectsFromTag', function () { describe('with a valid tag_id', function () { - it('should call update in mongo', function (done) { + it('should call update in mongo', async function () { this.TagMock.expects('updateOne') .once() .withArgs( @@ -252,22 +227,18 @@ describe('TagsHandler', function () { } ) .resolves() - this.TagsHandler.removeProjectsFromTag( + await this.TagsHandler.promises.removeProjectsFromTag( this.userId, this.tagId, - this.projectIds, - err => { - expect(err).to.not.exist - this.TagMock.verify() - done() - } + this.projectIds ) + this.TagMock.verify() }) }) }) describe('removeProjectFromAllTags', function () { - it('should pull the project id from the tag', function (done) { + it('should pull the project id from the tag', async function () { this.TagMock.expects('updateMany') .once() .withArgs( @@ -279,20 +250,16 @@ describe('TagsHandler', function () { } ) .resolves() - this.TagsHandler.removeProjectFromAllTags( + await this.TagsHandler.promises.removeProjectFromAllTags( this.userId, - this.projectId, - err => { - expect(err).to.not.exist - this.TagMock.verify() - done() - } + this.projectId ) + this.TagMock.verify() }) }) describe('addProjectToTags', function () { - it('should add the project id to each tag', function (done) { + it('should add the project id to each tag', async function () { const tagIds = [] this.TagMock.expects('updateMany') @@ -307,38 +274,31 @@ describe('TagsHandler', function () { } ) .resolves() - this.TagsHandler.addProjectToTags( + await this.TagsHandler.promises.addProjectToTags( this.userId, tagIds, - this.projectId, - (err, result) => { - expect(err).to.not.exist - this.TagMock.verify() - done() - } + this.projectId ) + this.TagMock.verify() }) }) describe('deleteTag', function () { describe('with a valid tag_id', function () { - it('should call remove in mongo', function (done) { + it('should call remove in mongo', async function () { this.TagMock.expects('deleteOne') .once() .withArgs({ _id: this.tagId, user_id: this.userId }) .resolves() - this.TagsHandler.deleteTag(this.userId, this.tagId, err => { - expect(err).to.not.exist - this.TagMock.verify() - done() - }) + await this.TagsHandler.promises.deleteTag(this.userId, this.tagId) + this.TagMock.verify() }) }) }) describe('renameTag', function () { describe('with a valid tag_id', function () { - it('should call remove in mongo', function (done) { + it('should call remove in mongo', async function () { this.newName = 'new name' this.TagMock.expects('updateOne') .once() @@ -347,30 +307,31 @@ describe('TagsHandler', function () { { $set: { name: this.newName } } ) .resolves() - this.TagsHandler.renameTag( + await this.TagsHandler.promises.renameTag( this.userId, this.tagId, - this.newName, - err => { - expect(err).to.not.exist - this.TagMock.verify() - done() - } + this.newName ) + this.TagMock.verify() }) }) describe('when tag is too long', function () { - it('should throw an error', function (done) { - this.TagsHandler.renameTag( - this.userId, - this.tagId, - 'this is a tag that is very very very very very very long', - err => { - expect(err.message).to.equal('Exceeded max tag length') - done() - } - ) + it('should throw an error', async function () { + let error + + try { + await this.TagsHandler.promises.renameTag( + this.userId, + this.tagId, + 'this is a tag that is very very very very very very long' + ) + } catch (err) { + error = err + } + + expect(error).to.exist + expect(error).to.have.property('message', 'Exceeded max tag length') }) }) }) diff --git a/services/web/test/unit/src/Uploads/FileTypeManagerTests.js b/services/web/test/unit/src/Uploads/FileTypeManagerTests.js index ae8645b034..032f11ed57 100644 --- a/services/web/test/unit/src/Uploads/FileTypeManagerTests.js +++ b/services/web/test/unit/src/Uploads/FileTypeManagerTests.js @@ -65,22 +65,25 @@ describe('FileTypeManager', function () { describe('when it is a directory', function () { beforeEach(function () { this.stats.isDirectory.returns(true) - this.FileTypeManager.isDirectory('/some/path', this.callback) }) - it('should return true', function () { - this.callback.should.have.been.calledWith(null, true) + it('should return true', async function () { + const result = + await this.FileTypeManager.promises.isDirectory('/some/path') + + expect(result).to.equal(true) }) }) describe('when it is not a directory', function () { beforeEach(function () { this.stats.isDirectory.returns(false) - this.FileTypeManager.isDirectory('/some/path', this.callback) }) - it('should return false', function () { - this.callback.should.have.been.calledWith(null, false) + it('should return false', async function () { + const result = + await this.FileTypeManager.promises.isDirectory('/some/path') + expect(result).to.equal(false) }) }) }) @@ -113,223 +116,158 @@ describe('FileTypeManager', function () { '/GNUMakefile', ] TEXT_FILENAMES.forEach(filename => { - it(`should classify ${filename} as text`, function (done) { - this.FileTypeManager.getType( + it(`should classify ${filename} as text`, async function () { + const { binary } = await this.FileTypeManager.promises.getType( filename, 'utf8.tex', - null, - (err, { binary }) => { - if (err) { - return done(err) - } - binary.should.equal(false) - done() - } + null ) + + binary.should.equal(false) }) }) - it('should not classify short text files as binary', function (done) { + it('should not classify short text files as binary', async function () { this.stats.size = 2 * 1024 * 1024 // 2MB - this.FileTypeManager.getType( + const { binary } = await this.FileTypeManager.promises.getType( '/file.tex', 'text-short.tex', - null, - (err, { binary }) => { - if (err) { - return done(err) - } - binary.should.equal(false) - done() - } + null ) + + binary.should.equal(false) }) - it('should not classify text files just under the size limit as binary', function (done) { + it('should not classify text files just under the size limit as binary', async function () { this.stats.size = 2 * 1024 * 1024 // 2MB - this.FileTypeManager.getType( + const { binary } = await this.FileTypeManager.promises.getType( '/file.tex', 'text-smaller.tex', - null, - (err, { binary }) => { - if (err) { - return done(err) - } - binary.should.equal(false) - done() - } + null ) + + binary.should.equal(false) }) - it('should classify text files at the size limit as binary', function (done) { + it('should classify text files at the size limit as binary', async function () { this.stats.size = 2 * 1024 * 1024 // 2MB - this.FileTypeManager.getType( + const { binary } = await this.FileTypeManager.promises.getType( '/file.tex', 'text-exact.tex', - null, - (err, { binary }) => { - if (err) { - return done(err) - } - binary.should.equal(true) - done() - } + null ) + + binary.should.equal(true) }) - it('should classify long text files as binary', function (done) { + it('should classify long text files as binary', async function () { this.stats.size = 2 * 1024 * 1024 // 2MB - this.FileTypeManager.getType( + const { binary } = await this.FileTypeManager.promises.getType( '/file.tex', 'text-long.tex', - null, - (err, { binary }) => { - if (err) { - return done(err) - } - binary.should.equal(true) - done() - } + null ) + + binary.should.equal(true) }) - it('should classify large text files as binary', function (done) { + it('should classify large text files as binary', async function () { this.stats.size = 8 * 1024 * 1024 // 8MB - this.FileTypeManager.getType( + const { binary } = await this.FileTypeManager.promises.getType( '/file.tex', 'utf8.tex', - null, - (err, { binary }) => { - if (err) { - return done(err) - } - binary.should.equal(true) - done() - } + null ) + + binary.should.equal(true) }) - it('should not try to determine the encoding of large files', function (done) { + it('should not try to determine the encoding of large files', async function () { this.stats.size = 8 * 1024 * 1024 // 8MB - this.FileTypeManager.getType('/file.tex', 'utf8.tex', null, err => { - if (err) { - return done(err) - } - sinon.assert.notCalled(this.isUtf8) - done() - }) - }) - - it('should detect the encoding of a utf8 file', function (done) { - this.FileTypeManager.getType( + await this.FileTypeManager.promises.getType( '/file.tex', 'utf8.tex', - null, - (err, { binary, encoding }) => { - if (err) { - return done(err) - } - sinon.assert.calledOnce(this.isUtf8) - this.isUtf8.returned(true).should.equal(true) - encoding.should.equal('utf-8') - done() - } + null ) + + sinon.assert.notCalled(this.isUtf8) }) - it("should return 'latin1' for non-unicode encodings", function (done) { - this.FileTypeManager.getType( + it('should detect the encoding of a utf8 file', async function () { + const { encoding } = await this.FileTypeManager.promises.getType( + '/file.tex', + 'utf8.tex', + null + ) + + sinon.assert.calledOnce(this.isUtf8) + this.isUtf8.returned(true).should.equal(true) + encoding.should.equal('utf-8') + }) + + it("should return 'latin1' for non-unicode encodings", async function () { + const { encoding } = await this.FileTypeManager.promises.getType( '/file.tex', 'latin1.tex', - null, - (err, { binary, encoding }) => { - if (err) { - return done(err) - } - sinon.assert.calledOnce(this.isUtf8) - this.isUtf8.returned(false).should.equal(true) - encoding.should.equal('latin1') - done() - } + null ) + + sinon.assert.calledOnce(this.isUtf8) + this.isUtf8.returned(false).should.equal(true) + encoding.should.equal('latin1') }) - it('should classify utf16 with BOM as utf-16', function (done) { - this.FileTypeManager.getType( + it('should classify utf16 with BOM as utf-16', async function () { + const { encoding } = await this.FileTypeManager.promises.getType( '/file.tex', 'utf16.tex', - null, - (err, { binary, encoding }) => { - if (err) { - return done(err) - } - sinon.assert.calledOnce(this.isUtf8) - this.isUtf8.returned(false).should.equal(true) - encoding.should.equal('utf-16le') - done() - } + null ) + + sinon.assert.calledOnce(this.isUtf8) + this.isUtf8.returned(false).should.equal(true) + encoding.should.equal('utf-16le') }) - it('should classify latin1 files with a null char as binary', function (done) { - this.FileTypeManager.getType( + it('should classify latin1 files with a null char as binary', async function () { + const { binary } = await this.FileTypeManager.promises.getType( '/file.tex', 'latin1-null.tex', - null, - (err, { binary }) => { - if (err) { - return done(err) - } - expect(binary).to.equal(true) - done() - } + null ) + expect(binary).to.equal(true) }) - it('should classify utf8 files with a null char as binary', function (done) { - this.FileTypeManager.getType( + it('should classify utf8 files with a null char as binary', async function () { + const { binary } = await this.FileTypeManager.promises.getType( '/file.tex', 'utf8-null.tex', - null, - (err, { binary }) => { - if (err) { - return done(err) - } - expect(binary).to.equal(true) - done() - } + null ) + + expect(binary).to.equal(true) }) - it('should classify utf8 files with non-BMP chars as binary', function (done) { - this.FileTypeManager.getType( + it('should classify utf8 files with non-BMP chars as binary', async function () { + const { binary } = await this.FileTypeManager.promises.getType( '/file.tex', 'utf8-non-bmp.tex', - null, - (err, { binary }) => { - if (err) { - return done(err) - } - expect(binary).to.equal(true) - done() - } + null ) + + expect(binary).to.equal(true) }) - it('should classify utf8 files with ascii control chars as utf-8', function (done) { - this.FileTypeManager.getType( - '/file.tex', - 'utf8-control-chars.tex', - null, - (err, { binary, encoding }) => { - if (err) { - return done(err) - } - expect(binary).to.equal(false) - expect(encoding).to.equal('utf-8') - done() - } - ) + it('should classify utf8 files with ascii control chars as utf-8', async function () { + const { binary, encoding } = + await this.FileTypeManager.promises.getType( + '/file.tex', + 'utf8-control-chars.tex', + null + ) + + expect(binary).to.equal(false) + expect(encoding).to.equal('utf-8') }) }) @@ -342,189 +280,132 @@ describe('FileTypeManager', function () { '/tex', ] BINARY_FILENAMES.forEach(filename => { - it(`should classify ${filename} as binary`, function (done) { - this.FileTypeManager.getType( + it(`should classify ${filename} as binary`, async function () { + const { binary } = await this.FileTypeManager.promises.getType( filename, 'latin1.tex', // even if the content is not binary - null, - (err, { binary }) => { - if (err) { - return done(err) - } - binary.should.equal(true) - done() - } + null ) + + binary.should.equal(true) }) }) - it('should not try to get the character encoding', function (done) { - this.FileTypeManager.getType('/file.png', 'utf8.tex', null, err => { - if (err) { - return done(err) - } - sinon.assert.notCalled(this.isUtf8) - done() - }) + it('should not try to get the character encoding', async function () { + await this.FileTypeManager.promises.getType( + '/file.png', + 'utf8.tex', + null + ) + + sinon.assert.notCalled(this.isUtf8) }) - it('should recognise new binary files as binary', function (done) { - this.FileTypeManager.getType( + it('should recognise new binary files as binary', async function () { + const { binary } = await this.FileTypeManager.promises.getType( '/file.py', 'latin1.tex', - null, - (err, { binary }) => { - if (err) { - return done(err) - } - binary.should.equal(true) - done() - } + null ) + + binary.should.equal(true) }) - it('should recognise existing binary files as binary', function (done) { - this.FileTypeManager.getType( + it('should recognise existing binary files as binary', async function () { + const { binary } = await this.FileTypeManager.promises.getType( '/file.py', 'latin1.tex', - 'file', - (err, { binary }) => { - if (err) { - return done(err) - } - binary.should.equal(true) - done() - } + 'file' ) + + binary.should.equal(true) }) - it('should preserve existing non-binary files as non-binary', function (done) { - this.FileTypeManager.getType( + it('should preserve existing non-binary files as non-binary', async function () { + const { binary } = await this.FileTypeManager.promises.getType( '/file.py', 'latin1.tex', - 'doc', - (err, { binary }) => { - if (err) { - return done(err) - } - binary.should.equal(false) - done() - } + 'doc' ) + + binary.should.equal(false) }) }) }) describe('shouldIgnore', function () { - it('should ignore tex auxiliary files', function (done) { - this.FileTypeManager.shouldIgnore('file.aux', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(true) - done() - }) + it('should ignore tex auxiliary files', async function () { + const ignore = + await this.FileTypeManager.promises.shouldIgnore('file.aux') + ignore.should.equal(true) }) - it('should ignore dotfiles', function (done) { - this.FileTypeManager.shouldIgnore('path/.git', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(true) - done() - }) + it('should ignore dotfiles', async function () { + const ignore = + await this.FileTypeManager.promises.shouldIgnore('path/.git') + + ignore.should.equal(true) }) - it('should ignore .git directories and contained files', function (done) { - this.FileTypeManager.shouldIgnore('path/.git/info', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(true) - done() - }) + it('should ignore .git directories and contained files', async function () { + const ignore = + await this.FileTypeManager.promises.shouldIgnore('path/.git/info') + + ignore.should.equal(true) }) - it('should not ignore .latexmkrc dotfile', function (done) { - this.FileTypeManager.shouldIgnore('path/.latexmkrc', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(false) - done() - }) + it('should not ignore .latexmkrc dotfile', async function () { + const ignore = + await this.FileTypeManager.promises.shouldIgnore('path/.latexmkrc') + + ignore.should.equal(false) }) - it('should ignore __MACOSX', function (done) { - this.FileTypeManager.shouldIgnore('path/__MACOSX', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(true) - done() - }) + it('should ignore __MACOSX', async function () { + const ignore = + await this.FileTypeManager.promises.shouldIgnore('path/__MACOSX') + + ignore.should.equal(true) }) - it('should ignore synctex files', function (done) { - this.FileTypeManager.shouldIgnore('file.synctex', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(true) - done() - }) + it('should ignore synctex files', async function () { + const ignore = + await this.FileTypeManager.promises.shouldIgnore('file.synctex') + + ignore.should.equal(true) }) - it('should ignore synctex(busy) files', function (done) { - this.FileTypeManager.shouldIgnore('file.synctex(busy)', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(true) - done() - }) + it('should ignore synctex(busy) files', async function () { + const ignore = + await this.FileTypeManager.promises.shouldIgnore('file.synctex(busy)') + + ignore.should.equal(true) }) - it('should not ignore .tex files', function (done) { - this.FileTypeManager.shouldIgnore('file.tex', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(false) - done() - }) + it('should not ignore .tex files', async function () { + const ignore = + await this.FileTypeManager.promises.shouldIgnore('file.tex') + + ignore.should.equal(false) }) - it('should ignore the case of the extension', function (done) { - this.FileTypeManager.shouldIgnore('file.AUX', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(true) - done() - }) + it('should ignore the case of the extension', async function () { + const ignore = + await this.FileTypeManager.promises.shouldIgnore('file.AUX') + + ignore.should.equal(true) }) - it('should not ignore files with an ignored extension as full name', function (done) { - this.FileTypeManager.shouldIgnore('dvi', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(false) - done() - }) + it('should not ignore files with an ignored extension as full name', async function () { + const ignore = await this.FileTypeManager.promises.shouldIgnore('dvi') + ignore.should.equal(false) }) - it('should not ignore directories with an ignored extension as full name', function (done) { + it('should not ignore directories with an ignored extension as full name', async function () { this.stats.isDirectory.returns(true) - this.FileTypeManager.shouldIgnore('dvi', (err, ignore) => { - if (err) { - return done(err) - } - ignore.should.equal(false) - done() - }) + const ignore = await this.FileTypeManager.promises.shouldIgnore('dvi') + + ignore.should.equal(false) }) }) }) diff --git a/services/web/test/unit/src/User/ThirdPartyIdentityManagerTests.js b/services/web/test/unit/src/User/ThirdPartyIdentityManagerTests.js index d944262518..2dbbf64991 100644 --- a/services/web/test/unit/src/User/ThirdPartyIdentityManagerTests.js +++ b/services/web/test/unit/src/User/ThirdPartyIdentityManagerTests.js @@ -2,6 +2,9 @@ const sinon = require('sinon') const { expect } = require('chai') const SandboxedModule = require('sandboxed-module') const OError = require('@overleaf/o-error') +const { + ThirdPartyUserNotFoundError, +} = require('../../../../app/src/Features/Errors/Errors') const modulePath = '../../../../app/src/Features/User/ThirdPartyIdentityManager.js' @@ -77,16 +80,19 @@ describe('ThirdPartyIdentityManager', function () { expect(user).to.deep.equal(this.user) }) }) - it('should return ThirdPartyUserNotFoundError when no user linked', function (done) { - this.ThirdPartyIdentityManager.getUser( - 'google', - 'an-id-not-linked', - (error, user) => { - expect(error).to.exist - expect(error.name).to.equal('ThirdPartyUserNotFoundError') - done() - } - ) + it('should return ThirdPartyUserNotFoundError when no user linked', async function () { + let error + + try { + await this.ThirdPartyIdentityManager.promises.getUser( + 'google', + 'an-id-not-linked' + ) + } catch (err) { + error = err + } + + expect(error).to.be.instanceOf(ThirdPartyUserNotFoundError) }) }) describe('link', function () {