UNPKG

@sync-in/server

Version:

The secure, open-source platform for file storage, sharing, collaboration, and sync

1,043 lines (1,042 loc) 44.5 kB
/* * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com> * This file is part of Sync-in | The open source file sync and share solution * See the LICENSE file for licensing details */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const _testing = require("@nestjs/testing"); const _bcryptjs = /*#__PURE__*/ _interop_require_default(require("bcryptjs")); const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path")); const _nodestream = require("node:stream"); const _authmanagerservice = require("../../../authentication/services/auth-manager.service"); const _functions = require("../../../common/functions"); const _image = /*#__PURE__*/ _interop_require_wildcard(require("../../../common/image")); const _cacheservice = require("../../../infrastructure/cache/services/cache.service"); const _constants = require("../../../infrastructure/database/constants"); const _files = /*#__PURE__*/ _interop_require_wildcard(require("../../files/utils/files")); const _notificationsmanagerservice = require("../../notifications/services/notifications-manager.service"); const _member = require("../constants/member"); const _user = require("../constants/user"); const _usermodel = require("../models/user.model"); const _test = require("../utils/test"); const _adminusersmanagerservice = require("./admin-users-manager.service"); const _adminusersqueriesservice = require("./admin-users-queries.service"); const _usersmanagerservice = require("./users-manager.service"); const _usersqueriesservice = require("./users-queries.service"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interop_require_wildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = { __proto__: null }; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } jest.mock('../../../common/functions', ()=>{ const actual = jest.requireActual('../../../common/functions'); return { ...actual, comparePassword: jest.fn() }; }); jest.mock('bcryptjs', ()=>({ __esModule: true, default: { hash: jest.fn(()=>Promise.resolve('hashed-password')) } })); jest.mock('../../../common/image', ()=>{ const actual = jest.requireActual('../../../common/image'); return { ...actual, generateAvatar: jest.fn(()=>_nodestream.Readable.from([ Buffer.from('PNGDATA') ])) }; }); describe(_usersmanagerservice.UsersManager.name, ()=>{ let usersManager; let adminUsersManager; let adminUsersQueries; let usersQueriesService; let userTest; let deleteUserDto; const flush = ()=>new Promise((r)=>setImmediate(r)); const okStream = (d = 'OK')=>{ const s = _nodestream.Readable.from([ Buffer.from(d) ]); s.truncated = false; return s; }; const errStream = (msg = 'err', truncated = false)=>{ const s = new _nodestream.Readable({ read () { this.destroy(new Error(msg)); } }); s.truncated = truncated; return s; }; const mkReq = (mimetype, stream, user = userTest)=>({ user, file: jest.fn().mockResolvedValue({ mimetype, file: stream }) }); const ensurePaths = async ()=>{ if (!await (0, _files.isPathExists)(userTest.homePath)) { await userTest.makePaths(); } }; const notificationsManager = { sendEmailNotification: jest.fn().mockResolvedValue(undefined) }; beforeAll(async ()=>{ const module = await _testing.Test.createTestingModule({ providers: [ _adminusersmanagerservice.AdminUsersManager, _adminusersqueriesservice.AdminUsersQueries, _usersmanagerservice.UsersManager, _usersqueriesservice.UsersQueries, { provide: _notificationsmanagerservice.NotificationsManager, useValue: notificationsManager }, { provide: _authmanagerservice.AuthManager, useValue: {} }, { provide: _constants.DB_TOKEN_PROVIDER, useValue: {} }, { provide: _cacheservice.Cache, useValue: {} } ] }).compile(); module.useLogger([ 'fatal' ]); usersManager = module.get(_usersmanagerservice.UsersManager); adminUsersManager = module.get(_adminusersmanagerservice.AdminUsersManager); adminUsersQueries = module.get(_adminusersqueriesservice.AdminUsersQueries); usersQueriesService = module.get(_usersqueriesservice.UsersQueries); userTest = new _usermodel.UserModel((0, _test.generateUserTest)(), false); deleteUserDto = { deleteSpace: true, isGuest: false }; }); afterEach(()=>jest.restoreAllMocks()); afterAll(async ()=>{ await expect(adminUsersManager.deleteUserSpace(userTest.login)).resolves.not.toThrow(); }); it('instances + findUser/me/fromUserId + impersonation', async ()=>{ expect(usersManager && adminUsersManager && usersQueriesService && userTest).toBeDefined(); usersQueriesService.from = jest.fn().mockReturnValue(userTest); const u1 = await usersManager.findUser(userTest.login, true); expect(u1).toBeInstanceOf(_usermodel.UserModel); expect(u1.password).toBeUndefined(); const u2 = await usersManager.findUser(userTest.login, false); expect(u2).toBeInstanceOf(_usermodel.UserModel); expect(u2.password).toBeDefined(); const me1 = await usersManager.me(userTest); expect(me1.user.password).toBeUndefined(); usersQueriesService.from = jest.fn().mockReturnValue(null); await expect(usersManager.findUser('unknown')).resolves.toBeNull(); await expect(usersManager.me({ id: 0 })).rejects.toThrow(); usersQueriesService.from = jest.fn().mockResolvedValue(null); await expect(usersManager.fromUserId(123)).resolves.toBeNull(); const authUser = new _usermodel.UserModel({ ...(0, _test.generateUserTest)(), id: 42, clientId: 'CID', impersonatedFromId: 1 }, true); const fromUser = new _usermodel.UserModel({ ...(0, _test.generateUserTest)(), id: 42 }, true); usersQueriesService.from = jest.fn().mockResolvedValue(fromUser); const me2 = await usersManager.me(authUser); expect(me2.user.impersonated).toBe(true); expect(me2.user.clientId).toBe('CID'); }); it('paths + avatars (default/generate) + create/delete user', async ()=>{ await expect(ensurePaths()).resolves.not.toThrow(); expect(await (0, _files.isPathExists)(userTest.filesPath)).toBe(true); usersQueriesService.from = jest.fn().mockReturnValueOnce(userTest); const [p0, m0] = await usersManager.getAvatar(userTest.login); expect((0, _files.fileName)(p0)).toBe('avatar.svg'); expect(m0).toBe(_image.svgMimeType); usersQueriesService.from = jest.fn().mockReturnValueOnce(null); await expect(usersManager.getAvatar('#', true)).rejects.toThrow('does not exist'); usersQueriesService.from = jest.fn().mockReturnValue(userTest); expect(await usersManager.getAvatar(userTest.login, true)).toBeUndefined(); const [p1, m1] = await usersManager.getAvatar(userTest.login); expect((0, _files.fileName)(p1)).toBe('avatar.png'); expect(m1).toBe(_image.pngMimeType); usersQueriesService.checkUserExists = jest.fn().mockReturnValue(undefined); usersQueriesService.createUserOrGuest = jest.fn().mockReturnValue(888); usersQueriesService.clearWhiteListCaches = jest.fn(); const created = await adminUsersManager.createUserOrGuest(userTest, _user.USER_ROLE.USER); expect(created).toBeInstanceOf(_usermodel.UserModel); expect(await (0, _files.isPathExists)(created.filesPath)).toBe(true); usersQueriesService.checkUserExists = jest.fn().mockReturnValueOnce({ login: userTest.login, email: '' }).mockReturnValueOnce({ login: '', email: userTest.email }).mockReturnValueOnce(undefined); await expect(adminUsersManager.createUserOrGuest(userTest, _user.USER_ROLE.USER)).rejects.toThrow(); await expect(adminUsersManager.createUserOrGuest(userTest, _user.USER_ROLE.USER)).rejects.toThrow(); usersQueriesService.createUserOrGuest = jest.fn().mockImplementation(()=>{ throw new Error('testing'); }); await expect(adminUsersManager.createUserOrGuest(userTest, _user.USER_ROLE.USER)).rejects.toThrow(); adminUsersQueries.deleteUser = jest.fn().mockReturnValue(true); await expect(adminUsersManager.deleteUserOrGuest(userTest.id, userTest.login, deleteUserDto)).resolves.not.toThrow(); expect(await (0, _files.isPathExists)(userTest.filesPath)).toBe(false); adminUsersQueries.deleteUser = jest.fn().mockReturnValue(false); await expect(adminUsersManager.deleteUserOrGuest(userTest.id, userTest.login, deleteUserDto)).resolves.not.toThrow(); adminUsersQueries.deleteUser = jest.fn().mockImplementation(()=>{ throw new Error('testing'); }); await expect(adminUsersManager.deleteUserOrGuest(userTest.id, userTest.login, deleteUserDto)).rejects.toThrow(); }); it('logUser branches: forbidden/locked/bad/good', async ()=>{ const linkUser = new _usermodel.UserModel({ ...(0, _test.generateUserTest)(), role: _user.USER_ROLE.LINK }, false); await expect(usersManager.logUser(linkUser, 'x', '127.0.0.1')).rejects.toThrow('Account is not allowed'); const uLocked = new _usermodel.UserModel({ ...(0, _test.generateUserTest)(), isActive: false, passwordAttempts: 5 }, false); const errSpy = jest.spyOn(usersManager['logger'], 'error').mockImplementation(()=>undefined); const updSpy1 = jest.spyOn(usersManager, 'updateAccesses').mockRejectedValue(new Error('reject-locked')); await expect(usersManager.logUser(uLocked, 'pwd', 'ip')).rejects.toThrow('Account locked'); await flush(); expect(errSpy).toHaveBeenCalledWith(expect.stringContaining('reject-locked')); expect(updSpy1).toHaveBeenCalledWith(uLocked, 'ip', false); _functions.comparePassword.mockResolvedValue(false); const uBad = new _usermodel.UserModel({ ...(0, _test.generateUserTest)(), isActive: true, passwordAttempts: 0 }, false); const errSpy2 = jest.spyOn(usersManager['logger'], 'error').mockImplementation(()=>undefined); const updSpy2 = jest.spyOn(usersManager, 'updateAccesses').mockRejectedValue(new Error('reject-auth')); const out = await usersManager.logUser(uBad, 'bad', '1.1.1.1'); expect(out).toBeNull(); await flush(); expect(errSpy2).toHaveBeenCalledWith(expect.stringContaining('reject-auth')); expect(updSpy2).toHaveBeenCalledWith(uBad, '1.1.1.1', false); _functions.comparePassword.mockResolvedValue(true); const uGood = new _usermodel.UserModel({ ...(0, _test.generateUserTest)(), isActive: true, passwordAttempts: 0 }, false); const updSpy3 = jest.spyOn(usersManager, 'updateAccesses').mockResolvedValue(undefined); const pathsSpy = jest.spyOn(uGood, 'makePaths').mockResolvedValue(undefined); const out2 = await usersManager.logUser(uGood, 'good', '8.8.8.8'); expect(out2).toBe(uGood); expect(updSpy3).toHaveBeenCalledWith(uGood, '8.8.8.8', true); expect(pathsSpy).toHaveBeenCalled(); }); it('compareUserPassword + updateLanguage + updatePassword branches', async ()=>{ usersQueriesService.compareUserPassword = jest.fn().mockResolvedValue(true); await expect(usersManager.compareUserPassword(1, 'p')).resolves.toBe(true); expect(usersQueriesService.compareUserPassword).toHaveBeenCalledWith(1, 'p'); usersQueriesService.updateUserOrGuest = jest.fn().mockResolvedValue(false); await expect(usersManager.updateLanguage(userTest, { language: '' })).rejects.toThrow('Unable to update language'); expect(usersQueriesService.updateUserOrGuest).toHaveBeenCalledWith(userTest.id, { language: null }); usersQueriesService.updateUserOrGuest = jest.fn().mockResolvedValue(true); await expect(usersManager.updateLanguage(userTest, { language: 'fr' })).resolves.toBeUndefined(); usersQueriesService.selectUserProperties = jest.fn().mockResolvedValue(null); await expect(usersManager.updatePassword(userTest, { oldPassword: 'a', newPassword: 'b' })).rejects.toThrow('Unable to check password'); usersQueriesService.selectUserProperties = jest.fn().mockResolvedValue({ password: 'HASH' }); _functions.comparePassword.mockResolvedValue(false); await expect(usersManager.updatePassword(userTest, { oldPassword: 'a', newPassword: 'b' })).rejects.toThrow('Password mismatch'); _functions.comparePassword.mockResolvedValue(true); _bcryptjs.default.hash.mockResolvedValue('HASHED'); usersQueriesService.updateUserOrGuest = jest.fn().mockResolvedValue(true); await expect(usersManager.updatePassword(userTest, { oldPassword: 'a', newPassword: 'b' })).resolves.toBeUndefined(); expect(usersQueriesService.updateUserOrGuest).toHaveBeenCalledWith(userTest.id, { password: 'HASHED' }); usersQueriesService.updateUserOrGuest = jest.fn().mockResolvedValue(false); usersQueriesService.selectUserProperties = jest.fn().mockResolvedValue({ password: 'HASH' }); _functions.comparePassword.mockResolvedValue(true); _bcryptjs.default.hash.mockResolvedValue('HASHED2'); await expect(usersManager.updatePassword(userTest, { oldPassword: 'a', newPassword: 'b' })).rejects.toThrow('Unable to update password'); }); it('updateNotification + updateAccesses branches', async ()=>{ usersQueriesService.updateUserOrGuest = jest.fn().mockResolvedValue(false); await expect(usersManager.updateNotification(userTest, { notification: 1 })).rejects.toThrow('Unable to update notification'); usersQueriesService.updateUserOrGuest = jest.fn().mockResolvedValue(true); await expect(usersManager.updateNotification(userTest, { notification: 2 })).resolves.toBeUndefined(); const prevAccess1 = new Date('2021-01-01T00:00:00Z'); const u1 = new _usermodel.UserModel({ ...(0, _test.generateUserTest)(), isActive: true, passwordAttempts: 3, currentIp: '1.2.3.4', currentAccess: prevAccess1 }, false); usersQueriesService.updateUserOrGuest = jest.fn().mockResolvedValue(true); await expect(usersManager.updateAccesses(u1, '5.6.7.8', true)).resolves.toBeUndefined(); const payload1 = usersQueriesService.updateUserOrGuest.mock.calls[0][1]; expect(payload1).toMatchObject({ lastIp: '1.2.3.4', currentIp: '5.6.7.8', passwordAttempts: 0, isActive: true }); expect(payload1.lastAccess).toBe(prevAccess1); expect(payload1.currentAccess).toBeInstanceOf(Date); const prevAccess2 = new Date('2022-02-02T00:00:00Z'); const u2 = new _usermodel.UserModel({ ...(0, _test.generateUserTest)(), isActive: true, passwordAttempts: _user.USER_MAX_PASSWORD_ATTEMPTS - 1, currentIp: 'old.ip', currentAccess: prevAccess2 }, false); usersQueriesService.updateUserOrGuest = jest.fn().mockResolvedValue(true); await expect(usersManager.updateAccesses(u2, 'new.ip', false)).resolves.toBeUndefined(); const payload2 = usersQueriesService.updateUserOrGuest.mock.calls[0][1]; expect(payload2.passwordAttempts).toBe(_user.USER_MAX_PASSWORD_ATTEMPTS); expect(payload2.isActive).toBe(false); expect(payload2.lastAccess).toBe(prevAccess2); expect(payload2.lastIp).toBe('old.ip'); expect(payload2.currentIp).toBe('new.ip'); expect(payload2.currentAccess).toBeInstanceOf(Date); }); it('avatars advanced: generateIsNotExists, failure branches, base64 fallback', async ()=>{ await ensurePaths(); usersManager.findUser = jest.fn().mockResolvedValue({ getInitials: ()=>'UT' }); const [p, m] = await usersManager.getAvatar(userTest.login, false, true); expect((0, _files.fileName)(p)).toBe('avatar.png'); expect(m).toBe(_image.pngMimeType); jest.spyOn(_image, 'generateAvatar').mockImplementation(()=>errStream('gen error')); await expect(usersManager.getAvatar(userTest.login, true)).rejects.toThrow('Unable to create avatar'); usersManager.findUser = jest.fn().mockResolvedValue(null); await expect(usersManager.getAvatar(userTest.login, true)).rejects.toThrow('avatar not found'); }); it('updateAvatar branches: mime error, stream error, truncated, move fail, success', async ()=>{ await ensurePaths(); await expect(usersManager.updateAvatar(mkReq('text/plain', okStream('X')))).rejects.toThrow('Unsupported file type'); await expect(usersManager.updateAvatar(mkReq('image/png', errStream('stream error')))).rejects.toThrow('Unable to upload avatar'); const t = okStream('OK'); t.truncated = true; const mvSpy = jest.spyOn(_files, 'moveFiles').mockResolvedValue(undefined); await expect(usersManager.updateAvatar(mkReq('image/png', t))).rejects.toThrow('Image is too large (5MB max)'); expect(mvSpy).not.toHaveBeenCalled(); jest.spyOn(_files, 'moveFiles').mockRejectedValue(new Error('mv fail')); await expect(usersManager.updateAvatar(mkReq('image/png', okStream()))).rejects.toThrow('Unable to create avatar'); const mvSpy2 = jest.spyOn(_files, 'moveFiles').mockResolvedValue(undefined); await expect(usersManager.updateAvatar(mkReq('image/png', okStream()))).resolves.toBeUndefined(); const expectedSrc = _nodepath.default.join(userTest.tmpPath, 'avatar.png'); const expectedDst = _nodepath.default.join(userTest.homePath, 'avatar.png'); expect(mvSpy2).toHaveBeenCalledWith(expectedSrc, expectedDst, true); }); it('setOnlineStatus + browseGroups + getGroup', async ()=>{ usersQueriesService.setOnlineStatus = jest.fn().mockRejectedValue(new Error('boom')); expect(()=>usersManager.setOnlineStatus({ id: 1 }, 1)).not.toThrow(); usersQueriesService.browseRootGroups = jest.fn().mockResolvedValue([ { id: 1 } ]); const root = await usersManager.browseGroups(userTest, ''); expect(root.parentGroup).toBeUndefined(); expect(root.members.length).toBe(1); usersQueriesService.groupFromName = jest.fn().mockResolvedValue(null); await expect(usersManager.browseGroups(userTest, 'unknown')).rejects.toThrow('Group not found'); const group = { id: 42, name: 'Team' }; usersQueriesService.groupFromName = jest.fn().mockResolvedValue(group); usersQueriesService.browseGroupMembers = jest.fn().mockResolvedValue([ { id: 7 }, { id: 8 } ]); const g2 = await usersManager.browseGroups(userTest, 'Team'); expect(g2.parentGroup).toEqual(group); expect(g2.members).toEqual([ { id: 7 }, { id: 8 } ]); expect(usersQueriesService.browseGroupMembers).toHaveBeenCalledWith(42); usersQueriesService.getGroupWithMembers = jest.fn().mockResolvedValue({ id: 1, members: [] }); await expect(usersManager.getGroup(userTest, 1)).resolves.toEqual({ id: 1, members: [] }); usersQueriesService.getGroup = jest.fn().mockResolvedValue({ id: 2 }); await expect(usersManager.getGroup(userTest, 2, false)).resolves.toEqual({ id: 2 }); usersQueriesService.getGroup = jest.fn().mockResolvedValue(null); await expect(usersManager.getGroup(userTest, 3, false)).rejects.toThrow('You are not allowed to do this action'); }); it('create/update personal group', async ()=>{ await expect(usersManager.createPersonalGroup(userTest, { name: '' })).rejects.toThrow('Group name is missing'); usersQueriesService.checkGroupNameExists = jest.fn().mockResolvedValue(true); await expect(usersManager.createPersonalGroup(userTest, { name: 'A' })).rejects.toThrow('Name already used'); usersQueriesService.checkGroupNameExists = jest.fn().mockResolvedValue(false); usersQueriesService.createPersonalGroup = jest.fn().mockResolvedValue(10); usersQueriesService.clearWhiteListCaches = jest.fn(); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 10 }); const logSpy = jest.spyOn(usersManager['logger'], 'log').mockImplementation(()=>undefined); await expect(usersManager.createPersonalGroup(userTest, { name: 'OK' })).resolves.toEqual({ id: 10 }); expect(logSpy).toHaveBeenCalled(); usersQueriesService.createPersonalGroup = jest.fn().mockRejectedValue(new Error('db down')); await expect(usersManager.createPersonalGroup(userTest, { name: 'OK' })).rejects.toThrow('Unable to create group'); await expect(usersManager.updatePersonalGroup(userTest, 1, {})).rejects.toThrow('No changes to update'); usersManager.getGroup = jest.fn().mockResolvedValueOnce({ id: 1, type: _member.MEMBER_TYPE.GROUP }); await expect(usersManager.updatePersonalGroup(userTest, 1, { name: 'x' })).rejects.toThrow('You are not allowed to do this action'); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP }); usersQueriesService.checkGroupNameExists = jest.fn().mockResolvedValue(true); await expect(usersManager.updatePersonalGroup(userTest, 1, { name: 'dup' })).rejects.toThrow('Name already used'); usersQueriesService.checkGroupNameExists = jest.fn().mockResolvedValue(false); usersQueriesService.updateGroup = jest.fn().mockRejectedValue(new Error('oops')); await expect(usersManager.updatePersonalGroup(userTest, 1, { name: 'ok' })).rejects.toThrow('oops'); usersQueriesService.updateGroup = jest.fn().mockResolvedValue(true); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP }); await expect(usersManager.updatePersonalGroup(userTest, 1, { name: 'ok' })).resolves.not.toThrow(); expect(usersManager.getGroup).toHaveBeenCalledWith(userTest, 1, false, userTest.isAdmin); }); it('addUsersToGroup (GROUP/PGROUP)', async ()=>{ usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.GROUP, members: [ { id: 2 }, { id: 3 } ] }); usersQueriesService.usersWhitelist = jest.fn().mockResolvedValue([ 3, 4, 5 ]); await expect(usersManager.addUsersToGroup(userTest, 1, [ 2, 3 ])).rejects.toThrow('No users to add to group'); usersQueriesService.updateGroupMembers = jest.fn().mockResolvedValue(undefined); await expect(usersManager.addUsersToGroup(userTest, 1, [ 3, 4, 5 ])).resolves.toBeUndefined(); expect(usersQueriesService.updateGroupMembers).toHaveBeenCalledWith(1, { add: [ { id: 4, groupRole: _user.USER_GROUP_ROLE.MEMBER }, { id: 5, groupRole: _user.USER_GROUP_ROLE.MEMBER } ] }); expect(usersQueriesService.usersWhitelist).toHaveBeenCalledWith(userTest.id, _user.USER_ROLE.USER); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 2, type: _member.MEMBER_TYPE.PGROUP, members: [] }); usersQueriesService.usersWhitelist = jest.fn().mockResolvedValue([ 10, 11 ]); usersQueriesService.updateGroupMembers = jest.fn().mockResolvedValue(undefined); await expect(usersManager.addUsersToGroup(userTest, 2, [ 10, 11 ])).resolves.toBeUndefined(); expect(usersQueriesService.usersWhitelist).toHaveBeenCalledWith(userTest.id, undefined); expect(usersQueriesService.updateGroupMembers).toHaveBeenCalledWith(2, { add: [ { id: 10, groupRole: _user.USER_GROUP_ROLE.MEMBER }, { id: 11, groupRole: _user.USER_GROUP_ROLE.MEMBER } ] }); }); it('updateUserFromPersonalGroup', async ()=>{ usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.GROUP, members: [] }); await expect(usersManager.updateUserFromPersonalGroup(userTest, 1, 9, { role: 1 })).rejects.toThrow('You are not allowed to do this action'); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [] }); await expect(usersManager.updateUserFromPersonalGroup(userTest, 1, 9, { role: 1 })).rejects.toThrow('User was not found'); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [ { id: 9, groupRole: _user.USER_GROUP_ROLE.MANAGER }, { id: 10, groupRole: _user.USER_GROUP_ROLE.MEMBER } ] }); await expect(usersManager.updateUserFromPersonalGroup(userTest, 1, 9, { role: _user.USER_GROUP_ROLE.MEMBER })).rejects.toThrow(/group must have at least one manager/i); const spy = jest.spyOn(adminUsersManager, 'updateUserFromGroup').mockResolvedValue(undefined); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [ { id: 9, groupRole: _user.USER_GROUP_ROLE.MEMBER }, { id: 10, groupRole: _user.USER_GROUP_ROLE.MEMBER }, { id: 11, groupRole: _user.USER_GROUP_ROLE.MEMBER } ] }); await expect(usersManager.updateUserFromPersonalGroup(userTest, 1, 9, { role: _user.USER_GROUP_ROLE.MANAGER })).resolves.toBeUndefined(); expect(spy).toHaveBeenCalledWith(1, 9, { role: 1 }); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [ { id: 9, groupRole: _user.USER_GROUP_ROLE.MEMBER } ] }); await expect(usersManager.updateUserFromPersonalGroup(userTest, 1, 9, { role: _user.USER_GROUP_ROLE.MEMBER })).resolves.toBeUndefined(); spy.mockClear(); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [ { id: 9, groupRole: _user.USER_GROUP_ROLE.MANAGER }, { id: 10, groupRole: _user.USER_GROUP_ROLE.MANAGER } ] }); await expect(usersManager.updateUserFromPersonalGroup(userTest, 1, 9, { role: _user.USER_GROUP_ROLE.MEMBER })).resolves.toBeUndefined(); expect(spy).toHaveBeenCalledWith(1, 9, { role: _user.USER_GROUP_ROLE.MEMBER }); spy.mockClear(); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [ { id: 9, groupRole: _user.USER_GROUP_ROLE.MANAGER }, { id: 10, groupRole: _user.USER_GROUP_ROLE.MANAGER } ] }); await expect(usersManager.updateUserFromPersonalGroup(userTest, 1, 9, { role: _user.USER_GROUP_ROLE.MANAGER })).resolves.toBeUndefined(); expect(spy).not.toHaveBeenCalled(); }); it('removeUserFromGroup', async ()=>{ usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, members: [] }); await expect(usersManager.removeUserFromGroup(userTest, 1, 9)).rejects.toThrow('User was not found'); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.GROUP, members: [ { id: 9, groupRole: _user.USER_GROUP_ROLE.MANAGER } ] }); await expect(usersManager.removeUserFromGroup(userTest, 1, 9)).rejects.toThrow('You are not allowed to do this action'); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [ { id: 9, groupRole: _user.USER_GROUP_ROLE.MANAGER } ] }); await expect(usersManager.removeUserFromGroup(userTest, 1, 9)).rejects.toThrow('Group must have at least one manager'); usersQueriesService.updateGroupMembers = jest.fn().mockResolvedValue(undefined); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [ { id: 9, groupRole: _user.USER_GROUP_ROLE.MEMBER }, { id: 10, groupRole: _user.USER_GROUP_ROLE.MANAGER } ] }); await expect(usersManager.removeUserFromGroup(userTest, 1, 9)).resolves.toBeUndefined(); expect(usersQueriesService.updateGroupMembers).toHaveBeenCalledWith(1, { remove: [ 9 ] }); usersQueriesService.updateGroupMembers = jest.fn().mockResolvedValue(undefined); usersManager.getGroup = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [ { id: 9, groupRole: _user.USER_GROUP_ROLE.MANAGER }, { id: 10, groupRole: _user.USER_GROUP_ROLE.MANAGER } ] }); await expect(usersManager.removeUserFromGroup(userTest, 1, 9)).resolves.toBeUndefined(); expect(usersQueriesService.updateGroupMembers).toHaveBeenCalledWith(1, { remove: [ 9 ] }); }); it('leave/delete personal group', async ()=>{ usersQueriesService.getGroupWithMembers = jest.fn().mockResolvedValue(null); await expect(usersManager.leavePersonalGroup(userTest, 1)).rejects.toThrow('You are not allowed to do this action'); usersQueriesService.getGroupWithMembers = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.GROUP, members: [ { id: userTest.id } ] }); await expect(usersManager.leavePersonalGroup(userTest, 1)).rejects.toThrow('You are not allowed to do this action'); usersQueriesService.getGroupWithMembers = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [] }); await expect(usersManager.leavePersonalGroup(userTest, 1)).rejects.toThrow('User was not found'); usersQueriesService.getGroupWithMembers = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [ { id: userTest.id, groupRole: _user.USER_GROUP_ROLE.MANAGER } ] }); await expect(usersManager.leavePersonalGroup(userTest, 1)).rejects.toThrow('Group must have at least one manager'); usersQueriesService.getGroupWithMembers = jest.fn().mockResolvedValue({ id: 1, type: 2, members: [ { id: userTest.id, groupRole: _user.USER_GROUP_ROLE.MEMBER }, { id: 9, groupRole: _user.USER_GROUP_ROLE.MANAGER } ] }); const lSpy = jest.spyOn(usersManager['logger'], 'log').mockImplementation(()=>undefined); usersQueriesService.updateGroupMembers = jest.fn().mockResolvedValue(undefined); await expect(usersManager.leavePersonalGroup(userTest, 1)).resolves.toBeUndefined(); expect(lSpy).toHaveBeenCalledWith(expect.stringMatching(/has left group/)); usersQueriesService.updateGroupMembers = jest.fn().mockRejectedValue(new Error('DB')); await expect(usersManager.leavePersonalGroup(userTest, 1)).rejects.toThrow('DB'); usersQueriesService.getGroupWithMembers = jest.fn().mockResolvedValue({ id: 1, type: _member.MEMBER_TYPE.PGROUP, members: [ { id: userTest.id, groupRole: _user.USER_GROUP_ROLE.MANAGER }, { id: 9, groupRole: _user.USER_GROUP_ROLE.MANAGER } ] }); usersQueriesService.updateGroupMembers = jest.fn().mockResolvedValue(undefined); await expect(usersManager.leavePersonalGroup(userTest, 1)).resolves.toBeUndefined(); usersQueriesService.canDeletePersonalGroup = jest.fn().mockResolvedValue(false); await expect(usersManager.deletePersonalGroup(userTest, 7)).rejects.toThrow('You are not allowed to do this action'); usersQueriesService.canDeletePersonalGroup = jest.fn().mockResolvedValue(true); const wSpy = jest.spyOn(usersManager['logger'], 'warn').mockImplementation(()=>undefined); usersQueriesService.deletePersonalGroup = jest.fn().mockResolvedValue(false); await expect(usersManager.deletePersonalGroup(userTest, 7)).rejects.toThrow('Unable to delete group'); expect(wSpy).toHaveBeenCalledWith(expect.stringMatching(/does not exist/)); const lgSpy = jest.spyOn(usersManager['logger'], 'log').mockImplementation(()=>undefined); usersQueriesService.deletePersonalGroup = jest.fn().mockResolvedValue(true); await expect(usersManager.deletePersonalGroup(userTest, 7)).resolves.toBeUndefined(); expect(lgSpy).toHaveBeenCalledWith(expect.stringMatching(/was deleted/)); }); it('guests + proxies', async ()=>{ usersQueriesService.listGuests = jest.fn().mockResolvedValue([ { id: 1 } ]); await expect(usersManager.listGuests(userTest)).resolves.toEqual([ { id: 1 } ]); const checkSpy = jest.spyOn(adminUsersManager, 'checkUser').mockImplementation(()=>undefined); usersQueriesService.listGuests = jest.fn().mockResolvedValue({ id: 9 }); await expect(usersManager.getGuest(userTest, 9)).resolves.toEqual({ id: 9 }); expect(checkSpy).toHaveBeenCalled(); usersQueriesService.usersWhitelist = jest.fn().mockResolvedValue([ userTest.id, 100 ]); usersQueriesService.clearWhiteListCaches = jest.fn(); const createSpy = jest.spyOn(adminUsersManager, 'createUserOrGuest').mockResolvedValue({ id: 55 }); const dto1 = { ...userTest, managers: [ 100 ], password: 'x' }; const r = await usersManager.createGuest(userTest, dto1); expect(createSpy).toHaveBeenCalled(); expect(usersQueriesService.clearWhiteListCaches).toHaveBeenCalledWith([ userTest.id ]); expect(r).toEqual({ id: 55 }); const args1 = createSpy.mock.calls[0][0]; expect(args1.managers).toEqual(expect.arrayContaining([ userTest.id ])); createSpy.mockClear(); const dto2 = { ...userTest, managers: [ userTest.id, 100 ], password: 'y' }; await usersManager.createGuest(userTest, dto2); const args2 = createSpy.mock.calls[0][0]; expect(args2.managers.filter((m)=>m === userTest.id)).toHaveLength(1); await expect(usersManager.updateGuest(userTest, 9, {})).rejects.toThrow('No changes to update'); usersQueriesService.usersWhitelist = jest.fn().mockResolvedValue([ 1 ]); await expect(usersManager.updateGuest(userTest, 9, { managers: [ 2 ] })).rejects.toThrow('Guest must have at least one manager'); usersQueriesService.isGuestManager = jest.fn().mockResolvedValue(false); await expect(usersManager.updateGuest(userTest, 9, { email: 'a' })).rejects.toThrow('You are not allowed to do this action'); usersQueriesService.isGuestManager = jest.fn().mockResolvedValue(true); jest.spyOn(adminUsersManager, 'updateUserOrGuest').mockResolvedValue({ managers: [ { id: 999 } ] }); await expect(usersManager.updateGuest(userTest, 9, { email: 'a' })).resolves.toBeNull(); jest.spyOn(adminUsersManager, 'updateUserOrGuest').mockResolvedValue({ managers: [ { id: userTest.id } ] }); await expect(usersManager.updateGuest(userTest, 9, { email: 'a' })).resolves.toEqual({ managers: [ { id: userTest.id } ] }); usersQueriesService.usersWhitelist = jest.fn().mockResolvedValue([ userTest.id, 77 ]); usersQueriesService.isGuestManager = jest.fn().mockResolvedValue(true); jest.spyOn(adminUsersManager, 'updateUserOrGuest').mockResolvedValue({ managers: [ { id: userTest.id }, { id: 77 } ] }); await expect(usersManager.updateGuest(userTest, 9, { managers: [ userTest.id, 77 ] })).resolves.toEqual({ managers: [ { id: userTest.id }, { id: 77 } ] }); usersQueriesService.isGuestManager = jest.fn().mockResolvedValue(null); await expect(usersManager.deleteGuest(userTest, 9)).rejects.toThrow('You are not allowed to do this action'); usersQueriesService.isGuestManager = jest.fn().mockResolvedValue({ id: 9, login: 'guest' }); const delSpy = jest.spyOn(adminUsersManager, 'deleteUserOrGuest').mockResolvedValue(undefined); await expect(usersManager.deleteGuest(userTest, 9)).resolves.toBeUndefined(); expect(delSpy).toHaveBeenCalledWith(9, 'guest', { deleteSpace: true, isGuest: true }); usersQueriesService.searchUsersOrGroups = jest.fn().mockResolvedValue([ { id: 1 } ]); await expect(usersManager.searchMembers(userTest, { search: '' })).resolves.toEqual([ { id: 1 } ]); usersQueriesService.getOnlineUsers = jest.fn().mockResolvedValue([ { id: 123 } ]); await expect(usersManager.getOnlineUsers([ 123 ])).resolves.toEqual([ { id: 123 } ]); expect(usersQueriesService.getOnlineUsers).toHaveBeenCalledWith([ 123 ]); usersQueriesService.usersWhitelist = jest.fn().mockResolvedValue([ 10, 11 ]); await expect(usersManager.usersWhitelist(userTest.id)).resolves.toEqual([ 10, 11 ]); expect(usersQueriesService.usersWhitelist).toHaveBeenCalledWith(userTest.id); }); }); //# sourceMappingURL=users-manager.service.spec.js.map