mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 14:43:46 +01:00
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:
@@ -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()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user