mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-02 05:41:33 +02:00
[web] Migrate some User and UserMembership files to ESM (#29181)
* Rename files to mjs * Migrate files to ESM * Fix imports * Misc. fixes: Fixup InsititutionsAPI import, ObjectId import, ... * Rename test files to mjs * Convert test files to ESM * Fix tests * Update UserMembershipErrors imports * Convert some tests: sinon -> vitest * Fixup UserMembershipHandler.test.mjs * Convert UserMembershipErrors to ESM GitOrigin-RevId: 05d34c7e112a567f9c59398740ae0830ef93d32f
This commit is contained in:
@@ -7,8 +7,8 @@ import UserDeleter from './UserDeleter.js'
|
||||
import UserGetter from './UserGetter.js'
|
||||
import UserUpdater from './UserUpdater.js'
|
||||
import Analytics from '../Analytics/AnalyticsManager.js'
|
||||
import UserOnboardingEmailManager from './UserOnboardingEmailManager.js'
|
||||
import UserPostRegistrationAnalyticsManager from './UserPostRegistrationAnalyticsManager.js'
|
||||
import UserOnboardingEmailManager from './UserOnboardingEmailManager.mjs'
|
||||
import UserPostRegistrationAnalyticsManager from './UserPostRegistrationAnalyticsManager.mjs'
|
||||
import OError from '@overleaf/o-error'
|
||||
|
||||
async function _addAffiliation(user, affiliationOptions) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const Queues = require('../../infrastructure/Queues')
|
||||
const EmailHandler = require('../Email/EmailHandler')
|
||||
const UserUpdater = require('./UserUpdater')
|
||||
const UserGetter = require('./UserGetter')
|
||||
const Settings = require('@overleaf/settings')
|
||||
import Queues from '../../infrastructure/Queues.js'
|
||||
import EmailHandler from '../Email/EmailHandler.js'
|
||||
import UserUpdater from './UserUpdater.js'
|
||||
import UserGetter from './UserGetter.js'
|
||||
import Settings from '@overleaf/settings'
|
||||
|
||||
const ONE_DAY_MS = 24 * 60 * 60 * 1000
|
||||
|
||||
@@ -26,4 +26,4 @@ async function sendOnboardingEmail(userId) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { scheduleOnboardingEmail, sendOnboardingEmail }
|
||||
export default { scheduleOnboardingEmail, sendOnboardingEmail }
|
||||
@@ -1,9 +1,7 @@
|
||||
const Queues = require('../../infrastructure/Queues')
|
||||
const UserGetter = require('./UserGetter')
|
||||
const {
|
||||
promises: InstitutionsAPIPromises,
|
||||
} = require('../Institutions/InstitutionsAPI')
|
||||
const AnalyticsManager = require('../Analytics/AnalyticsManager')
|
||||
import Queues from '../../infrastructure/Queues.js'
|
||||
import UserGetter from './UserGetter.js'
|
||||
import InstitutionsAPI from '../Institutions/InstitutionsAPI.js'
|
||||
import AnalyticsManager from '../Analytics/AnalyticsManager.js'
|
||||
|
||||
const ONE_DAY_MS = 24 * 60 * 60 * 1000
|
||||
|
||||
@@ -25,7 +23,7 @@ async function postRegistrationAnalytics(userId) {
|
||||
|
||||
async function checkAffiliations(userId) {
|
||||
const affiliationsData =
|
||||
await InstitutionsAPIPromises.getUserAffiliations(userId)
|
||||
await InstitutionsAPI.promises.getUserAffiliations(userId)
|
||||
const hasCommonsAccountAffiliation = affiliationsData.some(
|
||||
affiliationData =>
|
||||
affiliationData.institution && affiliationData.institution.commonsAccount
|
||||
@@ -40,7 +38,7 @@ async function checkAffiliations(userId) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
schedulePostRegistrationAnalytics,
|
||||
postRegistrationAnalytics,
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
const {
|
||||
import {
|
||||
hasAdminCapability,
|
||||
hasAdminAccess,
|
||||
} = require('../Helpers/AdminAuthorizationHelper')
|
||||
const SessionManager = require('../Authentication/SessionManager')
|
||||
const Settings = require('@overleaf/settings')
|
||||
} from '../Helpers/AdminAuthorizationHelper.js'
|
||||
import SessionManager from '../Authentication/SessionManager.js'
|
||||
import Settings from '@overleaf/settings'
|
||||
|
||||
const UserMembershipAuthorization = {
|
||||
hasStaffAccess(requiredStaffAccess) {
|
||||
@@ -52,4 +52,4 @@ const UserMembershipAuthorization = {
|
||||
}
|
||||
},
|
||||
}
|
||||
module.exports = UserMembershipAuthorization
|
||||
export default UserMembershipAuthorization
|
||||
@@ -1,20 +1,16 @@
|
||||
import SessionManager from '../Authentication/SessionManager.js'
|
||||
import UserMembershipHandler from './UserMembershipHandler.js'
|
||||
import UserMembershipHandler from './UserMembershipHandler.mjs'
|
||||
import Errors from '../Errors/Errors.js'
|
||||
import EmailHelper from '../Helpers/EmailHelper.js'
|
||||
import { csvAttachment } from '../../infrastructure/Response.js'
|
||||
import {
|
||||
UserIsManagerError,
|
||||
UserAlreadyAddedError,
|
||||
UserNotFoundError,
|
||||
} from './UserMembershipErrors.js'
|
||||
import UserMembershipErrors from './UserMembershipErrors.mjs'
|
||||
import { SSOConfig } from '../../models/SSOConfig.js'
|
||||
import { Parser as CSVParser } from 'json2csv'
|
||||
import { expressify } from '@overleaf/promise-utils'
|
||||
import PlansLocator from '../Subscription/PlansLocator.js'
|
||||
import RecurlyClient from '../Subscription/RecurlyClient.js'
|
||||
import Modules from '../../infrastructure/Modules.js'
|
||||
import UserMembershipAuthorization from './UserMembershipAuthorization.js'
|
||||
import UserMembershipAuthorization from './UserMembershipAuthorization.mjs'
|
||||
|
||||
async function manageGroupMembers(req, res, next) {
|
||||
const { entity: subscription, entityConfig } = req
|
||||
@@ -201,7 +197,10 @@ export default {
|
||||
entityConfig,
|
||||
email,
|
||||
function (error, user) {
|
||||
if (error && error instanceof UserAlreadyAddedError) {
|
||||
if (
|
||||
error &&
|
||||
error instanceof UserMembershipErrors.UserAlreadyAddedError
|
||||
) {
|
||||
return res.status(400).json({
|
||||
error: {
|
||||
code: 'user_already_added',
|
||||
@@ -209,7 +208,7 @@ export default {
|
||||
},
|
||||
})
|
||||
}
|
||||
if (error && error instanceof UserNotFoundError) {
|
||||
if (error && error instanceof UserMembershipErrors.UserNotFoundError) {
|
||||
return res.status(404).json({
|
||||
error: {
|
||||
code: 'user_not_found',
|
||||
@@ -247,7 +246,7 @@ export default {
|
||||
entityConfig,
|
||||
userId,
|
||||
function (error, user) {
|
||||
if (error && error instanceof UserIsManagerError) {
|
||||
if (error && error instanceof UserMembershipErrors.UserIsManagerError) {
|
||||
return res.status(400).json({
|
||||
error: {
|
||||
code: 'managers_cannot_remove_admin',
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
const OError = require('@overleaf/o-error')
|
||||
import OError from '@overleaf/o-error'
|
||||
|
||||
class UserIsManagerError extends OError {}
|
||||
class UserNotFoundError extends OError {}
|
||||
class UserAlreadyAddedError extends OError {}
|
||||
|
||||
module.exports = {
|
||||
const UserMembershipErrors = {
|
||||
UserIsManagerError,
|
||||
UserNotFoundError,
|
||||
UserAlreadyAddedError,
|
||||
}
|
||||
|
||||
export default UserMembershipErrors
|
||||
@@ -1,17 +1,15 @@
|
||||
const { ObjectId } = require('mongodb-legacy')
|
||||
const { promisifyAll, callbackify } = require('@overleaf/promise-utils')
|
||||
const EntityModels = {
|
||||
Institution: require('../../models/Institution').Institution,
|
||||
Subscription: require('../../models/Subscription').Subscription,
|
||||
Publisher: require('../../models/Publisher').Publisher,
|
||||
}
|
||||
const UserMembershipViewModel = require('./UserMembershipViewModel')
|
||||
const UserGetter = require('../User/UserGetter')
|
||||
const {
|
||||
UserIsManagerError,
|
||||
UserNotFoundError,
|
||||
UserAlreadyAddedError,
|
||||
} = require('./UserMembershipErrors')
|
||||
import mongodb from 'mongodb-legacy'
|
||||
import { promisifyAll, callbackify } from '@overleaf/promise-utils'
|
||||
import { Institution } from '../../models/Institution.js'
|
||||
import { Subscription } from '../../models/Subscription.js'
|
||||
import { Publisher } from '../../models/Publisher.js'
|
||||
import UserMembershipViewModel from './UserMembershipViewModel.mjs'
|
||||
import UserGetter from '../User/UserGetter.js'
|
||||
import UserMembershipErrors from './UserMembershipErrors.mjs'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
const EntityModels = { Institution, Subscription, Publisher }
|
||||
|
||||
const UserMembershipHandler = {
|
||||
async getEntityWithoutAuthorizationCheck(entityId, entityConfig) {
|
||||
@@ -34,11 +32,11 @@ const UserMembershipHandler = {
|
||||
const user = await UserGetter.promises.getUserByAnyEmail(email)
|
||||
|
||||
if (!user) {
|
||||
throw new UserNotFoundError()
|
||||
throw new UserMembershipErrors.UserNotFoundError()
|
||||
}
|
||||
|
||||
if (entity[attribute].some(managerId => managerId.equals(user._id))) {
|
||||
throw new UserAlreadyAddedError()
|
||||
throw new UserMembershipErrors.UserAlreadyAddedError()
|
||||
}
|
||||
|
||||
await addUserToEntity(entity, attribute, user)
|
||||
@@ -48,14 +46,15 @@ const UserMembershipHandler = {
|
||||
async removeUser(entity, entityConfig, userId) {
|
||||
const attribute = entityConfig.fields.write
|
||||
if (entity.admin_id ? entity.admin_id.equals(userId) : undefined) {
|
||||
throw new UserIsManagerError()
|
||||
throw new UserMembershipErrors.UserIsManagerError()
|
||||
}
|
||||
return await removeUserFromEntity(entity, attribute, userId)
|
||||
},
|
||||
}
|
||||
|
||||
UserMembershipHandler.promises = promisifyAll(UserMembershipHandler)
|
||||
module.exports = {
|
||||
|
||||
export default {
|
||||
getEntityWithoutAuthorizationCheck: callbackify(
|
||||
UserMembershipHandler.getEntityWithoutAuthorizationCheck
|
||||
),
|
||||
@@ -1,16 +1,17 @@
|
||||
// @ts-check
|
||||
|
||||
const { expressify } = require('@overleaf/promise-utils')
|
||||
const async = require('async')
|
||||
const UserMembershipAuthorization = require('./UserMembershipAuthorization')
|
||||
const AuthenticationController = require('../Authentication/AuthenticationController')
|
||||
const UserMembershipHandler = require('./UserMembershipHandler')
|
||||
const EntityConfigs = require('./UserMembershipEntityConfigs')
|
||||
const Errors = require('../Errors/Errors')
|
||||
const HttpErrorHandler = require('../Errors/HttpErrorHandler')
|
||||
const TemplatesManager = require('../Templates/TemplatesManager')
|
||||
const { z, zz, validateReq } = require('../../infrastructure/Validation')
|
||||
const { useAdminCapabilities } = require('../Helpers/AdminAuthorizationHelper')
|
||||
import { expressify } from '@overleaf/promise-utils'
|
||||
|
||||
import async from 'async'
|
||||
import UserMembershipAuthorization from './UserMembershipAuthorization.mjs'
|
||||
import AuthenticationController from '../Authentication/AuthenticationController.js'
|
||||
import UserMembershipHandler from './UserMembershipHandler.mjs'
|
||||
import EntityConfigs from './UserMembershipEntityConfigs.js'
|
||||
import Errors from '../Errors/Errors.js'
|
||||
import HttpErrorHandler from '../Errors/HttpErrorHandler.js'
|
||||
import TemplatesManager from '../Templates/TemplatesManager.js'
|
||||
import { z, zz, validateReq } from '../../infrastructure/Validation.js'
|
||||
import { useAdminCapabilities } from '../Helpers/AdminAuthorizationHelper.js'
|
||||
|
||||
// set of middleware arrays or functions that checks user access to an entity
|
||||
// (publisher, institution, group, template, etc.)
|
||||
@@ -234,7 +235,7 @@ const UserMembershipMiddleware = {
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = UserMembershipMiddleware
|
||||
export default UserMembershipMiddleware
|
||||
|
||||
// fetch entity config and set it in the request
|
||||
function fetchEntityConfig(entityName) {
|
||||
@@ -1,4 +1,4 @@
|
||||
import UserMembershipMiddleware from './UserMembershipMiddleware.js'
|
||||
import UserMembershipMiddleware from './UserMembershipMiddleware.mjs'
|
||||
import UserMembershipController from './UserMembershipController.mjs'
|
||||
import SubscriptionGroupController from '../Subscription/SubscriptionGroupController.mjs'
|
||||
import TeamInvitesController from '../Subscription/TeamInvitesController.mjs'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const UserGetter = require('../User/UserGetter')
|
||||
const { isObjectIdInstance } = require('../Helpers/Mongo')
|
||||
import UserGetter from '../User/UserGetter.js'
|
||||
import { isObjectIdInstance } from '../Helpers/Mongo.js'
|
||||
|
||||
const UserMembershipViewModel = {
|
||||
build(userOrEmail) {
|
||||
@@ -81,4 +81,4 @@ UserMembershipViewModel.promises = {
|
||||
buildAsync: UserMembershipViewModel.buildAsync,
|
||||
}
|
||||
|
||||
module.exports = UserMembershipViewModel
|
||||
export default UserMembershipViewModel
|
||||
@@ -1,7 +1,7 @@
|
||||
import Features from './Features.js'
|
||||
import Queues from './Queues.js'
|
||||
import UserOnboardingEmailManager from '../Features/User/UserOnboardingEmailManager.js'
|
||||
import UserPostRegistrationAnalyticsManager from '../Features/User/UserPostRegistrationAnalyticsManager.js'
|
||||
import UserOnboardingEmailManager from '../Features/User/UserOnboardingEmailManager.mjs'
|
||||
import UserPostRegistrationAnalyticsManager from '../Features/User/UserPostRegistrationAnalyticsManager.mjs'
|
||||
import FeaturesUpdater from '../Features/Subscription/FeaturesUpdater.js'
|
||||
|
||||
import {
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { vi, expect } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
|
||||
const MODULE_PATH =
|
||||
'../../../../app/src/Features/User/UserOnboardingEmailManager'
|
||||
|
||||
describe('UserOnboardingEmailManager', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.fakeUserId = '123abc'
|
||||
ctx.fakeUserEmail = 'frog@overleaf.com'
|
||||
ctx.onboardingEmailsQueue = {
|
||||
add: sinon.stub().resolves(),
|
||||
process: callback => {
|
||||
ctx.queueProcessFunction = callback
|
||||
},
|
||||
}
|
||||
ctx.Queues = {
|
||||
createScheduledJob: sinon.stub().resolves(),
|
||||
}
|
||||
ctx.UserGetter = {
|
||||
promises: {
|
||||
getUser: sinon.stub().resolves(null),
|
||||
},
|
||||
}
|
||||
ctx.UserGetter.promises.getUser.withArgs({ _id: ctx.fakeUserId }).resolves({
|
||||
_id: ctx.fakeUserId,
|
||||
email: ctx.fakeUserEmail,
|
||||
})
|
||||
ctx.EmailHandler = {
|
||||
promises: {
|
||||
sendEmail: sinon.stub().resolves(),
|
||||
},
|
||||
}
|
||||
ctx.UserUpdater = {
|
||||
promises: {
|
||||
updateUser: sinon.stub().resolves(),
|
||||
},
|
||||
}
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/Queues', () => ({
|
||||
default: ctx.Queues,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/Email/EmailHandler', () => ({
|
||||
default: ctx.EmailHandler,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/User/UserGetter', () => ({
|
||||
default: ctx.UserGetter,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/User/UserUpdater', () => ({
|
||||
default: ctx.UserUpdater,
|
||||
}))
|
||||
|
||||
vi.doMock('@overleaf/settings', () => ({
|
||||
default: (ctx.Settings = {
|
||||
enableOnboardingEmails: true,
|
||||
}),
|
||||
}))
|
||||
|
||||
ctx.UserOnboardingEmailManager = (await import(MODULE_PATH)).default
|
||||
})
|
||||
|
||||
describe('scheduleOnboardingEmail', function () {
|
||||
it('should schedule delayed job on queue', async function (ctx) {
|
||||
await ctx.UserOnboardingEmailManager.scheduleOnboardingEmail({
|
||||
_id: ctx.fakeUserId,
|
||||
})
|
||||
sinon.assert.calledWith(
|
||||
ctx.Queues.createScheduledJob,
|
||||
'emails-onboarding',
|
||||
{ data: { userId: ctx.fakeUserId } },
|
||||
24 * 60 * 60 * 1000
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('sendOnboardingEmail', function () {
|
||||
describe('when onboarding emails are disabled', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.Settings.enableOnboardingEmails = false
|
||||
})
|
||||
it('should not send onboarding email', async function (ctx) {
|
||||
await ctx.UserOnboardingEmailManager.sendOnboardingEmail(ctx.fakeUserId)
|
||||
expect(ctx.EmailHandler.promises.sendEmail).not.to.have.been.called
|
||||
expect(ctx.UserUpdater.promises.updateUser).not.to.have.been.called
|
||||
})
|
||||
})
|
||||
describe('when onboarding emails are enabled', function () {
|
||||
it('should send onboarding email and update user', async function (ctx) {
|
||||
await ctx.UserOnboardingEmailManager.sendOnboardingEmail(ctx.fakeUserId)
|
||||
expect(ctx.EmailHandler.promises.sendEmail).to.have.been.calledWith(
|
||||
'userOnboardingEmail',
|
||||
{
|
||||
to: ctx.fakeUserEmail,
|
||||
}
|
||||
)
|
||||
expect(ctx.UserUpdater.promises.updateUser).to.have.been.calledWith(
|
||||
ctx.fakeUserId,
|
||||
{ $set: { onboardingEmailSentAt: sinon.match.date } }
|
||||
)
|
||||
})
|
||||
|
||||
it('should stop if user is not found', async function (ctx) {
|
||||
await ctx.UserOnboardingEmailManager.sendOnboardingEmail({
|
||||
data: { userId: 'deleted-user' },
|
||||
})
|
||||
expect(ctx.EmailHandler.promises.sendEmail).not.to.have.been.called
|
||||
expect(ctx.UserUpdater.promises.updateUser).not.to.have.been.called
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,112 +0,0 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const path = require('path')
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
|
||||
const MODULE_PATH = path.join(
|
||||
__dirname,
|
||||
'../../../../app/src/Features/User/UserOnboardingEmailManager'
|
||||
)
|
||||
|
||||
describe('UserOnboardingEmailManager', function () {
|
||||
beforeEach(function () {
|
||||
this.fakeUserId = '123abc'
|
||||
this.fakeUserEmail = 'frog@overleaf.com'
|
||||
this.onboardingEmailsQueue = {
|
||||
add: sinon.stub().resolves(),
|
||||
process: callback => {
|
||||
this.queueProcessFunction = callback
|
||||
},
|
||||
}
|
||||
this.Queues = {
|
||||
createScheduledJob: sinon.stub().resolves(),
|
||||
}
|
||||
this.UserGetter = {
|
||||
promises: {
|
||||
getUser: sinon.stub().resolves(null),
|
||||
},
|
||||
}
|
||||
this.UserGetter.promises.getUser
|
||||
.withArgs({ _id: this.fakeUserId })
|
||||
.resolves({
|
||||
_id: this.fakeUserId,
|
||||
email: this.fakeUserEmail,
|
||||
})
|
||||
this.EmailHandler = {
|
||||
promises: {
|
||||
sendEmail: sinon.stub().resolves(),
|
||||
},
|
||||
}
|
||||
this.UserUpdater = {
|
||||
promises: {
|
||||
updateUser: sinon.stub().resolves(),
|
||||
},
|
||||
}
|
||||
|
||||
this.UserOnboardingEmailManager = SandboxedModule.require(MODULE_PATH, {
|
||||
requires: {
|
||||
'../../infrastructure/Queues': this.Queues,
|
||||
'../Email/EmailHandler': this.EmailHandler,
|
||||
'./UserGetter': this.UserGetter,
|
||||
'./UserUpdater': this.UserUpdater,
|
||||
'@overleaf/settings': (this.Settings = {
|
||||
enableOnboardingEmails: true,
|
||||
}),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('scheduleOnboardingEmail', function () {
|
||||
it('should schedule delayed job on queue', async function () {
|
||||
await this.UserOnboardingEmailManager.scheduleOnboardingEmail({
|
||||
_id: this.fakeUserId,
|
||||
})
|
||||
sinon.assert.calledWith(
|
||||
this.Queues.createScheduledJob,
|
||||
'emails-onboarding',
|
||||
{ data: { userId: this.fakeUserId } },
|
||||
24 * 60 * 60 * 1000
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('sendOnboardingEmail', function () {
|
||||
describe('when onboarding emails are disabled', function () {
|
||||
beforeEach(function () {
|
||||
this.Settings.enableOnboardingEmails = false
|
||||
})
|
||||
it('should not send onboarding email', async function () {
|
||||
await this.UserOnboardingEmailManager.sendOnboardingEmail(
|
||||
this.fakeUserId
|
||||
)
|
||||
expect(this.EmailHandler.promises.sendEmail).not.to.have.been.called
|
||||
expect(this.UserUpdater.promises.updateUser).not.to.have.been.called
|
||||
})
|
||||
})
|
||||
describe('when onboarding emails are enabled', function () {
|
||||
it('should send onboarding email and update user', async function () {
|
||||
await this.UserOnboardingEmailManager.sendOnboardingEmail(
|
||||
this.fakeUserId
|
||||
)
|
||||
expect(this.EmailHandler.promises.sendEmail).to.have.been.calledWith(
|
||||
'userOnboardingEmail',
|
||||
{
|
||||
to: this.fakeUserEmail,
|
||||
}
|
||||
)
|
||||
expect(this.UserUpdater.promises.updateUser).to.have.been.calledWith(
|
||||
this.fakeUserId,
|
||||
{ $set: { onboardingEmailSentAt: sinon.match.date } }
|
||||
)
|
||||
})
|
||||
|
||||
it('should stop if user is not found', async function () {
|
||||
await this.UserOnboardingEmailManager.sendOnboardingEmail({
|
||||
data: { userId: 'deleted-user' },
|
||||
})
|
||||
expect(this.EmailHandler.promises.sendEmail).not.to.have.been.called
|
||||
expect(this.UserUpdater.promises.updateUser).not.to.have.been.called
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,125 @@
|
||||
import { vi, expect } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
|
||||
const MODULE_PATH =
|
||||
'../../../../app/src/Features/User/UserPostRegistrationAnalyticsManager'
|
||||
|
||||
describe('UserPostRegistrationAnalyticsManager', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.fakeUserId = '123abc'
|
||||
ctx.Queues = {
|
||||
createScheduledJob: sinon.stub().resolves(),
|
||||
}
|
||||
ctx.UserGetter = {
|
||||
promises: {
|
||||
getUser: sinon.stub().resolves(),
|
||||
},
|
||||
}
|
||||
ctx.UserGetter.promises.getUser
|
||||
.withArgs({ _id: ctx.fakeUserId })
|
||||
.resolves({ _id: ctx.fakeUserId })
|
||||
ctx.InstitutionsAPI = {
|
||||
promises: {
|
||||
getUserAffiliations: sinon.stub().resolves([]),
|
||||
},
|
||||
}
|
||||
ctx.AnalyticsManager = {
|
||||
setUserPropertyForUser: sinon.stub().resolves(),
|
||||
}
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/Queues', () => ({
|
||||
default: ctx.Queues,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/User/UserGetter', () => ({
|
||||
default: ctx.UserGetter,
|
||||
}))
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/Institutions/InstitutionsAPI',
|
||||
() => ({
|
||||
default: ctx.InstitutionsAPI,
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/Analytics/AnalyticsManager',
|
||||
() => ({
|
||||
default: ctx.AnalyticsManager,
|
||||
})
|
||||
)
|
||||
|
||||
ctx.UserPostRegistrationAnalyticsManager = (
|
||||
await import(MODULE_PATH)
|
||||
).default
|
||||
})
|
||||
|
||||
describe('schedulePostRegistrationAnalytics', function () {
|
||||
it('should schedule delayed job on queue', async function (ctx) {
|
||||
await ctx.UserPostRegistrationAnalyticsManager.schedulePostRegistrationAnalytics(
|
||||
{
|
||||
_id: ctx.fakeUserId,
|
||||
}
|
||||
)
|
||||
sinon.assert.calledWith(
|
||||
ctx.Queues.createScheduledJob,
|
||||
'post-registration-analytics',
|
||||
{ data: { userId: ctx.fakeUserId } },
|
||||
24 * 60 * 60 * 1000
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('postRegistrationAnalytics', function () {
|
||||
it('stops without errors if user is not found', async function (ctx) {
|
||||
await ctx.UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(
|
||||
'not-a-user'
|
||||
)
|
||||
expect(ctx.InstitutionsAPI.promises.getUserAffiliations).not.to.have.been
|
||||
.called
|
||||
expect(ctx.AnalyticsManager.setUserPropertyForUser).not.to.have.been
|
||||
.called
|
||||
})
|
||||
|
||||
it('sets user property if user has commons account affiliationd', async function (ctx) {
|
||||
ctx.InstitutionsAPI.promises.getUserAffiliations.resolves([
|
||||
{},
|
||||
{
|
||||
institution: {
|
||||
commonsAccount: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
institution: {
|
||||
commonsAccount: false,
|
||||
},
|
||||
},
|
||||
])
|
||||
await ctx.UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(
|
||||
ctx.fakeUserId
|
||||
)
|
||||
expect(
|
||||
ctx.AnalyticsManager.setUserPropertyForUser
|
||||
).to.have.been.calledWith(
|
||||
ctx.fakeUserId,
|
||||
'registered-from-commons-account',
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('does not set user property if user has no commons account affiliation', async function (ctx) {
|
||||
ctx.InstitutionsAPI.promises.getUserAffiliations.resolves([
|
||||
{
|
||||
institution: {
|
||||
commonsAccount: false,
|
||||
},
|
||||
},
|
||||
])
|
||||
await ctx.UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(
|
||||
ctx.fakeUserId
|
||||
)
|
||||
expect(ctx.AnalyticsManager.setUserPropertyForUser).not.to.have.been
|
||||
.called
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,114 +0,0 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const path = require('path')
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
|
||||
const MODULE_PATH = path.join(
|
||||
__dirname,
|
||||
'../../../../app/src/Features/User/UserPostRegistrationAnalyticsManager'
|
||||
)
|
||||
|
||||
describe('UserPostRegistrationAnalyticsManager', function () {
|
||||
beforeEach(function () {
|
||||
this.fakeUserId = '123abc'
|
||||
this.Queues = {
|
||||
createScheduledJob: sinon.stub().resolves(),
|
||||
}
|
||||
this.UserGetter = {
|
||||
promises: {
|
||||
getUser: sinon.stub().resolves(),
|
||||
},
|
||||
}
|
||||
this.UserGetter.promises.getUser
|
||||
.withArgs({ _id: this.fakeUserId })
|
||||
.resolves({ _id: this.fakeUserId })
|
||||
this.InstitutionsAPI = {
|
||||
promises: {
|
||||
getUserAffiliations: sinon.stub().resolves([]),
|
||||
},
|
||||
}
|
||||
this.AnalyticsManager = {
|
||||
setUserPropertyForUser: sinon.stub().resolves(),
|
||||
}
|
||||
this.UserPostRegistrationAnalyticsManager = SandboxedModule.require(
|
||||
MODULE_PATH,
|
||||
{
|
||||
requires: {
|
||||
'../../infrastructure/Queues': this.Queues,
|
||||
'./UserGetter': this.UserGetter,
|
||||
'../Institutions/InstitutionsAPI': this.InstitutionsAPI,
|
||||
'../Analytics/AnalyticsManager': this.AnalyticsManager,
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
describe('schedulePostRegistrationAnalytics', function () {
|
||||
it('should schedule delayed job on queue', async function () {
|
||||
await this.UserPostRegistrationAnalyticsManager.schedulePostRegistrationAnalytics(
|
||||
{
|
||||
_id: this.fakeUserId,
|
||||
}
|
||||
)
|
||||
sinon.assert.calledWith(
|
||||
this.Queues.createScheduledJob,
|
||||
'post-registration-analytics',
|
||||
{ data: { userId: this.fakeUserId } },
|
||||
24 * 60 * 60 * 1000
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('postRegistrationAnalytics', function () {
|
||||
it('stops without errors if user is not found', async function () {
|
||||
await this.UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(
|
||||
'not-a-user'
|
||||
)
|
||||
expect(this.InstitutionsAPI.promises.getUserAffiliations).not.to.have.been
|
||||
.called
|
||||
expect(this.AnalyticsManager.setUserPropertyForUser).not.to.have.been
|
||||
.called
|
||||
})
|
||||
|
||||
it('sets user property if user has commons account affiliationd', async function () {
|
||||
this.InstitutionsAPI.promises.getUserAffiliations.resolves([
|
||||
{},
|
||||
{
|
||||
institution: {
|
||||
commonsAccount: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
institution: {
|
||||
commonsAccount: false,
|
||||
},
|
||||
},
|
||||
])
|
||||
await this.UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(
|
||||
this.fakeUserId
|
||||
)
|
||||
expect(
|
||||
this.AnalyticsManager.setUserPropertyForUser
|
||||
).to.have.been.calledWith(
|
||||
this.fakeUserId,
|
||||
'registered-from-commons-account',
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('does not set user property if user has no commons account affiliation', async function () {
|
||||
this.InstitutionsAPI.promises.getUserAffiliations.resolves([
|
||||
{
|
||||
institution: {
|
||||
commonsAccount: false,
|
||||
},
|
||||
},
|
||||
])
|
||||
await this.UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(
|
||||
this.fakeUserId
|
||||
)
|
||||
expect(this.AnalyticsManager.setUserPropertyForUser).not.to.have.been
|
||||
.called
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,25 +1,18 @@
|
||||
import { expect, vi } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
import MockRequest from '../helpers/MockRequest.js'
|
||||
import MockResponse from '../helpers/MockResponse.js'
|
||||
import { expect, vi, describe, it, beforeEach } from 'vitest'
|
||||
import MockRequest from '../helpers/MockRequestVitest.mjs'
|
||||
import MockResponse from '../helpers/MockResponseVitest.mjs'
|
||||
import EntityConfigs from '../../../../app/src/Features/UserMembership/UserMembershipEntityConfigs.js'
|
||||
import Errors from '../../../../app/src/Features/Errors/Errors.js'
|
||||
import {
|
||||
UserIsManagerError,
|
||||
UserNotFoundError,
|
||||
UserAlreadyAddedError,
|
||||
} from '../../../../app/src/Features/UserMembership/UserMembershipErrors.js'
|
||||
|
||||
const assertCalledWith = sinon.assert.calledWith
|
||||
import UserMembershipErrors from '../../../../app/src/Features/UserMembership/UserMembershipErrors.mjs'
|
||||
|
||||
const modulePath =
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipController.mjs'
|
||||
|
||||
vi.mock(
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipErrors.js',
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipErrors.mjs',
|
||||
() =>
|
||||
vi.importActual(
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipErrors.js'
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipErrors.mjs'
|
||||
)
|
||||
)
|
||||
|
||||
@@ -27,9 +20,9 @@ vi.mock('../../../../app/src/Features/Errors/Errors.js', () =>
|
||||
vi.importActual('../../../../app/src/Features/Errors/Errors.js')
|
||||
)
|
||||
|
||||
describe('UserMembershipController', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.req = new MockRequest()
|
||||
describe('UserMembershipController', () => {
|
||||
beforeEach(async ctx => {
|
||||
ctx.req = new MockRequest(vi)
|
||||
ctx.req.params.id = 'mock-entity-id'
|
||||
ctx.user = { _id: 'mock-user-id' }
|
||||
ctx.newUser = { _id: 'mock-new-user-id', email: 'new-user-email@foo.bar' }
|
||||
@@ -37,16 +30,16 @@ describe('UserMembershipController', function () {
|
||||
_id: 'mock-subscription-id',
|
||||
admin_id: 'mock-admin-id',
|
||||
manager_ids: ['mock-admin-id'],
|
||||
fetchV1Data: callback => callback(null, ctx.subscription),
|
||||
fetchV1Data: vi.fn(callback => callback(null, ctx.subscription)),
|
||||
}
|
||||
ctx.institution = {
|
||||
_id: 'mock-institution-id',
|
||||
v1Id: 123,
|
||||
fetchV1Data: callback => {
|
||||
const institution = Object.assign({}, ctx.institution)
|
||||
fetchV1Data: vi.fn(callback => {
|
||||
const institution = { ...ctx.institution }
|
||||
institution.name = 'Test Institution Name'
|
||||
callback(null, institution)
|
||||
},
|
||||
}),
|
||||
managerIds: ['mock-member-id-1'],
|
||||
}
|
||||
ctx.users = [
|
||||
@@ -113,45 +106,48 @@ describe('UserMembershipController', function () {
|
||||
}
|
||||
|
||||
ctx.SessionManager = {
|
||||
getSessionUser: sinon.stub().returns(ctx.user),
|
||||
getLoggedInUserId: sinon.stub().returns(ctx.user._id),
|
||||
getSessionUser: vi.fn().mockReturnValue(ctx.user),
|
||||
getLoggedInUserId: vi.fn().mockReturnValue(ctx.user._id),
|
||||
}
|
||||
ctx.SSOConfig = {
|
||||
findById: sinon
|
||||
.stub()
|
||||
.returns({ exec: sinon.stub().resolves({ enabled: true }) }),
|
||||
findById: vi.fn().mockReturnValue({
|
||||
exec: vi.fn().mockResolvedValue({ enabled: true }),
|
||||
}),
|
||||
}
|
||||
ctx.UserMembershipHandler = {
|
||||
getEntity: sinon.stub().yields(null, ctx.subscription),
|
||||
createEntity: sinon.stub().yields(null, ctx.institution),
|
||||
getUsers: sinon.stub().yields(null, ctx.users),
|
||||
addUser: sinon.stub().yields(null, ctx.newUser),
|
||||
removeUser: sinon.stub().yields(null),
|
||||
getEntity: vi.fn((_entity, _options, callback) =>
|
||||
callback(null, ctx.subscription)
|
||||
),
|
||||
createEntity: vi.fn((_entity, _options, callback) =>
|
||||
callback(null, ctx.institution)
|
||||
),
|
||||
getUsers: vi.fn((_entity, _options, callback) =>
|
||||
callback(null, ctx.users)
|
||||
),
|
||||
addUser: vi.fn((_entity, _options, _email, callback) =>
|
||||
callback(null, ctx.newUser)
|
||||
),
|
||||
removeUser: vi.fn((_entity, _options, _userId, callback) =>
|
||||
callback(null)
|
||||
),
|
||||
promises: {
|
||||
getUsers: sinon.stub().resolves(ctx.users),
|
||||
getUsers: vi.fn().mockResolvedValue(ctx.users),
|
||||
},
|
||||
}
|
||||
ctx.SplitTestHandler = {
|
||||
promises: {
|
||||
getAssignment: sinon.stub().resolves({ variant: 'default' }),
|
||||
getAssignment: vi.fn().mockResolvedValue({ variant: 'default' }),
|
||||
},
|
||||
getAssignment: sinon.stub().yields(null, { variant: 'default' }),
|
||||
getAssignment: vi.fn((_testName, _userId, callback) =>
|
||||
callback(null, { variant: 'default' })
|
||||
),
|
||||
}
|
||||
ctx.RecurlyClient = {
|
||||
promises: {
|
||||
getSubscription: sinon.stub().resolves({}),
|
||||
getSubscription: vi.fn().mockResolvedValue({}),
|
||||
},
|
||||
}
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipErrors',
|
||||
() => ({
|
||||
UserIsManagerError,
|
||||
UserNotFoundError,
|
||||
UserAlreadyAddedError,
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/Authentication/SessionManager',
|
||||
() => ({
|
||||
@@ -191,7 +187,7 @@ describe('UserMembershipController', function () {
|
||||
ctx.Modules = {
|
||||
promises: {
|
||||
hooks: {
|
||||
fire: sinon.stub(),
|
||||
fire: vi.fn(),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -202,55 +198,90 @@ describe('UserMembershipController', function () {
|
||||
ctx.UserMembershipController = (await import(modulePath)).default
|
||||
})
|
||||
|
||||
describe('index', function () {
|
||||
beforeEach(function (ctx) {
|
||||
describe('index', () => {
|
||||
beforeEach(ctx => {
|
||||
ctx.req.user = ctx.user
|
||||
ctx.req.entity = ctx.subscription
|
||||
ctx.req.entityConfig = EntityConfigs.group
|
||||
ctx.Modules.promises.hooks.fire.resolves([])
|
||||
ctx.Modules.promises.hooks.fire.mockResolvedValue([])
|
||||
})
|
||||
|
||||
it('get users', async function (ctx) {
|
||||
await ctx.UserMembershipController.manageGroupMembers(ctx.req, {
|
||||
it('get users', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
UserMembershipHandler,
|
||||
subscription,
|
||||
}) => {
|
||||
expect.assertions(1)
|
||||
await UserMembershipController.manageGroupMembers(req, {
|
||||
render: () => {
|
||||
sinon.assert.calledWithMatch(
|
||||
ctx.UserMembershipHandler.promises.getUsers,
|
||||
ctx.subscription,
|
||||
{ modelName: 'Subscription' }
|
||||
expect(UserMembershipHandler.promises.getUsers).toHaveBeenCalledWith(
|
||||
subscription,
|
||||
{
|
||||
modelName: 'Subscription',
|
||||
baseQuery: { groupPlan: true },
|
||||
fields: {
|
||||
access: 'manager_ids',
|
||||
membership: 'member_ids',
|
||||
name: 'teamName',
|
||||
primaryKey: '_id',
|
||||
read: ['invited_emails', 'teamInvites', 'member_ids'],
|
||||
write: null,
|
||||
},
|
||||
hasMembersLimit: true,
|
||||
readOnly: true,
|
||||
}
|
||||
)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('render group view', async function (ctx) {
|
||||
ctx.subscription.managedUsersEnabled = false
|
||||
await ctx.UserMembershipController.manageGroupMembers(ctx.req, {
|
||||
it('render group view', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
subscription,
|
||||
users,
|
||||
}) => {
|
||||
expect.assertions(4)
|
||||
subscription.managedUsersEnabled = false
|
||||
await UserMembershipController.manageGroupMembers(req, {
|
||||
render: (viewPath, viewParams) => {
|
||||
expect(viewPath).to.equal('user_membership/group-members-react')
|
||||
expect(viewParams.users).to.deep.equal(ctx.users)
|
||||
expect(viewParams.groupSize).to.equal(ctx.subscription.membersLimit)
|
||||
expect(viewParams.users).to.deep.equal(users)
|
||||
expect(viewParams.groupSize).to.equal(subscription.membersLimit)
|
||||
expect(viewParams.managedUsersActive).to.equal(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('render group view with managed users', async function (ctx) {
|
||||
ctx.subscription.managedUsersEnabled = true
|
||||
await ctx.UserMembershipController.manageGroupMembers(ctx.req, {
|
||||
it('render group view with managed users', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
subscription,
|
||||
users,
|
||||
}) => {
|
||||
expect.assertions(5)
|
||||
subscription.managedUsersEnabled = true
|
||||
await UserMembershipController.manageGroupMembers(req, {
|
||||
render: (viewPath, viewParams) => {
|
||||
expect(viewPath).to.equal('user_membership/group-members-react')
|
||||
expect(viewParams.users).to.deep.equal(ctx.users)
|
||||
expect(viewParams.groupSize).to.equal(ctx.subscription.membersLimit)
|
||||
expect(viewParams.users).to.deep.equal(users)
|
||||
expect(viewParams.groupSize).to.equal(subscription.membersLimit)
|
||||
expect(viewParams.managedUsersActive).to.equal(true)
|
||||
expect(viewParams.isUserGroupManager).to.equal(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('render group managers view', async function (ctx) {
|
||||
ctx.req.user = ctx.user
|
||||
ctx.req.entityConfig = EntityConfigs.groupManagers
|
||||
await ctx.UserMembershipController.manageGroupManagers(ctx.req, {
|
||||
it('render group managers view', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
user,
|
||||
}) => {
|
||||
expect.assertions(2)
|
||||
req.user = user
|
||||
req.entityConfig = EntityConfigs.groupManagers
|
||||
await UserMembershipController.manageGroupManagers(req, {
|
||||
render: (viewPath, viewParams) => {
|
||||
expect(viewPath).to.equal('user_membership/group-managers-react')
|
||||
expect(viewParams.groupSize).to.equal(undefined)
|
||||
@@ -258,11 +289,17 @@ describe('UserMembershipController', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('render institution view', async function (ctx) {
|
||||
ctx.req.user = ctx.user
|
||||
ctx.req.entity = ctx.institution
|
||||
ctx.req.entityConfig = EntityConfigs.institution
|
||||
await ctx.UserMembershipController.manageInstitutionManagers(ctx.req, {
|
||||
it('render institution view', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
user,
|
||||
institution,
|
||||
}) => {
|
||||
expect.assertions(3)
|
||||
req.user = user
|
||||
req.entity = institution
|
||||
req.entityConfig = EntityConfigs.institution
|
||||
await UserMembershipController.manageInstitutionManagers(req, {
|
||||
render: (viewPath, viewParams) => {
|
||||
expect(viewPath).to.equal(
|
||||
'user_membership/institution-managers-react'
|
||||
@@ -274,22 +311,40 @@ describe('UserMembershipController', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('add', function () {
|
||||
beforeEach(function (ctx) {
|
||||
describe('add', () => {
|
||||
beforeEach(ctx => {
|
||||
ctx.req.body.email = ctx.newUser.email
|
||||
ctx.req.entity = ctx.subscription
|
||||
ctx.req.entityConfig = EntityConfigs.groupManagers
|
||||
})
|
||||
|
||||
it('add user', async function (ctx) {
|
||||
it('add user', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
UserMembershipHandler,
|
||||
subscription,
|
||||
newUser,
|
||||
}) => {
|
||||
expect.assertions(1)
|
||||
await new Promise(resolve => {
|
||||
ctx.UserMembershipController.add(ctx.req, {
|
||||
UserMembershipController.add(req, {
|
||||
json: () => {
|
||||
sinon.assert.calledWithMatch(
|
||||
ctx.UserMembershipHandler.addUser,
|
||||
ctx.subscription,
|
||||
{ modelName: 'Subscription' },
|
||||
ctx.newUser.email
|
||||
expect(UserMembershipHandler.addUser).toHaveBeenCalledWith(
|
||||
subscription,
|
||||
{
|
||||
modelName: 'Subscription',
|
||||
baseQuery: { groupPlan: true },
|
||||
fields: {
|
||||
access: 'manager_ids',
|
||||
membership: 'member_ids',
|
||||
name: 'teamName',
|
||||
primaryKey: '_id',
|
||||
read: ['manager_ids'],
|
||||
write: 'manager_ids',
|
||||
},
|
||||
},
|
||||
newUser.email,
|
||||
expect.any(Function)
|
||||
)
|
||||
resolve()
|
||||
},
|
||||
@@ -297,21 +352,27 @@ describe('UserMembershipController', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('return user object', async function (ctx) {
|
||||
it('return user object', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
newUser,
|
||||
}) => {
|
||||
expect.assertions(1)
|
||||
await new Promise(resolve => {
|
||||
ctx.UserMembershipController.add(ctx.req, {
|
||||
UserMembershipController.add(req, {
|
||||
json: payload => {
|
||||
payload.user.should.equal(ctx.newUser)
|
||||
expect(payload.user).to.equal(newUser)
|
||||
resolve()
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('handle readOnly entity', async function (ctx) {
|
||||
it('handle readOnly entity', async ({ UserMembershipController, req }) => {
|
||||
expect.assertions(2)
|
||||
req.entityConfig = EntityConfigs.group
|
||||
await new Promise(resolve => {
|
||||
ctx.req.entityConfig = EntityConfigs.group
|
||||
ctx.UserMembershipController.add(ctx.req, null, error => {
|
||||
UserMembershipController.add(req, null, error => {
|
||||
expect(error).to.exist
|
||||
expect(error).to.be.an.instanceof(Errors.NotFoundError)
|
||||
resolve()
|
||||
@@ -319,24 +380,47 @@ describe('UserMembershipController', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('handle user already added', async function (ctx) {
|
||||
it('handle user already added', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
UserMembershipHandler,
|
||||
}) => {
|
||||
expect.assertions(1)
|
||||
UserMembershipHandler.addUser.mockImplementation(
|
||||
(_entity, _options, _email, callback) => {
|
||||
callback(new UserMembershipErrors.UserAlreadyAddedError())
|
||||
}
|
||||
)
|
||||
await new Promise(resolve => {
|
||||
ctx.UserMembershipHandler.addUser.yields(new UserAlreadyAddedError())
|
||||
ctx.UserMembershipController.add(ctx.req, {
|
||||
status: () => ({
|
||||
json: payload => {
|
||||
expect(payload.error.code).to.equal('user_already_added')
|
||||
resolve()
|
||||
},
|
||||
}),
|
||||
})
|
||||
UserMembershipController.add(
|
||||
req,
|
||||
{
|
||||
status: () => ({
|
||||
json: payload => {
|
||||
expect(payload.error.code).to.equal('user_already_added')
|
||||
resolve()
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
() => {}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('handle user not found', async function (ctx) {
|
||||
it('handle user not found', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
UserMembershipHandler,
|
||||
}) => {
|
||||
expect.assertions(1)
|
||||
UserMembershipHandler.addUser.mockImplementation(
|
||||
(_entity, _options, _email, callback) => {
|
||||
callback(new UserMembershipErrors.UserNotFoundError())
|
||||
}
|
||||
)
|
||||
await new Promise(resolve => {
|
||||
ctx.UserMembershipHandler.addUser.yields(new UserNotFoundError())
|
||||
ctx.UserMembershipController.add(ctx.req, {
|
||||
UserMembershipController.add(req, {
|
||||
status: () => ({
|
||||
json: payload => {
|
||||
expect(payload.error.code).to.equal('user_not_found')
|
||||
@@ -347,10 +431,11 @@ describe('UserMembershipController', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('handle invalid email', async function (ctx) {
|
||||
it('handle invalid email', async ({ UserMembershipController, req }) => {
|
||||
expect.assertions(1)
|
||||
req.body.email = 'not_valid_email'
|
||||
await new Promise(resolve => {
|
||||
ctx.req.body.email = 'not_valid_email'
|
||||
ctx.UserMembershipController.add(ctx.req, {
|
||||
UserMembershipController.add(req, {
|
||||
status: () => ({
|
||||
json: payload => {
|
||||
expect(payload.error.code).to.equal('invalid_email')
|
||||
@@ -362,33 +447,52 @@ describe('UserMembershipController', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('remove', function () {
|
||||
beforeEach(function (ctx) {
|
||||
describe('remove', () => {
|
||||
beforeEach(ctx => {
|
||||
ctx.req.params.userId = ctx.newUser._id
|
||||
ctx.req.entity = ctx.subscription
|
||||
ctx.req.entityConfig = EntityConfigs.groupManagers
|
||||
})
|
||||
|
||||
it('remove user', async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.UserMembershipController.remove(ctx.req, {
|
||||
sendStatus: () => {
|
||||
sinon.assert.calledWithMatch(
|
||||
ctx.UserMembershipHandler.removeUser,
|
||||
ctx.subscription,
|
||||
{ modelName: 'Subscription' },
|
||||
ctx.newUser._id
|
||||
)
|
||||
resolve()
|
||||
},
|
||||
})
|
||||
it('remove user', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
UserMembershipHandler,
|
||||
subscription,
|
||||
newUser,
|
||||
}) => {
|
||||
expect.assertions(1)
|
||||
await UserMembershipController.remove(req, {
|
||||
sendStatus: () => {
|
||||
expect(UserMembershipHandler.removeUser).toHaveBeenCalledWith(
|
||||
subscription,
|
||||
{
|
||||
modelName: 'Subscription',
|
||||
|
||||
baseQuery: {
|
||||
groupPlan: true,
|
||||
},
|
||||
fields: {
|
||||
access: 'manager_ids',
|
||||
membership: 'member_ids',
|
||||
name: 'teamName',
|
||||
primaryKey: '_id',
|
||||
read: ['manager_ids'],
|
||||
write: 'manager_ids',
|
||||
},
|
||||
},
|
||||
newUser._id,
|
||||
expect.any(Function)
|
||||
)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('handle readOnly entity', async function (ctx) {
|
||||
it('handle readOnly entity', async ({ UserMembershipController, req }) => {
|
||||
expect.assertions(2)
|
||||
req.entityConfig = EntityConfigs.group
|
||||
await new Promise(resolve => {
|
||||
ctx.req.entityConfig = EntityConfigs.group
|
||||
ctx.UserMembershipController.remove(ctx.req, null, error => {
|
||||
UserMembershipController.remove(req, null, error => {
|
||||
expect(error).to.exist
|
||||
expect(error).to.be.an.instanceof(Errors.NotFoundError)
|
||||
resolve()
|
||||
@@ -396,172 +500,194 @@ describe('UserMembershipController', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('prevent self removal', async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.req.params.userId = ctx.user._id
|
||||
ctx.UserMembershipController.remove(ctx.req, {
|
||||
status: () => ({
|
||||
json: payload => {
|
||||
expect(payload.error.code).to.equal('managers_cannot_remove_self')
|
||||
resolve()
|
||||
},
|
||||
}),
|
||||
})
|
||||
it('prevent self removal', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
user,
|
||||
}) => {
|
||||
expect.assertions(1)
|
||||
req.params.userId = user._id
|
||||
await UserMembershipController.remove(req, {
|
||||
status: () => ({
|
||||
json: payload => {
|
||||
expect(payload.error.code).to.equal('managers_cannot_remove_self')
|
||||
},
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
it('prevent admin removal', async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.UserMembershipHandler.removeUser.yields(new UserIsManagerError())
|
||||
ctx.UserMembershipController.remove(ctx.req, {
|
||||
status: () => ({
|
||||
json: payload => {
|
||||
expect(payload.error.code).to.equal(
|
||||
'managers_cannot_remove_admin'
|
||||
)
|
||||
resolve()
|
||||
},
|
||||
}),
|
||||
})
|
||||
it('prevent admin removal', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
UserMembershipHandler,
|
||||
}) => {
|
||||
expect.assertions(1)
|
||||
UserMembershipHandler.removeUser.mockImplementation(
|
||||
(_entity, _options, _userId, callback) => {
|
||||
callback(new UserMembershipErrors.UserIsManagerError())
|
||||
}
|
||||
)
|
||||
await UserMembershipController.remove(req, {
|
||||
status: () => ({
|
||||
json: payload => {
|
||||
expect(payload.error.code).to.equal('managers_cannot_remove_admin')
|
||||
},
|
||||
}),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('exportCsv', function () {
|
||||
beforeEach(function (ctx) {
|
||||
describe('exportCsv', () => {
|
||||
beforeEach(ctx => {
|
||||
ctx.req.entity = ctx.subscription
|
||||
ctx.req.entityConfig = EntityConfigs.groupManagers
|
||||
ctx.res = new MockResponse()
|
||||
ctx.res = new MockResponse(vi)
|
||||
ctx.UserMembershipController.exportCsv(ctx.req, ctx.res)
|
||||
})
|
||||
|
||||
it('get users', function (ctx) {
|
||||
sinon.assert.calledWithMatch(
|
||||
ctx.UserMembershipHandler.promises.getUsers,
|
||||
ctx.subscription,
|
||||
{ modelName: 'Subscription' }
|
||||
it('get users', ({ UserMembershipHandler, subscription }) => {
|
||||
expect(UserMembershipHandler.promises.getUsers).toHaveBeenCalledWith(
|
||||
subscription,
|
||||
{
|
||||
modelName: 'Subscription',
|
||||
baseQuery: { groupPlan: true },
|
||||
fields: {
|
||||
access: 'manager_ids',
|
||||
membership: 'member_ids',
|
||||
name: 'teamName',
|
||||
primaryKey: '_id',
|
||||
read: ['manager_ids'],
|
||||
write: 'manager_ids',
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should set the correct content type on the request', function (ctx) {
|
||||
assertCalledWith(ctx.res.contentType, 'text/csv; charset=utf-8')
|
||||
it('should set the correct content type on the request', ({ res }) => {
|
||||
expect(res.contentType).toHaveBeenCalledWith('text/csv; charset=utf-8')
|
||||
})
|
||||
|
||||
it('should name the exported csv file', function (ctx) {
|
||||
assertCalledWith(
|
||||
ctx.res.header,
|
||||
it('should name the exported csv file', ({ res }) => {
|
||||
expect(res.header).toHaveBeenCalledWith(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="Group.csv"'
|
||||
)
|
||||
})
|
||||
|
||||
it('should export the correct csv', function (ctx) {
|
||||
assertCalledWith(
|
||||
ctx.res.send,
|
||||
it('should export the correct csv', ({ res }) => {
|
||||
expect(res.send).toHaveBeenCalledWith(
|
||||
'"email","last_logged_in_at","last_active_at"\n"mock-email-1@foo.com","2020-08-09T12:43:11.467Z","2021-08-09T12:43:11.467Z"\n"mock-email-2@foo.com","2020-05-20T10:41:11.407Z","2021-05-20T10:41:11.407Z"\n"mock-email-3@foo.com","2021-08-10T10:41:11.407Z","2021-08-20T10:41:11.407Z"\n"mock-email-4@foo.com","2021-01-01T10:41:11.407Z","2021-01-02T10:41:11.407Z"\n"mock-email-5@foo.com","2023-01-01T10:41:11.407Z","2023-01-02T10:41:11.407Z"\n"mock-email-6@foo.com","2024-01-01T10:41:11.407Z","2024-01-02T10:41:11.407Z"'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('exportCsv when group is managed', function () {
|
||||
beforeEach(function (ctx) {
|
||||
describe('exportCsv when group is managed', () => {
|
||||
beforeEach(ctx => {
|
||||
ctx.req.entity = Object.assign(
|
||||
{ managedUsersEnabled: true },
|
||||
ctx.subscription
|
||||
)
|
||||
ctx.req.entityConfig = EntityConfigs.groupManagers
|
||||
ctx.res = new MockResponse()
|
||||
ctx.res = new MockResponse(vi)
|
||||
ctx.UserMembershipController.exportCsv(ctx.req, ctx.res)
|
||||
})
|
||||
|
||||
it('should export the correct csv', function (ctx) {
|
||||
assertCalledWith(
|
||||
ctx.res.send,
|
||||
it('should export the correct csv', ({ res }) => {
|
||||
expect(res.send).toHaveBeenCalledWith(
|
||||
'"email","last_logged_in_at","last_active_at","managed"\n"mock-email-1@foo.com","2020-08-09T12:43:11.467Z","2021-08-09T12:43:11.467Z",false\n"mock-email-2@foo.com","2020-05-20T10:41:11.407Z","2021-05-20T10:41:11.407Z",false\n"mock-email-3@foo.com","2021-08-10T10:41:11.407Z","2021-08-20T10:41:11.407Z",false\n"mock-email-4@foo.com","2021-01-01T10:41:11.407Z","2021-01-02T10:41:11.407Z",true\n"mock-email-5@foo.com","2023-01-01T10:41:11.407Z","2023-01-02T10:41:11.407Z",false\n"mock-email-6@foo.com","2024-01-01T10:41:11.407Z","2024-01-02T10:41:11.407Z",true'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('exportCsv when group has SSO', function () {
|
||||
beforeEach(function (ctx) {
|
||||
describe('exportCsv when group has SSO', () => {
|
||||
beforeEach(ctx => {
|
||||
ctx.req.entity = Object.assign(
|
||||
{ ssoConfig: 'sso-config-id' },
|
||||
ctx.subscription
|
||||
)
|
||||
ctx.req.entityConfig = EntityConfigs.groupManagers
|
||||
ctx.Modules.promises.hooks.fire.resolves([true])
|
||||
ctx.res = new MockResponse()
|
||||
ctx.Modules.promises.hooks.fire.mockResolvedValue([true])
|
||||
ctx.res = new MockResponse(vi)
|
||||
ctx.UserMembershipController.exportCsv(ctx.req, ctx.res)
|
||||
})
|
||||
|
||||
it('should export the correct csv', function (ctx) {
|
||||
assertCalledWith(
|
||||
ctx.res.send,
|
||||
it('should export the correct csv', ({ res }) => {
|
||||
expect(res.send).toHaveBeenCalledWith(
|
||||
'"email","last_logged_in_at","last_active_at","sso"\n"mock-email-1@foo.com","2020-08-09T12:43:11.467Z","2021-08-09T12:43:11.467Z",false\n"mock-email-2@foo.com","2020-05-20T10:41:11.407Z","2021-05-20T10:41:11.407Z",false\n"mock-email-3@foo.com","2021-08-10T10:41:11.407Z","2021-08-20T10:41:11.407Z",false\n"mock-email-4@foo.com","2021-01-01T10:41:11.407Z","2021-01-02T10:41:11.407Z",false\n"mock-email-5@foo.com","2023-01-01T10:41:11.407Z","2023-01-02T10:41:11.407Z",true\n"mock-email-6@foo.com","2024-01-01T10:41:11.407Z","2024-01-02T10:41:11.407Z",true'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('exportCsv when group has SSO and managed users enabled', function () {
|
||||
beforeEach(function (ctx) {
|
||||
describe('exportCsv when group has SSO and managed users enabled', () => {
|
||||
beforeEach(ctx => {
|
||||
ctx.req.entity = Object.assign(
|
||||
{ managedUsersEnabled: true },
|
||||
{ ssoConfig: 'sso-config-id' },
|
||||
ctx.subscription
|
||||
)
|
||||
ctx.req.entityConfig = EntityConfigs.groupManagers
|
||||
ctx.Modules.promises.hooks.fire.resolves([true])
|
||||
ctx.res = new MockResponse()
|
||||
ctx.Modules.promises.hooks.fire.mockResolvedValue([true])
|
||||
ctx.res = new MockResponse(vi)
|
||||
ctx.UserMembershipController.exportCsv(ctx.req, ctx.res)
|
||||
})
|
||||
|
||||
it('should export the correct csv', function (ctx) {
|
||||
assertCalledWith(
|
||||
ctx.res.send,
|
||||
it('should export the correct csv', ({ res }) => {
|
||||
expect(res.send).toHaveBeenCalledWith(
|
||||
'"email","last_logged_in_at","last_active_at","managed","sso"\n"mock-email-1@foo.com","2020-08-09T12:43:11.467Z","2021-08-09T12:43:11.467Z",false,false\n"mock-email-2@foo.com","2020-05-20T10:41:11.407Z","2021-05-20T10:41:11.407Z",false,false\n"mock-email-3@foo.com","2021-08-10T10:41:11.407Z","2021-08-20T10:41:11.407Z",false,false\n"mock-email-4@foo.com","2021-01-01T10:41:11.407Z","2021-01-02T10:41:11.407Z",true,false\n"mock-email-5@foo.com","2023-01-01T10:41:11.407Z","2023-01-02T10:41:11.407Z",false,true\n"mock-email-6@foo.com","2024-01-01T10:41:11.407Z","2024-01-02T10:41:11.407Z",true,true'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('new', function () {
|
||||
beforeEach(function (ctx) {
|
||||
describe('new', () => {
|
||||
beforeEach(ctx => {
|
||||
ctx.req.params.name = 'publisher'
|
||||
ctx.req.params.id = 'abc'
|
||||
})
|
||||
|
||||
it('renders view', async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.UserMembershipController.new(ctx.req, {
|
||||
render: (viewPath, data) => {
|
||||
expect(data.entityName).to.eq('publisher')
|
||||
expect(data.entityId).to.eq('abc')
|
||||
resolve()
|
||||
},
|
||||
})
|
||||
it('renders view', async ({ UserMembershipController, req }) => {
|
||||
expect.assertions(2)
|
||||
await UserMembershipController.new(req, {
|
||||
render: (viewPath, data) => {
|
||||
expect(data.entityName).to.eq('publisher')
|
||||
expect(data.entityId).to.eq('abc')
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('create', function () {
|
||||
beforeEach(function (ctx) {
|
||||
describe('create', () => {
|
||||
beforeEach(ctx => {
|
||||
ctx.req.params.name = 'institution'
|
||||
ctx.req.entityConfig = EntityConfigs.institution
|
||||
ctx.req.params.id = 123
|
||||
})
|
||||
|
||||
it('creates institution', async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.UserMembershipController.create(ctx.req, {
|
||||
redirect: path => {
|
||||
expect(path).to.eq(EntityConfigs.institution.pathsFor(123).index)
|
||||
sinon.assert.calledWithMatch(
|
||||
ctx.UserMembershipHandler.createEntity,
|
||||
123,
|
||||
{ modelName: 'Institution' }
|
||||
)
|
||||
resolve()
|
||||
},
|
||||
})
|
||||
it('creates institution', async ({
|
||||
UserMembershipController,
|
||||
req,
|
||||
UserMembershipHandler,
|
||||
}) => {
|
||||
expect.assertions(2)
|
||||
await UserMembershipController.create(req, {
|
||||
redirect: path => {
|
||||
expect(path).to.eq(EntityConfigs.institution.pathsFor(123).index)
|
||||
expect(UserMembershipHandler.createEntity).toHaveBeenCalledWith(
|
||||
123,
|
||||
{
|
||||
fields: {
|
||||
access: 'managerIds',
|
||||
membership: 'member_ids',
|
||||
name: 'name',
|
||||
primaryKey: 'v1Id',
|
||||
read: ['managerIds'],
|
||||
write: 'managerIds',
|
||||
},
|
||||
modelName: 'Institution',
|
||||
pathsFor: EntityConfigs.institution.pathsFor,
|
||||
},
|
||||
expect.any(Function)
|
||||
)
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
import { vi, expect } from 'vitest'
|
||||
import mongodb from 'mongodb-legacy'
|
||||
import EntityConfigs from '../../../../app/src/Features/UserMembership/UserMembershipEntityConfigs.js'
|
||||
import UserMembershipErrors from '../../../../app/src/Features/UserMembership/UserMembershipErrors.mjs'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
const modulePath =
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipHandler'
|
||||
|
||||
const serializeIds = ids =>
|
||||
ids.map(id => (id instanceof ObjectId ? `objectId-${id.toString()}` : id))
|
||||
|
||||
vi.mock(
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipErrors.mjs',
|
||||
() =>
|
||||
vi.importActual(
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipErrors.mjs'
|
||||
)
|
||||
)
|
||||
|
||||
describe('UserMembershipHandler', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.user = { _id: new ObjectId() }
|
||||
ctx.newUser = { _id: new ObjectId(), email: 'new-user-email@foo.bar' }
|
||||
ctx.fakeEntityId = new ObjectId()
|
||||
ctx.subscription = {
|
||||
_id: 'mock-subscription-id',
|
||||
groupPlan: true,
|
||||
membersLimit: 10,
|
||||
member_ids: [new ObjectId(), new ObjectId()],
|
||||
manager_ids: [new ObjectId()],
|
||||
invited_emails: ['mock-email-1@foo.com'],
|
||||
teamInvites: [{ email: 'mock-email-1@bar.com' }],
|
||||
update: vi.fn().mockReturnValue({
|
||||
exec: vi.fn().mockResolvedValue(),
|
||||
}),
|
||||
}
|
||||
ctx.institution = {
|
||||
_id: 'mock-institution-id',
|
||||
v1Id: 123,
|
||||
managerIds: [new ObjectId(), new ObjectId(), new ObjectId()],
|
||||
updateOne: vi.fn().mockReturnValue({
|
||||
exec: vi.fn().mockResolvedValue(),
|
||||
}),
|
||||
}
|
||||
ctx.publisher = {
|
||||
_id: 'mock-publisher-id',
|
||||
slug: 'slug',
|
||||
managerIds: [new ObjectId(), new ObjectId()],
|
||||
updateOne: vi.fn().mockReturnValue({
|
||||
exec: vi.fn().mockResolvedValue(),
|
||||
}),
|
||||
}
|
||||
|
||||
ctx.UserMembershipViewModel = {
|
||||
promises: {
|
||||
buildAsync: vi.fn().mockResolvedValue([{ _id: 'mock-member-id' }]),
|
||||
},
|
||||
build: vi.fn().mockReturnValue(ctx.newUser),
|
||||
}
|
||||
ctx.UserGetter = {
|
||||
promises: {
|
||||
getUserByAnyEmail: vi.fn().mockResolvedValue(ctx.newUser),
|
||||
},
|
||||
}
|
||||
ctx.Institution = {
|
||||
findOne: vi.fn().mockReturnValue({
|
||||
exec: vi.fn().mockResolvedValue(ctx.institution),
|
||||
}),
|
||||
}
|
||||
ctx.Subscription = {
|
||||
findOne: vi.fn().mockReturnValue({
|
||||
exec: vi.fn().mockResolvedValue(ctx.subscription),
|
||||
}),
|
||||
}
|
||||
ctx.Publisher = {
|
||||
findOne: vi.fn().mockReturnValue({
|
||||
exec: vi.fn().mockResolvedValue(ctx.publisher),
|
||||
}),
|
||||
create: vi.fn().mockReturnValue({
|
||||
exec: vi.fn().mockResolvedValue(ctx.publisher),
|
||||
}),
|
||||
}
|
||||
|
||||
vi.doMock('mongodb-legacy', () => ({
|
||||
default: { ObjectId },
|
||||
}))
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipViewModel',
|
||||
() => ({
|
||||
default: ctx.UserMembershipViewModel,
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock('../../../../app/src/Features/User/UserGetter', () => ({
|
||||
default: ctx.UserGetter,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/models/Institution', () => ({
|
||||
Institution: ctx.Institution,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/models/Subscription', () => ({
|
||||
Subscription: ctx.Subscription,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/models/Publisher', () => ({
|
||||
Publisher: ctx.Publisher,
|
||||
}))
|
||||
|
||||
ctx.UserMembershipHandler = (await import(modulePath)).default
|
||||
})
|
||||
|
||||
describe('getEntityWithoutAuthorizationCheck', function () {
|
||||
it('get publisher', async function (ctx) {
|
||||
const subscription =
|
||||
await ctx.UserMembershipHandler.promises.getEntityWithoutAuthorizationCheck(
|
||||
ctx.fakeEntityId,
|
||||
EntityConfigs.publisher
|
||||
)
|
||||
const expectedQuery = { slug: ctx.fakeEntityId }
|
||||
expect(ctx.Publisher.findOne).toHaveBeenCalledWith(expectedQuery)
|
||||
expect(subscription).to.equal(ctx.publisher)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getUsers', function () {
|
||||
describe('group', function () {
|
||||
it('build view model for all users', async function (ctx) {
|
||||
await ctx.UserMembershipHandler.promises.getUsers(
|
||||
ctx.subscription,
|
||||
EntityConfigs.group
|
||||
)
|
||||
expect(
|
||||
serializeIds(
|
||||
ctx.UserMembershipViewModel.promises.buildAsync.mock.calls[0][0]
|
||||
)
|
||||
).toEqual(
|
||||
serializeIds(
|
||||
ctx.subscription.invited_emails.concat(
|
||||
ctx.subscription.teamInvites[0].email,
|
||||
ctx.subscription.member_ids
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('group managers', function () {
|
||||
it('build view model for all managers', async function (ctx) {
|
||||
await ctx.UserMembershipHandler.promises.getUsers(
|
||||
ctx.subscription,
|
||||
EntityConfigs.groupManagers
|
||||
)
|
||||
expect(
|
||||
serializeIds(
|
||||
ctx.UserMembershipViewModel.promises.buildAsync.mock.calls[0][0]
|
||||
)
|
||||
).toEqual(serializeIds(ctx.subscription.manager_ids))
|
||||
})
|
||||
})
|
||||
|
||||
describe('institution', function () {
|
||||
it('build view model for all managers', async function (ctx) {
|
||||
await ctx.UserMembershipHandler.promises.getUsers(
|
||||
ctx.institution,
|
||||
EntityConfigs.institution
|
||||
)
|
||||
expect(
|
||||
serializeIds(
|
||||
ctx.UserMembershipViewModel.promises.buildAsync.mock.calls[0][0]
|
||||
)
|
||||
).toEqual(serializeIds(ctx.institution.managerIds))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('createEntity', function () {
|
||||
it('creates publisher', async function (ctx) {
|
||||
await ctx.UserMembershipHandler.promises.createEntity(
|
||||
ctx.fakeEntityId,
|
||||
EntityConfigs.publisher
|
||||
)
|
||||
expect(ctx.Publisher.create).toHaveBeenCalledWith({
|
||||
slug: ctx.fakeEntityId,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('addUser', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.email = ctx.newUser.email
|
||||
})
|
||||
|
||||
describe('institution', function () {
|
||||
it('get user', async function (ctx) {
|
||||
await ctx.UserMembershipHandler.promises.addUser(
|
||||
ctx.institution,
|
||||
EntityConfigs.institution,
|
||||
ctx.email
|
||||
)
|
||||
expect(ctx.UserGetter.promises.getUserByAnyEmail).toHaveBeenCalledWith(
|
||||
ctx.email
|
||||
)
|
||||
})
|
||||
|
||||
it('handle user not found', async function (ctx) {
|
||||
ctx.UserGetter.promises.getUserByAnyEmail.mockResolvedValue(null)
|
||||
try {
|
||||
await ctx.UserMembershipHandler.promises.addUser(
|
||||
ctx.institution,
|
||||
EntityConfigs.institution,
|
||||
ctx.email
|
||||
)
|
||||
expect.fail('Expected addUser to throw')
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(UserMembershipErrors.UserNotFoundError)
|
||||
}
|
||||
})
|
||||
|
||||
it('handle user already added', async function (ctx) {
|
||||
ctx.institution.managerIds.push(ctx.newUser._id)
|
||||
try {
|
||||
await ctx.UserMembershipHandler.promises.addUser(
|
||||
ctx.institution,
|
||||
EntityConfigs.institution,
|
||||
ctx.email
|
||||
)
|
||||
expect.fail('Expected addUser to throw')
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(UserMembershipErrors.UserAlreadyAddedError)
|
||||
}
|
||||
})
|
||||
|
||||
it('add user to institution', async function (ctx) {
|
||||
await ctx.UserMembershipHandler.promises.addUser(
|
||||
ctx.institution,
|
||||
EntityConfigs.institution,
|
||||
ctx.email
|
||||
)
|
||||
expect(ctx.institution.updateOne).toHaveBeenCalledWith({
|
||||
$addToSet: { managerIds: ctx.newUser._id },
|
||||
})
|
||||
})
|
||||
|
||||
it('return user view', async function (ctx) {
|
||||
const user = await ctx.UserMembershipHandler.promises.addUser(
|
||||
ctx.institution,
|
||||
EntityConfigs.institution,
|
||||
ctx.email
|
||||
)
|
||||
expect(user).to.equal(ctx.newUser)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('removeUser', function () {
|
||||
describe('institution', function () {
|
||||
it('remove user from institution', async function (ctx) {
|
||||
await ctx.UserMembershipHandler.promises.removeUser(
|
||||
ctx.institution,
|
||||
EntityConfigs.institution,
|
||||
ctx.newUser._id
|
||||
)
|
||||
expect(ctx.institution.updateOne).toHaveBeenCalledWith({
|
||||
$pull: { managerIds: ctx.newUser._id },
|
||||
})
|
||||
})
|
||||
|
||||
it('handle admin', async function (ctx) {
|
||||
ctx.subscription.admin_id = ctx.newUser._id
|
||||
try {
|
||||
await ctx.UserMembershipHandler.promises.removeUser(
|
||||
ctx.subscription,
|
||||
EntityConfigs.groupManagers,
|
||||
ctx.newUser._id
|
||||
)
|
||||
expect.fail('Expected removeUser to throw')
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(UserMembershipErrors.UserIsManagerError)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,251 +0,0 @@
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const assertCalledWith = sinon.assert.calledWith
|
||||
const { ObjectId } = require('mongodb-legacy')
|
||||
const modulePath =
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipHandler'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const EntityConfigs = require('../../../../app/src/Features/UserMembership/UserMembershipEntityConfigs')
|
||||
const {
|
||||
UserIsManagerError,
|
||||
UserNotFoundError,
|
||||
UserAlreadyAddedError,
|
||||
} = require('../../../../app/src/Features/UserMembership/UserMembershipErrors')
|
||||
|
||||
describe('UserMembershipHandler', function () {
|
||||
beforeEach(function () {
|
||||
this.user = { _id: new ObjectId() }
|
||||
this.newUser = { _id: new ObjectId(), email: 'new-user-email@foo.bar' }
|
||||
this.fakeEntityId = new ObjectId()
|
||||
this.subscription = {
|
||||
_id: 'mock-subscription-id',
|
||||
groupPlan: true,
|
||||
membersLimit: 10,
|
||||
member_ids: [new ObjectId(), new ObjectId()],
|
||||
manager_ids: [new ObjectId()],
|
||||
invited_emails: ['mock-email-1@foo.com'],
|
||||
teamInvites: [{ email: 'mock-email-1@bar.com' }],
|
||||
update: sinon.stub().returns({
|
||||
exec: sinon.stub().resolves(),
|
||||
}),
|
||||
}
|
||||
this.institution = {
|
||||
_id: 'mock-institution-id',
|
||||
v1Id: 123,
|
||||
managerIds: [new ObjectId(), new ObjectId(), new ObjectId()],
|
||||
updateOne: sinon.stub().returns({
|
||||
exec: sinon.stub().resolves(),
|
||||
}),
|
||||
}
|
||||
this.publisher = {
|
||||
_id: 'mock-publisher-id',
|
||||
slug: 'slug',
|
||||
managerIds: [new ObjectId(), new ObjectId()],
|
||||
updateOne: sinon.stub().returns({
|
||||
exec: sinon.stub().resolves(),
|
||||
}),
|
||||
}
|
||||
|
||||
this.UserMembershipViewModel = {
|
||||
promises: {
|
||||
buildAsync: sinon.stub().resolves([{ _id: 'mock-member-id' }]),
|
||||
},
|
||||
build: sinon.stub().returns(this.newUser),
|
||||
}
|
||||
this.UserGetter = {
|
||||
promises: {
|
||||
getUserByAnyEmail: sinon.stub().resolves(this.newUser),
|
||||
},
|
||||
}
|
||||
this.Institution = {
|
||||
findOne: sinon.stub().returns({
|
||||
exec: sinon.stub().resolves(this.institution),
|
||||
}),
|
||||
}
|
||||
this.Subscription = {
|
||||
findOne: sinon.stub().returns({
|
||||
exec: sinon.stub().resolves(this.subscription),
|
||||
}),
|
||||
}
|
||||
this.Publisher = {
|
||||
findOne: sinon.stub().returns({
|
||||
exec: sinon.stub().resolves(this.publisher),
|
||||
}),
|
||||
create: sinon.stub().returns({
|
||||
exec: sinon.stub().resolves(this.publisher),
|
||||
}),
|
||||
}
|
||||
this.UserMembershipHandler = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'mongodb-legacy': { ObjectId },
|
||||
'./UserMembershipErrors': {
|
||||
UserIsManagerError,
|
||||
UserNotFoundError,
|
||||
UserAlreadyAddedError,
|
||||
},
|
||||
'./UserMembershipViewModel': this.UserMembershipViewModel,
|
||||
'../User/UserGetter': this.UserGetter,
|
||||
'../../models/Institution': {
|
||||
Institution: this.Institution,
|
||||
},
|
||||
'../../models/Subscription': {
|
||||
Subscription: this.Subscription,
|
||||
},
|
||||
'../../models/Publisher': {
|
||||
Publisher: this.Publisher,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('getEntityWithoutAuthorizationCheck', function () {
|
||||
it('get publisher', async function () {
|
||||
const subscription =
|
||||
await this.UserMembershipHandler.promises.getEntityWithoutAuthorizationCheck(
|
||||
this.fakeEntityId,
|
||||
EntityConfigs.publisher
|
||||
)
|
||||
const expectedQuery = { slug: this.fakeEntityId }
|
||||
assertCalledWith(this.Publisher.findOne, expectedQuery)
|
||||
expect(subscription).to.equal(this.publisher)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getUsers', function () {
|
||||
describe('group', function () {
|
||||
it('build view model for all users', async function () {
|
||||
await this.UserMembershipHandler.promises.getUsers(
|
||||
this.subscription,
|
||||
EntityConfigs.group
|
||||
)
|
||||
expect(
|
||||
this.UserMembershipViewModel.promises.buildAsync
|
||||
).to.be.calledOnceWith(
|
||||
this.subscription.invited_emails.concat(
|
||||
this.subscription.teamInvites[0].email,
|
||||
this.subscription.member_ids
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('group managers', function () {
|
||||
it('build view model for all managers', async function () {
|
||||
await this.UserMembershipHandler.promises.getUsers(
|
||||
this.subscription,
|
||||
EntityConfigs.groupManagers
|
||||
)
|
||||
expect(
|
||||
this.UserMembershipViewModel.promises.buildAsync
|
||||
).to.be.calledOnceWith(this.subscription.manager_ids)
|
||||
})
|
||||
})
|
||||
|
||||
describe('institution', function () {
|
||||
it('build view model for all managers', async function () {
|
||||
await this.UserMembershipHandler.promises.getUsers(
|
||||
this.institution,
|
||||
EntityConfigs.institution
|
||||
)
|
||||
expect(
|
||||
this.UserMembershipViewModel.promises.buildAsync
|
||||
).to.be.calledOnceWith(this.institution.managerIds)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('createEntity', function () {
|
||||
it('creates publisher', async function () {
|
||||
await this.UserMembershipHandler.promises.createEntity(
|
||||
this.fakeEntityId,
|
||||
EntityConfigs.publisher
|
||||
)
|
||||
assertCalledWith(this.Publisher.create, { slug: this.fakeEntityId })
|
||||
})
|
||||
})
|
||||
|
||||
describe('addUser', function () {
|
||||
beforeEach(function () {
|
||||
this.email = this.newUser.email
|
||||
})
|
||||
|
||||
describe('institution', function () {
|
||||
it('get user', async function () {
|
||||
await this.UserMembershipHandler.promises.addUser(
|
||||
this.institution,
|
||||
EntityConfigs.institution,
|
||||
this.email
|
||||
)
|
||||
assertCalledWith(this.UserGetter.promises.getUserByAnyEmail, this.email)
|
||||
})
|
||||
|
||||
it('handle user not found', async function () {
|
||||
this.UserGetter.promises.getUserByAnyEmail.resolves(null)
|
||||
expect(
|
||||
this.UserMembershipHandler.promises.addUser(
|
||||
this.institution,
|
||||
EntityConfigs.institution,
|
||||
this.email
|
||||
)
|
||||
).to.be.rejectedWith(UserNotFoundError)
|
||||
})
|
||||
|
||||
it('handle user already added', async function () {
|
||||
this.institution.managerIds.push(this.newUser._id)
|
||||
expect(
|
||||
this.UserMembershipHandler.promises.addUser(
|
||||
this.institution,
|
||||
EntityConfigs.institution,
|
||||
this.email
|
||||
)
|
||||
).to.be.rejectedWith(UserAlreadyAddedError)
|
||||
})
|
||||
|
||||
it('add user to institution', async function () {
|
||||
await this.UserMembershipHandler.promises.addUser(
|
||||
this.institution,
|
||||
EntityConfigs.institution,
|
||||
this.email
|
||||
)
|
||||
assertCalledWith(this.institution.updateOne, {
|
||||
$addToSet: { managerIds: this.newUser._id },
|
||||
})
|
||||
})
|
||||
|
||||
it('return user view', async function () {
|
||||
const user = await this.UserMembershipHandler.promises.addUser(
|
||||
this.institution,
|
||||
EntityConfigs.institution,
|
||||
this.email
|
||||
)
|
||||
user.should.equal(this.newUser)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('removeUser', function () {
|
||||
describe('institution', function () {
|
||||
it('remove user from institution', async function () {
|
||||
await this.UserMembershipHandler.promises.removeUser(
|
||||
this.institution,
|
||||
EntityConfigs.institution,
|
||||
this.newUser._id
|
||||
)
|
||||
assertCalledWith(this.institution.updateOne, {
|
||||
$pull: { managerIds: this.newUser._id },
|
||||
})
|
||||
})
|
||||
|
||||
it('handle admin', async function () {
|
||||
this.subscription.admin_id = this.newUser._id
|
||||
expect(
|
||||
this.UserMembershipHandler.promises.removeUser(
|
||||
this.subscription,
|
||||
EntityConfigs.groupManagers,
|
||||
this.newUser._id
|
||||
)
|
||||
).to.be.rejectedWith(UserIsManagerError)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,128 @@
|
||||
import { vi, expect } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
import mongodb from 'mongodb-legacy'
|
||||
import {
|
||||
isObjectIdInstance,
|
||||
normalizeQuery,
|
||||
} from '../../../../app/src/Features/Helpers/Mongo.js'
|
||||
|
||||
const assertCalledWith = sinon.assert.calledWith
|
||||
const assertNotCalled = sinon.assert.notCalled
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
const modulePath =
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipViewModel'
|
||||
|
||||
describe('UserMembershipViewModel', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.UserGetter = { promises: { getUsers: sinon.stub() } }
|
||||
|
||||
vi.doMock('mongodb-legacy', () => ({
|
||||
default: { ObjectId },
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/Helpers/Mongo', () => ({
|
||||
isObjectIdInstance,
|
||||
normalizeQuery,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/User/UserGetter', () => ({
|
||||
default: ctx.UserGetter,
|
||||
}))
|
||||
|
||||
ctx.UserMembershipViewModel = (await import(modulePath)).default
|
||||
ctx.email = 'mock-email@bar.com'
|
||||
ctx.user = {
|
||||
_id: 'mock-user-id',
|
||||
email: 'mock-email@baz.com',
|
||||
first_name: 'Name',
|
||||
lastLoggedIn: '2020-05-20T10:41:11.407Z',
|
||||
enrollment: {
|
||||
managedBy: 'mock-group-id',
|
||||
enrolledAt: new Date(),
|
||||
sso: {
|
||||
groupId: 'abc123abc123',
|
||||
linkedAt: new Date(),
|
||||
primary: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe('build', function () {
|
||||
it('build email', function (ctx) {
|
||||
const viewModel = ctx.UserMembershipViewModel.build(ctx.email)
|
||||
expect(viewModel).to.deep.equal({
|
||||
email: ctx.email,
|
||||
invite: true,
|
||||
last_active_at: null,
|
||||
last_logged_in_at: null,
|
||||
first_name: null,
|
||||
last_name: null,
|
||||
_id: null,
|
||||
enrollment: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it('build user', function (ctx) {
|
||||
const viewModel = ctx.UserMembershipViewModel.build(ctx.user)
|
||||
expect(viewModel).to.deep.equal({
|
||||
email: ctx.user.email,
|
||||
invite: false,
|
||||
last_active_at: ctx.user.lastLoggedIn,
|
||||
last_logged_in_at: ctx.user.lastLoggedIn,
|
||||
first_name: ctx.user.first_name,
|
||||
last_name: null,
|
||||
_id: ctx.user._id,
|
||||
enrollment: ctx.user.enrollment,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('build async', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.UserMembershipViewModel.build = sinon.stub()
|
||||
})
|
||||
|
||||
it('build email', async function (ctx) {
|
||||
ctx.UserGetter.promises.getUsers.resolves([])
|
||||
await ctx.UserMembershipViewModel.buildAsync([ctx.email])
|
||||
assertCalledWith(ctx.UserMembershipViewModel.build, ctx.email)
|
||||
})
|
||||
|
||||
it('build user', async function (ctx) {
|
||||
ctx.UserGetter.promises.getUsers.resolves([])
|
||||
await ctx.UserMembershipViewModel.buildAsync([ctx.user])
|
||||
assertCalledWith(ctx.UserMembershipViewModel.build, ctx.user)
|
||||
})
|
||||
|
||||
it('build user id', async function (ctx) {
|
||||
const user = {
|
||||
...ctx.user,
|
||||
_id: new ObjectId(),
|
||||
}
|
||||
ctx.UserGetter.promises.getUsers.resolves([user])
|
||||
const [viewModel] = await ctx.UserMembershipViewModel.buildAsync([
|
||||
user._id,
|
||||
])
|
||||
assertNotCalled(ctx.UserMembershipViewModel.build)
|
||||
expect(viewModel._id.toString()).to.equal(user._id.toString())
|
||||
expect(viewModel.email).to.equal(user.email)
|
||||
expect(viewModel.first_name).to.equal(user.first_name)
|
||||
expect(viewModel.invite).to.equal(false)
|
||||
expect(viewModel.email).to.exist
|
||||
expect(viewModel.enrollment).to.exist
|
||||
expect(viewModel.enrollment).to.deep.equal(user.enrollment)
|
||||
})
|
||||
|
||||
it('build user id with error', async function (ctx) {
|
||||
ctx.UserGetter.promises.getUsers.rejects(new Error('nope'))
|
||||
const userId = new ObjectId()
|
||||
const [viewModel] = await ctx.UserMembershipViewModel.buildAsync([userId])
|
||||
assertNotCalled(ctx.UserMembershipViewModel.build)
|
||||
expect(viewModel._id).to.equal(userId.toString())
|
||||
expect(viewModel.email).not.to.exist
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,119 +0,0 @@
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const assertCalledWith = sinon.assert.calledWith
|
||||
const assertNotCalled = sinon.assert.notCalled
|
||||
const { ObjectId } = require('mongodb-legacy')
|
||||
const modulePath =
|
||||
'../../../../app/src/Features/UserMembership/UserMembershipViewModel'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const {
|
||||
isObjectIdInstance,
|
||||
normalizeQuery,
|
||||
} = require('../../../../app/src/Features/Helpers/Mongo')
|
||||
|
||||
describe('UserMembershipViewModel', function () {
|
||||
beforeEach(function () {
|
||||
this.UserGetter = { promises: { getUsers: sinon.stub() } }
|
||||
this.UserMembershipViewModel = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'mongodb-legacy': { ObjectId },
|
||||
'../Helpers/Mongo': { isObjectIdInstance, normalizeQuery },
|
||||
'../User/UserGetter': this.UserGetter,
|
||||
},
|
||||
})
|
||||
this.email = 'mock-email@bar.com'
|
||||
this.user = {
|
||||
_id: 'mock-user-id',
|
||||
email: 'mock-email@baz.com',
|
||||
first_name: 'Name',
|
||||
lastLoggedIn: '2020-05-20T10:41:11.407Z',
|
||||
enrollment: {
|
||||
managedBy: 'mock-group-id',
|
||||
enrolledAt: new Date(),
|
||||
sso: {
|
||||
groupId: 'abc123abc123',
|
||||
linkedAt: new Date(),
|
||||
primary: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe('build', function () {
|
||||
it('build email', function () {
|
||||
const viewModel = this.UserMembershipViewModel.build(this.email)
|
||||
expect(viewModel).to.deep.equal({
|
||||
email: this.email,
|
||||
invite: true,
|
||||
last_active_at: null,
|
||||
last_logged_in_at: null,
|
||||
first_name: null,
|
||||
last_name: null,
|
||||
_id: null,
|
||||
enrollment: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it('build user', function () {
|
||||
const viewModel = this.UserMembershipViewModel.build(this.user)
|
||||
expect(viewModel).to.deep.equal({
|
||||
email: this.user.email,
|
||||
invite: false,
|
||||
last_active_at: this.user.lastLoggedIn,
|
||||
last_logged_in_at: this.user.lastLoggedIn,
|
||||
first_name: this.user.first_name,
|
||||
last_name: null,
|
||||
_id: this.user._id,
|
||||
enrollment: this.user.enrollment,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('build async', function () {
|
||||
beforeEach(function () {
|
||||
this.UserMembershipViewModel.build = sinon.stub()
|
||||
})
|
||||
|
||||
it('build email', async function () {
|
||||
this.UserGetter.promises.getUsers.resolves([])
|
||||
await this.UserMembershipViewModel.buildAsync([this.email])
|
||||
assertCalledWith(this.UserMembershipViewModel.build, this.email)
|
||||
})
|
||||
|
||||
it('build user', async function () {
|
||||
this.UserGetter.promises.getUsers.resolves([])
|
||||
await this.UserMembershipViewModel.buildAsync([this.user])
|
||||
assertCalledWith(this.UserMembershipViewModel.build, this.user)
|
||||
})
|
||||
|
||||
it('build user id', async function () {
|
||||
const user = {
|
||||
...this.user,
|
||||
_id: new ObjectId(),
|
||||
}
|
||||
this.UserGetter.promises.getUsers.resolves([user])
|
||||
const [viewModel] = await this.UserMembershipViewModel.buildAsync([
|
||||
user._id,
|
||||
])
|
||||
assertNotCalled(this.UserMembershipViewModel.build)
|
||||
expect(viewModel._id.toString()).to.equal(user._id.toString())
|
||||
expect(viewModel.email).to.equal(user.email)
|
||||
expect(viewModel.first_name).to.equal(user.first_name)
|
||||
expect(viewModel.invite).to.equal(false)
|
||||
expect(viewModel.email).to.exist
|
||||
expect(viewModel.enrollment).to.exist
|
||||
expect(viewModel.enrollment).to.deep.equal(user.enrollment)
|
||||
})
|
||||
|
||||
it('build user id with error', async function () {
|
||||
this.UserGetter.promises.getUsers.rejects(new Error('nope'))
|
||||
const userId = new ObjectId()
|
||||
const [viewModel] = await this.UserMembershipViewModel.buildAsync([
|
||||
userId,
|
||||
])
|
||||
assertNotCalled(this.UserMembershipViewModel.build)
|
||||
expect(viewModel._id).to.equal(userId.toString())
|
||||
expect(viewModel.email).not.to.exist
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user