Merge pull request #24357 from overleaf/em-reviewers-limit

Count reviewers towards the collaborator limit in the backend

GitOrigin-RevId: 352fa37023ffbb032e23a474a81897ca871fee69
This commit is contained in:
Eric Mc Sween
2025-03-24 10:51:14 +00:00
committed by Copybot
parent e4dae982d2
commit 1fb94dee18
6 changed files with 301 additions and 269 deletions
@@ -14,7 +14,6 @@ import { hasAdminAccess } from '../Helpers/AdminAuthorizationHelper.js'
import TokenAccessHandler from '../TokenAccess/TokenAccessHandler.js' import TokenAccessHandler from '../TokenAccess/TokenAccessHandler.js'
import ProjectAuditLogHandler from '../Project/ProjectAuditLogHandler.js' import ProjectAuditLogHandler from '../Project/ProjectAuditLogHandler.js'
import LimitationsManager from '../Subscription/LimitationsManager.js' import LimitationsManager from '../Subscription/LimitationsManager.js'
import PrivilegeLevels from '../Authorization/PrivilegeLevels.js'
const ObjectId = mongodb.ObjectId const ObjectId = mongodb.ObjectId
@@ -80,17 +79,20 @@ async function setCollaboratorInfo(req, res, next) {
const userId = req.params.user_id const userId = req.params.user_id
const { privilegeLevel } = req.body const { privilegeLevel } = req.body
if (privilegeLevel !== PrivilegeLevels.READ_ONLY) { const allowed =
const allowed = await LimitationsManager.promises.canChangeCollaboratorPrivilegeLevel(
await LimitationsManager.promises.canAddXEditCollaborators(projectId, 1) projectId,
if (!allowed) { userId,
return HttpErrorHandler.forbidden( privilegeLevel
req, )
res, if (!allowed) {
'edit collaborator limit reached' return HttpErrorHandler.forbidden(
) req,
} res,
'edit collaborator limit reached'
)
} }
await CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel( await CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel(
projectId, projectId,
userId, userId,
@@ -128,12 +128,13 @@ async function getMemberIdPrivilegeLevel(userId, projectId) {
} }
async function getInvitedEditCollaboratorCount(projectId) { async function getInvitedEditCollaboratorCount(projectId) {
// Only counts invited members with readAndWrite privilege // Counts invited members with editor or reviewer roles
const members = await getMemberIdsWithPrivilegeLevels(projectId) const members = await getMemberIdsWithPrivilegeLevels(projectId)
return members.filter( return members.filter(
m => m =>
m.source === Sources.INVITE && m.source === Sources.INVITE &&
m.privilegeLevel === PrivilegeLevels.READ_AND_WRITE (m.privilegeLevel === PrivilegeLevels.READ_AND_WRITE ||
m.privilegeLevel === PrivilegeLevels.REVIEW)
).length ).length
} }
@@ -1,3 +1,5 @@
// @ts-check
const logger = require('@overleaf/logger') const logger = require('@overleaf/logger')
const ProjectGetter = require('../Project/ProjectGetter') const ProjectGetter = require('../Project/ProjectGetter')
const UserGetter = require('../User/UserGetter') const UserGetter = require('../User/UserGetter')
@@ -5,6 +7,7 @@ const SubscriptionLocator = require('./SubscriptionLocator')
const Settings = require('@overleaf/settings') const Settings = require('@overleaf/settings')
const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter') const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
const CollaboratorsInvitesGetter = require('../Collaborators/CollaboratorsInviteGetter') const CollaboratorsInvitesGetter = require('../Collaborators/CollaboratorsInviteGetter')
const PrivilegeLevels = require('../Authorization/PrivilegeLevels')
const { const {
callbackify, callbackify,
callbackifyMultiResult, callbackifyMultiResult,
@@ -58,6 +61,54 @@ async function canAddXEditCollaborators(
) )
} }
/**
* Check whether a collaborator can be switched to the given privilege level
*
* @param {string} projectId
* @param {string} userId
* @param {'readOnly' | 'review' | 'readAndWrite'} privilegeLevel
* @return {Promise<boolean>}
*/
async function canChangeCollaboratorPrivilegeLevel(
projectId,
userId,
privilegeLevel
) {
if (privilegeLevel === PrivilegeLevels.READ_ONLY) {
return true
}
const currentPrivilegeLevel =
await CollaboratorsGetter.promises.getMemberIdPrivilegeLevel(
userId,
projectId
)
if (
[PrivilegeLevels.READ_AND_WRITE, PrivilegeLevels.REVIEW].includes(
currentPrivilegeLevel
)
) {
// Current collaborator already takes a slot, so changing the privilege
// level won't increase the collaborator count
return true
}
const allowedNumber = await allowedNumberOfCollaboratorsInProject(projectId)
if (allowedNumber < 0) {
// -1 means unlimited
return true
}
const slotsTaken =
await CollaboratorsGetter.promises.getInvitedEditCollaboratorCount(
projectId
)
const inviteCount =
await CollaboratorsInvitesGetter.promises.getEditInviteCount(projectId)
return slotsTaken + inviteCount < allowedNumber
}
async function hasPaidSubscription(user) { async function hasPaidSubscription(user) {
const { hasSubscription, subscription } = await userHasSubscription(user) const { hasSubscription, subscription } = await userHasSubscription(user)
const { isMember } = await userIsMemberOfGroupSubscription(user) const { isMember } = await userIsMemberOfGroupSubscription(user)
@@ -122,6 +173,9 @@ const LimitationsManager = {
allowedNumberOfCollaboratorsForUser allowedNumberOfCollaboratorsForUser
), ),
canAddXEditCollaborators: callbackify(canAddXEditCollaborators), canAddXEditCollaborators: callbackify(canAddXEditCollaborators),
canChangeCollaboratorPrivilegeLevel: callbackify(
canChangeCollaboratorPrivilegeLevel
),
hasPaidSubscription: callbackifyMultiResult(hasPaidSubscription, [ hasPaidSubscription: callbackifyMultiResult(hasPaidSubscription, [
'hasPaidSubscription', 'hasPaidSubscription',
'subscription', 'subscription',
@@ -150,6 +204,7 @@ const LimitationsManager = {
allowedNumberOfCollaboratorsForUser, allowedNumberOfCollaboratorsForUser,
canAcceptEditCollaboratorInvite, canAcceptEditCollaboratorInvite,
canAddXEditCollaborators, canAddXEditCollaborators,
canChangeCollaboratorPrivilegeLevel,
hasPaidSubscription, hasPaidSubscription,
userHasSubscriptionOrIsGroupMember, userHasSubscriptionOrIsGroupMember,
userHasSubscription, userHasSubscription,
@@ -76,6 +76,7 @@ describe('CollaboratorsController', function () {
this.LimitationsManager = { this.LimitationsManager = {
promises: { promises: {
canAddXEditCollaborators: sinon.stub().resolves(), canAddXEditCollaborators: sinon.stub().resolves(),
canChangeCollaboratorPrivilegeLevel: sinon.stub().resolves(true),
}, },
} }
@@ -310,18 +311,17 @@ describe('CollaboratorsController', function () {
}) })
describe('when owner can add new edit collaborators', function () { describe('when owner can add new edit collaborators', function () {
beforeEach(function () {
this.LimitationsManager.promises.canAddXEditCollaborators.resolves(
true
)
})
it('should set privilege level after checking collaborators can be added', function (done) { it('should set privilege level after checking collaborators can be added', function (done) {
this.res.sendStatus = status => { this.res.sendStatus = status => {
expect(status).to.equal(204) expect(status).to.equal(204)
expect( expect(
this.LimitationsManager.promises.canAddXEditCollaborators this.LimitationsManager.promises
).to.have.been.calledWith(this.projectId, 1) .canChangeCollaboratorPrivilegeLevel
).to.have.been.calledWith(
this.projectId,
this.user._id,
'readAndWrite'
)
done() done()
} }
this.CollaboratorsController.setCollaboratorInfo(this.req, this.res) this.CollaboratorsController.setCollaboratorInfo(this.req, this.res)
@@ -330,7 +330,7 @@ describe('CollaboratorsController', function () {
describe('when owner cannot add edit collaborators', function () { describe('when owner cannot add edit collaborators', function () {
beforeEach(function () { beforeEach(function () {
this.LimitationsManager.promises.canAddXEditCollaborators.resolves( this.LimitationsManager.promises.canChangeCollaboratorPrivilegeLevel.resolves(
false false
) )
}) })
@@ -340,8 +340,13 @@ describe('CollaboratorsController', function () {
expect(req).to.equal(this.req) expect(req).to.equal(this.req)
expect(res).to.equal(this.res) expect(res).to.equal(this.res)
expect( expect(
this.LimitationsManager.promises.canAddXEditCollaborators this.LimitationsManager.promises
).to.have.been.calledWith(this.projectId, 1) .canChangeCollaboratorPrivilegeLevel
).to.have.been.calledWith(
this.projectId,
this.user._id,
'readAndWrite'
)
expect( expect(
this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel
).to.not.have.been.called ).to.not.have.been.called
@@ -535,12 +535,12 @@ describe('CollaboratorsGetter', function () {
}) })
describe('getInvitedEditCollaboratorCount', function () { describe('getInvitedEditCollaboratorCount', function () {
it('should return the count of invited edit collaborators (token, readAndWrite)', async function () { it('should return the count of invited edit collaborators (readAndWrite, review)', async function () {
const count = const count =
await this.CollaboratorsGetter.promises.getInvitedEditCollaboratorCount( await this.CollaboratorsGetter.promises.getInvitedEditCollaboratorCount(
this.project._id this.project._id
) )
expect(count).to.equal(2) expect(count).to.equal(4)
}) })
}) })
@@ -1,5 +1,4 @@
const SandboxedModule = require('sandboxed-module') const SandboxedModule = require('sandboxed-module')
const assert = require('assert')
const sinon = require('sinon') const sinon = require('sinon')
const { expect } = require('chai') const { expect } = require('chai')
const modulePath = require('path').join( const modulePath = require('path').join(
@@ -50,13 +49,14 @@ describe('LimitationsManager', function () {
this.CollaboratorsGetter = { this.CollaboratorsGetter = {
promises: { promises: {
getInvitedEditCollaboratorCount: sinon.stub().resolves(), getInvitedEditCollaboratorCount: sinon.stub().resolves(0),
getMemberIdPrivilegeLevel: sinon.stub(),
}, },
} }
this.CollaboratorsInviteGetter = { this.CollaboratorsInviteGetter = {
promises: { promises: {
getEditInviteCount: sinon.stub().resolves(), getEditInviteCount: sinon.stub().resolves(0),
}, },
} }
@@ -76,76 +76,64 @@ describe('LimitationsManager', function () {
describe('allowedNumberOfCollaboratorsInProject', function () { describe('allowedNumberOfCollaboratorsInProject', function () {
describe('when the project is owned by a user without a subscription', function () { describe('when the project is owned by a user without a subscription', function () {
beforeEach(function (done) { beforeEach(function () {
this.Settings.defaultFeatures = { collaborators: 23 } this.Settings.defaultFeatures = { collaborators: 23 }
this.project.owner_ref = this.userId this.project.owner_ref = this.userId
delete this.user.features delete this.user.features
this.callback = sinon.stub().callsFake(() => done())
this.LimitationsManager.allowedNumberOfCollaboratorsInProject(
this.projectId,
this.callback
)
}) })
it('should return the default number', function () { it('should return the default number', async function () {
this.callback const result =
.calledWith(null, this.Settings.defaultFeatures.collaborators) await this.LimitationsManager.promises.allowedNumberOfCollaboratorsInProject(
.should.equal(true) this.projectId
)
expect(result).to.equal(this.Settings.defaultFeatures.collaborators)
}) })
}) })
describe('when the project is owned by a user with a subscription', function () { describe('when the project is owned by a user with a subscription', function () {
beforeEach(function (done) { beforeEach(function () {
this.project.owner_ref = this.userId this.project.owner_ref = this.userId
this.user.features = { collaborators: 21 } this.user.features = { collaborators: 21 }
this.callback = sinon.stub().callsFake(() => done())
this.LimitationsManager.allowedNumberOfCollaboratorsInProject(
this.projectId,
this.callback
)
}) })
it('should return the number of collaborators the user is allowed', function () { it('should return the number of collaborators the user is allowed', async function () {
this.callback const result =
.calledWith(null, this.user.features.collaborators) await this.LimitationsManager.promises.allowedNumberOfCollaboratorsInProject(
.should.equal(true) this.projectId
)
expect(result).to.equal(this.user.features.collaborators)
}) })
}) })
}) })
describe('allowedNumberOfCollaboratorsForUser', function () { describe('allowedNumberOfCollaboratorsForUser', function () {
describe('when the user has no features', function () { describe('when the user has no features', function () {
beforeEach(function (done) { beforeEach(function () {
this.Settings.defaultFeatures = { collaborators: 23 } this.Settings.defaultFeatures = { collaborators: 23 }
delete this.user.features delete this.user.features
this.callback = sinon.stub().callsFake(() => done())
this.LimitationsManager.allowedNumberOfCollaboratorsForUser(
this.userId,
this.callback
)
}) })
it('should return the default number', function () { it('should return the default number', async function () {
this.callback const result =
.calledWith(null, this.Settings.defaultFeatures.collaborators) await this.LimitationsManager.promises.allowedNumberOfCollaboratorsForUser(
.should.equal(true) this.userId
)
expect(result).to.equal(this.Settings.defaultFeatures.collaborators)
}) })
}) })
describe('when the user has features', function () { describe('when the user has features', function () {
beforeEach(function (done) { beforeEach(async function () {
this.user.features = { collaborators: 21 } this.user.features = { collaborators: 21 }
this.callback = sinon.stub().callsFake(() => done()) this.result =
this.LimitationsManager.allowedNumberOfCollaboratorsForUser( await this.LimitationsManager.promises.allowedNumberOfCollaboratorsForUser(
this.userId, this.userId
this.callback )
)
}) })
it('should return the number of collaborators the user is allowed', function () { it('should return the number of collaborators the user is allowed', function () {
this.callback expect(this.result).to.equal(this.user.features.collaborators)
.calledWith(null, this.user.features.collaborators)
.should.equal(true)
}) })
}) })
}) })
@@ -222,7 +210,7 @@ describe('LimitationsManager', function () {
describe('canAddXEditCollaborators', function () { describe('canAddXEditCollaborators', function () {
describe('when the project has fewer collaborators than allowed', function () { describe('when the project has fewer collaborators than allowed', function () {
beforeEach(function (done) { beforeEach(function () {
this.current_number = 1 this.current_number = 1
this.user.features.collaborators = 2 this.user.features.collaborators = 2
this.invite_count = 0 this.invite_count = 0
@@ -231,21 +219,20 @@ describe('LimitationsManager', function () {
this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon
.stub() .stub()
.resolves(this.invite_count) .resolves(this.invite_count)
this.callback = sinon.stub().callsFake(() => done())
this.LimitationsManager.canAddXEditCollaborators(
this.projectId,
1,
this.callback
)
}) })
it('should return true', function () { it('should return true', async function () {
this.callback.calledWith(null, true).should.equal(true) const result =
await this.LimitationsManager.promises.canAddXEditCollaborators(
this.projectId,
1
)
expect(result).to.be.true
}) })
}) })
describe('when the project has fewer collaborators and invites than allowed', function () { describe('when the project has fewer collaborators and invites than allowed', function () {
beforeEach(function (done) { beforeEach(function () {
this.current_number = 1 this.current_number = 1
this.user.features.collaborators = 4 this.user.features.collaborators = 4
this.invite_count = 1 this.invite_count = 1
@@ -254,21 +241,20 @@ describe('LimitationsManager', function () {
this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon
.stub() .stub()
.resolves(this.invite_count) .resolves(this.invite_count)
this.callback = sinon.stub().callsFake(() => done())
this.LimitationsManager.canAddXEditCollaborators(
this.projectId,
1,
this.callback
)
}) })
it('should return true', function () { it('should return true', async function () {
this.callback.calledWith(null, true).should.equal(true) const result =
await this.LimitationsManager.promises.canAddXEditCollaborators(
this.projectId,
1
)
expect(result).to.be.true
}) })
}) })
describe('when the project has fewer collaborators than allowed but I want to add more than allowed', function () { describe('when the project has fewer collaborators than allowed but I want to add more than allowed', function () {
beforeEach(function (done) { beforeEach(function () {
this.current_number = 1 this.current_number = 1
this.user.features.collaborators = 2 this.user.features.collaborators = 2
this.invite_count = 0 this.invite_count = 0
@@ -277,21 +263,20 @@ describe('LimitationsManager', function () {
this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon
.stub() .stub()
.resolves(this.invite_count) .resolves(this.invite_count)
this.callback = sinon.stub().callsFake(() => done())
this.LimitationsManager.canAddXEditCollaborators(
this.projectId,
2,
this.callback
)
}) })
it('should return false', function () { it('should return false', async function () {
this.callback.calledWith(null, false).should.equal(true) const result =
await this.LimitationsManager.promises.canAddXEditCollaborators(
this.projectId,
2
)
expect(result).to.be.false
}) })
}) })
describe('when the project has more collaborators than allowed', function () { describe('when the project has more collaborators than allowed', function () {
beforeEach(function (done) { beforeEach(function () {
this.current_number = 3 this.current_number = 3
this.user.features.collaborators = 2 this.user.features.collaborators = 2
this.invite_count = 0 this.invite_count = 0
@@ -300,21 +285,20 @@ describe('LimitationsManager', function () {
this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon
.stub() .stub()
.resolves(this.invite_count) .resolves(this.invite_count)
this.callback = sinon.stub().callsFake(() => done())
this.LimitationsManager.canAddXEditCollaborators(
this.projectId,
1,
this.callback
)
}) })
it('should return false', function () { it('should return false', async function () {
this.callback.calledWith(null, false).should.equal(true) const result =
await this.LimitationsManager.promises.canAddXEditCollaborators(
this.projectId,
1
)
expect(result).to.be.false
}) })
}) })
describe('when the project has infinite collaborators', function () { describe('when the project has infinite collaborators', function () {
beforeEach(function (done) { beforeEach(function () {
this.current_number = 100 this.current_number = 100
this.user.features.collaborators = -1 this.user.features.collaborators = -1
this.invite_count = 0 this.invite_count = 0
@@ -323,21 +307,20 @@ describe('LimitationsManager', function () {
this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon
.stub() .stub()
.resolves(this.invite_count) .resolves(this.invite_count)
this.callback = sinon.stub().callsFake(() => done())
this.LimitationsManager.canAddXEditCollaborators(
this.projectId,
1,
this.callback
)
}) })
it('should return true', function () { it('should return true', async function () {
this.callback.calledWith(null, true).should.equal(true) const result =
await this.LimitationsManager.promises.canAddXEditCollaborators(
this.projectId,
1
)
expect(result).to.be.true
}) })
}) })
describe('when the project has more invites than allowed', function () { describe('when the project has more invites than allowed', function () {
beforeEach(function (done) { beforeEach(function () {
this.current_number = 0 this.current_number = 0
this.user.features.collaborators = 2 this.user.features.collaborators = 2
this.invite_count = 2 this.invite_count = 2
@@ -346,21 +329,20 @@ describe('LimitationsManager', function () {
this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon
.stub() .stub()
.resolves(this.invite_count) .resolves(this.invite_count)
this.callback = sinon.stub().callsFake(() => done())
this.LimitationsManager.canAddXEditCollaborators(
this.projectId,
1,
this.callback
)
}) })
it('should return false', function () { it('should return false', async function () {
this.callback.calledWith(null, false).should.equal(true) const result =
await this.LimitationsManager.promises.canAddXEditCollaborators(
this.projectId,
1
)
expect(result).to.be.false
}) })
}) })
describe('when the project has more invites and collaborators than allowed', function () { describe('when the project has more invites and collaborators than allowed', function () {
beforeEach(function (done) { beforeEach(function () {
this.current_number = 1 this.current_number = 1
this.user.features.collaborators = 2 this.user.features.collaborators = 2
this.invite_count = 1 this.invite_count = 1
@@ -369,16 +351,67 @@ describe('LimitationsManager', function () {
this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon this.CollaboratorsInviteGetter.promises.getEditInviteCount = sinon
.stub() .stub()
.resolves(this.invite_count) .resolves(this.invite_count)
this.callback = sinon.stub().callsFake(() => done()) })
this.LimitationsManager.canAddXEditCollaborators(
this.projectId, it('should return false', async function () {
1, const result =
this.callback await this.LimitationsManager.promises.canAddXEditCollaborators(
this.projectId,
1
)
expect(result).to.be.false
})
})
})
describe('canChangeCollaboratorPrivilegeLevel', function () {
beforeEach(function () {
this.collaboratorId = 'collaborator-id'
this.CollaboratorsGetter.promises.getMemberIdPrivilegeLevel.resolves(
'readOnly'
)
})
describe("when the limit hasn't been reached", function () {
it('accepts changing a viewer to an editor', async function () {
const result =
await this.LimitationsManager.promises.canChangeCollaboratorPrivilegeLevel(
this.projectId,
this.collaboratorId,
'readAndWrite'
)
expect(result).to.be.true
})
})
describe('when the limit has been reached', function () {
beforeEach(function () {
this.CollaboratorsGetter.promises.getInvitedEditCollaboratorCount.resolves(
1
) )
}) })
it('should return false', function () { it('accepts changing a reviewer to an editor', async function () {
this.callback.calledWith(null, false).should.equal(true) this.CollaboratorsGetter.promises.getMemberIdPrivilegeLevel.resolves(
'review'
)
const result =
await this.LimitationsManager.promises.canChangeCollaboratorPrivilegeLevel(
this.projectId,
this.collaboratorId,
'readAndWrite'
)
expect(result).to.be.true
})
it('rejects changing a viewer to a reviewer', async function () {
const result =
await this.LimitationsManager.promises.canChangeCollaboratorPrivilegeLevel(
this.projectId,
this.collaboratorId,
'review'
)
expect(result).to.be.false
}) })
}) })
}) })
@@ -390,60 +423,39 @@ describe('LimitationsManager', function () {
.resolves() .resolves()
}) })
it('should return true if the recurly token is set', function (done) { it('should return true if the recurly token is set', async function () {
this.SubscriptionLocator.promises.getUsersSubscription = sinon this.SubscriptionLocator.promises.getUsersSubscription = sinon
.stub() .stub()
.resolves({ .resolves({
recurlySubscription_id: '1234', recurlySubscription_id: '1234',
}) })
this.LimitationsManager.userHasSubscription( const { hasSubscription } =
this.user, await this.LimitationsManager.promises.userHasSubscription(this.user)
(err, hasSubscription) => { expect(hasSubscription).to.be.true
assert.equal(err, null)
hasSubscription.should.equal(true)
done()
}
)
}) })
it('should return false if the recurly token is not set', function (done) { it('should return false if the recurly token is not set', async function () {
this.SubscriptionLocator.promises.getUsersSubscription.resolves({}) this.SubscriptionLocator.promises.getUsersSubscription.resolves({})
this.subscription = {} const { hasSubscription } =
this.LimitationsManager.userHasSubscription( await this.LimitationsManager.promises.userHasSubscription(this.user)
this.user, expect(hasSubscription).to.be.false
(err, hasSubscription) => {
assert.equal(err, null)
hasSubscription.should.equal(false)
done()
}
)
}) })
it('should return false if the subscription is undefined', function (done) { it('should return false if the subscription is undefined', async function () {
this.SubscriptionLocator.promises.getUsersSubscription.resolves() this.SubscriptionLocator.promises.getUsersSubscription.resolves()
this.LimitationsManager.userHasSubscription( const { hasSubscription } =
this.user, await this.LimitationsManager.promises.userHasSubscription(this.user)
(err, hasSubscription) => { expect(hasSubscription).to.be.false
assert.equal(err, null)
hasSubscription.should.equal(false)
done()
}
)
}) })
it('should return the subscription', function (done) { it('should return the subscription', async function () {
const stubbedSubscription = { freeTrial: {}, token: '' } const stubbedSubscription = { freeTrial: {}, token: '' }
this.SubscriptionLocator.promises.getUsersSubscription.resolves( this.SubscriptionLocator.promises.getUsersSubscription.resolves(
stubbedSubscription stubbedSubscription
) )
this.LimitationsManager.userHasSubscription( const { subscription } =
this.user, await this.LimitationsManager.promises.userHasSubscription(this.user)
(err, hasSubOrIsGroupMember, subscription) => { expect(subscription).to.deep.equal(stubbedSubscription)
assert.equal(err, null)
subscription.should.deep.equal(stubbedSubscription)
done()
}
)
}) })
describe('when user has a custom account', function () { describe('when user has a custom account', function () {
@@ -454,26 +466,16 @@ describe('LimitationsManager', function () {
) )
}) })
it('should return true', function (done) { it('should return true', async function () {
this.LimitationsManager.userHasSubscription( const { hasSubscription } =
this.user, await this.LimitationsManager.promises.userHasSubscription(this.user)
(err, hasSubscription, subscription) => { expect(hasSubscription).to.be.true
assert.equal(err, null)
hasSubscription.should.equal(true)
done()
}
)
}) })
it('should return the subscription', function (done) { it('should return the subscription', async function () {
this.LimitationsManager.userHasSubscription( const { subscription } =
this.user, await this.LimitationsManager.promises.userHasSubscription(this.user)
(err, hasSubscription, subscription) => { expect(subscription).to.deep.equal(this.fakeSubscription)
assert.equal(err, null)
subscription.should.deep.equal(this.fakeSubscription)
done()
}
)
}) })
}) })
}) })
@@ -485,32 +487,26 @@ describe('LimitationsManager', function () {
.resolves() .resolves()
}) })
it('should return false if there are no groups subcriptions', function (done) { it('should return false if there are no groups subcriptions', async function () {
this.SubscriptionLocator.promises.getMemberSubscriptions.resolves([]) this.SubscriptionLocator.promises.getMemberSubscriptions.resolves([])
this.LimitationsManager.userIsMemberOfGroupSubscription( const { isMember } =
this.user, await this.LimitationsManager.promises.userIsMemberOfGroupSubscription(
(err, isMember) => { this.user
assert.equal(err, null) )
isMember.should.equal(false) expect(isMember).to.be.false
done()
}
)
}) })
it('should return true if there are no groups subcriptions', function (done) { it('should return true if there are no groups subcriptions', async function () {
let subscriptions const subscriptions = ['mock-subscription']
this.SubscriptionLocator.promises.getMemberSubscriptions.resolves( this.SubscriptionLocator.promises.getMemberSubscriptions.resolves(
(subscriptions = ['mock-subscription']) subscriptions
)
this.LimitationsManager.userIsMemberOfGroupSubscription(
this.user,
(err, isMember, retSubscriptions) => {
assert.equal(err, null)
isMember.should.equal(true)
retSubscriptions.should.equal(subscriptions)
done()
}
) )
const { isMember, subscriptions: retSubscriptions } =
await this.LimitationsManager.promises.userIsMemberOfGroupSubscription(
this.user
)
expect(isMember).to.be.true
expect(retSubscriptions).to.deep.equal(subscriptions)
}) })
}) })
@@ -524,54 +520,36 @@ describe('LimitationsManager', function () {
.resolves(null) .resolves(null)
}) })
it('should return true if userIsMemberOfGroupSubscription', function (done) { it('should return true if userIsMemberOfGroupSubscription', async function () {
this.SubscriptionLocator.promises.getMemberSubscriptions = sinon this.SubscriptionLocator.promises.getMemberSubscriptions = sinon
.stub() .stub()
.resolves([{ _id: '123' }]) .resolves([{ _id: '123' }])
this.LimitationsManager.hasPaidSubscription( const { hasPaidSubscription } =
this.user, await this.LimitationsManager.promises.hasPaidSubscription(this.user)
(err, hasSubOrIsGroupMember) => { expect(hasPaidSubscription).to.be.true
assert.equal(err, null)
hasSubOrIsGroupMember.should.equal(true)
done()
}
)
}) })
it('should return true if userHasSubscription', function (done) { it('should return true if userHasSubscription', async function () {
this.SubscriptionLocator.promises.getUsersSubscription = sinon this.SubscriptionLocator.promises.getUsersSubscription = sinon
.stub() .stub()
.resolves({ recurlySubscription_id: '123' }) .resolves({ recurlySubscription_id: '123' })
this.LimitationsManager.hasPaidSubscription( const { hasPaidSubscription } =
this.user, await this.LimitationsManager.promises.hasPaidSubscription(this.user)
(err, hasSubOrIsGroupMember) => { expect(hasPaidSubscription).to.be.true
assert.equal(err, null)
hasSubOrIsGroupMember.should.equal(true)
done()
}
)
}) })
it('should return false if none are true', function (done) { it('should return false if none are true', async function () {
this.LimitationsManager.hasPaidSubscription( const { hasPaidSubscription } =
this.user, await this.LimitationsManager.promises.hasPaidSubscription(this.user)
(err, hasSubOrIsGroupMember) => { expect(hasPaidSubscription).to.be.false
assert.equal(err, null)
hasSubOrIsGroupMember.should.equal(false)
done()
}
)
}) })
it('should have userHasSubscriptionOrIsGroupMember alias', function (done) { it('should have userHasSubscriptionOrIsGroupMember alias', async function () {
this.LimitationsManager.userHasSubscriptionOrIsGroupMember( const { hasPaidSubscription } =
this.user, await this.LimitationsManager.promises.userHasSubscriptionOrIsGroupMember(
(err, hasSubOrIsGroupMember) => { this.user
assert.equal(err, null) )
hasSubOrIsGroupMember.should.equal(false) expect(hasPaidSubscription).to.be.false
done()
}
)
}) })
}) })
@@ -587,48 +565,39 @@ describe('LimitationsManager', function () {
} }
}) })
it('should return true if the limit is hit (including members and invites)', function (done) { it('should return true if the limit is hit (including members and invites)', async function () {
this.SubscriptionLocator.promises.getSubscription.resolves( this.SubscriptionLocator.promises.getSubscription.resolves(
this.subscription this.subscription
) )
this.LimitationsManager.hasGroupMembersLimitReached( const { limitReached } =
this.subscriptionId, await this.LimitationsManager.promises.hasGroupMembersLimitReached(
(err, limitReached) => { this.subscriptionId
assert.equal(err, null) )
limitReached.should.equal(true) expect(limitReached).to.be.true
done()
}
)
}) })
it('should return false if the limit is not hit (including members and invites)', function (done) { it('should return false if the limit is not hit (including members and invites)', async function () {
this.subscription.membersLimit = 4 this.subscription.membersLimit = 4
this.SubscriptionLocator.promises.getSubscription.resolves( this.SubscriptionLocator.promises.getSubscription.resolves(
this.subscription this.subscription
) )
this.LimitationsManager.hasGroupMembersLimitReached( const { limitReached } =
this.subscriptionId, await this.LimitationsManager.promises.hasGroupMembersLimitReached(
(err, limitReached) => { this.subscriptionId
assert.equal(err, null) )
limitReached.should.equal(false) expect(limitReached).to.be.false
done()
}
)
}) })
it('should return true if the limit has been exceded (including members and invites)', function (done) { it('should return true if the limit has been exceded (including members and invites)', async function () {
this.subscription.membersLimit = 2 this.subscription.membersLimit = 2
this.SubscriptionLocator.promises.getSubscription.resolves( this.SubscriptionLocator.promises.getSubscription.resolves(
this.subscription this.subscription
) )
this.LimitationsManager.hasGroupMembersLimitReached( const { limitReached } =
this.subscriptionId, await this.LimitationsManager.promises.hasGroupMembersLimitReached(
(err, limitReached) => { this.subscriptionId
assert.equal(err, null) )
limitReached.should.equal(true) expect(limitReached).to.be.true
done()
}
)
}) })
}) })
}) })