/* * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ import { ConfigModule } from '@nestjs/config'; import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter'; import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Mock } from 'ts-mockery'; import { DataSource, EntityManager, Repository } from 'typeorm'; import { ApiToken } from '../api-token/api-token.entity'; import { Author } from '../authors/author.entity'; import { DefaultAccessLevel } from '../config/default-access-level.enum'; import { GuestAccess } from '../config/guest_access.enum'; import appConfigMock from '../config/mock/app.config.mock'; import authConfigMock from '../config/mock/auth.config.mock'; import databaseConfigMock from '../config/mock/database.config.mock'; import { createDefaultMockNoteConfig, registerNoteConfig, } from '../config/mock/note.config.mock'; import { NoteConfig } from '../config/note.config'; import { PermissionsUpdateInconsistentError } from '../errors/errors'; import { eventModuleConfig, NoteEvent } from '../events'; import { Group } from '../groups/group.entity'; import { GroupsModule } from '../groups/groups.module'; import { GroupsService } from '../groups/groups.service'; import { Identity } from '../identity/identity.entity'; import { LoggerModule } from '../logger/logger.module'; import { MediaUpload } from '../media/media-upload.entity'; import { Alias } from '../notes/alias.entity'; import { NoteGroupPermissionUpdateDto, NoteUserPermissionUpdateDto, } from '../notes/note-permissions.dto'; import { Note } from '../notes/note.entity'; import { NotesModule } from '../notes/notes.module'; import { Tag } from '../notes/tag.entity'; import { Edit } from '../revisions/edit.entity'; import { Revision } from '../revisions/revision.entity'; import { Session } from '../sessions/session.entity'; import { User } from '../users/user.entity'; import { UsersModule } from '../users/users.module'; import { NoteGroupPermission } from './note-group-permission.entity'; import { getNotePermissionDisplayName, NotePermission, } from './note-permission.enum'; import { NoteUserPermission } from './note-user-permission.entity'; import { PermissionsModule } from './permissions.module'; import { PermissionsService } from './permissions.service'; import { convertGuestAccessToNotePermission } from './utils/convert-guest-access-to-note-permission'; import * as FindHighestNotePermissionByGroupModule from './utils/find-highest-note-permission-by-group'; import * as FindHighestNotePermissionByUserModule from './utils/find-highest-note-permission-by-user'; jest.mock( './utils/find-highest-note-permission-by-user', () => jest.requireActual( './utils/find-highest-note-permission-by-user', ) as unknown, ); jest.mock( './utils/find-highest-note-permission-by-group', () => jest.requireActual( './utils/find-highest-note-permission-by-group', ) as unknown, ); function mockedEventEmitter(eventEmitter: EventEmitter2) { return jest.spyOn(eventEmitter, 'emit').mockImplementationOnce((event) => { expect(event).toEqual(NoteEvent.PERMISSION_CHANGE); return true; }); } function mockNoteRepo(noteRepo: Repository) { jest .spyOn(noteRepo, 'save') // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore .mockImplementationOnce(async (entry: Note) => { return entry; }); } describe('PermissionsService', () => { let service: PermissionsService; let groupService: GroupsService; let noteRepo: Repository; let userRepo: Repository; let groupRepo: Repository; let eventEmitter: EventEmitter2; let eventEmitterEmitSpy: jest.SpyInstance; const noteMockConfig: NoteConfig = createDefaultMockNoteConfig(); beforeAll(async () => { /** * We need to have *one* userRepo and *one* noteRepo for both the providers * array and the overrideProvider call, as otherwise we have two instances * and the mock of createQueryBuilder replaces the wrong one * **/ userRepo = new Repository( '', new EntityManager( new DataSource({ type: 'sqlite', database: ':memory:', }), ), undefined, ); noteRepo = new Repository( '', new EntityManager( new DataSource({ type: 'sqlite', database: ':memory:', }), ), undefined, ); const module: TestingModule = await Test.createTestingModule({ providers: [ PermissionsService, { provide: getRepositoryToken(Note), useValue: noteRepo, }, { provide: getRepositoryToken(Group), useClass: Repository, }, { provide: getRepositoryToken(User), useValue: userRepo, }, ], imports: [ LoggerModule, PermissionsModule, UsersModule, NotesModule, ConfigModule.forRoot({ isGlobal: true, load: [ appConfigMock, databaseConfigMock, authConfigMock, registerNoteConfig(noteMockConfig), ], }), GroupsModule, EventEmitterModule.forRoot(eventModuleConfig), ], }) .overrideProvider(getRepositoryToken(User)) .useValue(userRepo) .overrideProvider(getRepositoryToken(ApiToken)) .useValue({}) .overrideProvider(getRepositoryToken(Identity)) .useValue({}) .overrideProvider(getRepositoryToken(Edit)) .useValue({}) .overrideProvider(getRepositoryToken(Revision)) .useValue({}) .overrideProvider(getRepositoryToken(Note)) .useValue(noteRepo) .overrideProvider(getRepositoryToken(Tag)) .useValue({}) .overrideProvider(getRepositoryToken(NoteGroupPermission)) .useValue({}) .overrideProvider(getRepositoryToken(NoteUserPermission)) .useValue({}) .overrideProvider(getRepositoryToken(Group)) .useClass(Repository) .overrideProvider(getRepositoryToken(Session)) .useValue({}) .overrideProvider(getRepositoryToken(Author)) .useValue({}) .overrideProvider(getRepositoryToken(Alias)) .useValue({}) .compile(); service = module.get(PermissionsService); groupService = module.get(GroupsService); groupRepo = module.get>(getRepositoryToken(Group)); noteRepo = module.get>(getRepositoryToken(Note)); eventEmitter = module.get(EventEmitter2); }); beforeEach(() => { mockNoteRepo(noteRepo); eventEmitterEmitSpy = mockedEventEmitter(eventEmitter); }); afterEach(() => { jest.resetModules(); jest.restoreAllMocks(); }); // The two users we test with: const user1 = Mock.of({ id: 1 }); const user2 = Mock.of({ id: 2 }); function mockNote( owner: User, userPermissions: NoteUserPermission[] = [], groupPermissions: NoteGroupPermission[] = [], ): Note { return Mock.of({ owner: Promise.resolve(owner), userPermissions: Promise.resolve(userPermissions), groupPermissions: Promise.resolve(groupPermissions), }); } it('should be defined', () => { expect(service).toBeDefined(); }); describe('mayCreate', () => { it('allows creation for logged in', () => { expect(service.mayCreate(user1)).toBeTruthy(); }); it('allows creation of notes for guests with permission', () => { noteMockConfig.guestAccess = GuestAccess.CREATE; noteMockConfig.permissions.default.loggedIn = DefaultAccessLevel.WRITE; noteMockConfig.permissions.default.everyone = DefaultAccessLevel.WRITE; expect(service.mayCreate(null)).toBeTruthy(); }); it('denies creation of notes for guests without permission', () => { noteMockConfig.guestAccess = GuestAccess.WRITE; noteMockConfig.permissions.default.loggedIn = DefaultAccessLevel.WRITE; noteMockConfig.permissions.default.everyone = DefaultAccessLevel.WRITE; expect(service.mayCreate(null)).toBeFalsy(); }); }); describe('isOwner', () => { it('works correctly if user is owner', async () => { const note = mockNote(user1); expect(await service.isOwner(user1, note)).toBeTruthy(); }); it("works correctly if user isn't the owner", async () => { const note = mockNote(user2); expect(await service.isOwner(user1, note)).toBeFalsy(); }); it('works correctly if no user is provided', async () => { const note = mockNote(user2); expect(await service.isOwner(null, note)).toBeFalsy(); }); }); describe('checkMediaDeletePermission', () => { const noteUserPermission1 = Mock.of({ user: Promise.resolve(user1), canEdit: false, }); const noteOfUser2 = mockNote(user2, [noteUserPermission1]); describe('accepts', () => { it('for media owner', async () => { const mediaUpload = {} as MediaUpload; mediaUpload.note = Promise.resolve(noteOfUser2); mediaUpload.user = Promise.resolve(user1); expect( service.checkMediaDeletePermission(user1, mediaUpload), ).toBeTruthy(); }); it('for note owner', async () => { const mediaUpload = {} as MediaUpload; mediaUpload.note = Promise.resolve(noteOfUser2); mediaUpload.user = Promise.resolve(user1); expect( service.checkMediaDeletePermission(user2, mediaUpload), ).toBeTruthy(); }); }); describe('denies', () => { it('for not owner', async () => { const user3 = Mock.of({ id: 3 }); const mediaUpload = Mock.of(); mediaUpload.note = Promise.resolve(noteOfUser2); mediaUpload.user = Promise.resolve(user1); expect( await service.checkMediaDeletePermission(user3, mediaUpload), ).toBeFalsy(); }); }); }); describe('determinePermission', () => { const everyoneGroup = Mock.of({ id: 99 }); const loggedInGroup = Mock.of({ id: 98 }); beforeEach(() => { jest .spyOn(groupService, 'getEveryoneGroup') .mockImplementation(() => Promise.resolve(everyoneGroup)); jest .spyOn(groupService, 'getLoggedInGroup') .mockImplementation(() => Promise.resolve(loggedInGroup)); }); describe('with guest user', () => { const loggedInReadPermission = Mock.of({ canEdit: false, group: Promise.resolve(loggedInGroup), }); it(`with no everyone permission will deny`, async () => { const note = mockNote(user1, [], [loggedInReadPermission]); const foundPermission = await service.determinePermission(null, note); expect(foundPermission).toBe(NotePermission.DENY); }); describe.each([ GuestAccess.DENY, GuestAccess.READ, GuestAccess.WRITE, GuestAccess.CREATE, ])('with configured guest access %s', (guestAccess) => { beforeEach(() => { noteMockConfig.guestAccess = guestAccess; }); const guestAccessNotePermission = convertGuestAccessToNotePermission(guestAccess); describe.each([false, true])( 'with everybody group permission with edit set to %s', (canEdit) => { const editPermission = canEdit ? NotePermission.WRITE : NotePermission.READ; const expectedLimitedPermission = guestAccessNotePermission >= editPermission ? editPermission : guestAccessNotePermission; const permissionDisplayName = getNotePermissionDisplayName( expectedLimitedPermission, ); it(`will ${permissionDisplayName}`, async () => { const everybodyPermission = Mock.of({ group: Promise.resolve(everyoneGroup), canEdit: canEdit, }); const note = mockNote( user1, [], [everybodyPermission, loggedInReadPermission], ); const foundPermission = await service.determinePermission( null, note, ); expect(foundPermission).toBe(expectedLimitedPermission); }); }, ); }); }); describe('with logged in user', () => { describe('as owner will be OWNER permission', () => { it('without other permissions', async () => { const note = mockNote(user1); const foundPermission = await service.determinePermission( user1, note, ); expect(foundPermission).toBe(NotePermission.OWNER); }); it('with other lower permissions', async () => { const userPermission = Mock.of({ user: Promise.resolve(user1), canEdit: true, }); const group1 = Mock.of({ name: 'mockGroup', id: 99, members: Promise.resolve([user1]), }); const groupPermission = Mock.of({ group: Promise.resolve(group1), canEdit: true, }); const note = mockNote(user1, [userPermission], [groupPermission]); const foundPermission = await service.determinePermission( user1, note, ); expect(foundPermission).toBe(NotePermission.OWNER); }); }); describe('as non owner', () => { it('with user permission higher than group permission', async () => { jest .spyOn( FindHighestNotePermissionByUserModule, 'findHighestNotePermissionByUser', ) .mockReturnValue(Promise.resolve(NotePermission.DENY)); jest .spyOn( FindHighestNotePermissionByGroupModule, 'findHighestNotePermissionByGroup', ) .mockReturnValue(Promise.resolve(NotePermission.WRITE)); const note = mockNote(user2); const foundPermission = await service.determinePermission( user1, note, ); expect(foundPermission).toBe(NotePermission.WRITE); }); it('with group permission higher than user permission', async () => { jest .spyOn( FindHighestNotePermissionByUserModule, 'findHighestNotePermissionByUser', ) .mockReturnValue(Promise.resolve(NotePermission.WRITE)); jest .spyOn( FindHighestNotePermissionByGroupModule, 'findHighestNotePermissionByGroup', ) .mockReturnValue(Promise.resolve(NotePermission.DENY)); const note = mockNote(user2); const foundPermission = await service.determinePermission( user1, note, ); expect(foundPermission).toBe(NotePermission.WRITE); }); }); }); }); describe('updateNotePermissions', () => { const userPermissionUpdate = new NoteUserPermissionUpdateDto(); userPermissionUpdate.username = 'hardcoded'; userPermissionUpdate.canEdit = true; const groupPermissionUpdate = new NoteGroupPermissionUpdateDto(); groupPermissionUpdate.groupName = 'testGroup'; groupPermissionUpdate.canEdit = false; const user = User.create(userPermissionUpdate.username, 'Testy') as User; const group = Group.create( groupPermissionUpdate.groupName, groupPermissionUpdate.groupName, false, ) as Group; const note = Note.create(user) as Note; it('emits PERMISSION_CHANGE event', async () => { expect(eventEmitterEmitSpy).not.toHaveBeenCalled(); await service.updateNotePermissions(note, { sharedToUsers: [], sharedToGroups: [], }); expect(eventEmitterEmitSpy).toHaveBeenCalled(); }); describe('works', () => { it('with empty GroupPermissions and with empty UserPermissions', async () => { const savedNote = await service.updateNotePermissions(note, { sharedToUsers: [], sharedToGroups: [], }); expect(await savedNote.userPermissions).toHaveLength(0); expect(await savedNote.groupPermissions).toHaveLength(0); }); it('with empty GroupPermissions and with new UserPermissions', async () => { jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user); const savedNote = await service.updateNotePermissions(note, { sharedToUsers: [userPermissionUpdate], sharedToGroups: [], }); expect(await savedNote.userPermissions).toHaveLength(1); expect( (await (await savedNote.userPermissions)[0].user).username, ).toEqual(userPermissionUpdate.username); expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); expect(await savedNote.groupPermissions).toHaveLength(0); }); it('with empty GroupPermissions and with existing UserPermissions', async () => { const noteWithPreexistingPermissions: Note = { ...note }; noteWithPreexistingPermissions.userPermissions = Promise.resolve([ { id: 1, note: Promise.resolve(noteWithPreexistingPermissions), user: Promise.resolve(user), canEdit: !userPermissionUpdate.canEdit, }, ]); jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user); const savedNote = await service.updateNotePermissions(note, { sharedToUsers: [userPermissionUpdate], sharedToGroups: [], }); expect(await savedNote.userPermissions).toHaveLength(1); expect( (await (await savedNote.userPermissions)[0].user).username, ).toEqual(userPermissionUpdate.username); expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); expect(await savedNote.groupPermissions).toHaveLength(0); }); it('with new GroupPermissions and with empty UserPermissions', async () => { jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group); const savedNote = await service.updateNotePermissions(note, { sharedToUsers: [], sharedToGroups: [groupPermissionUpdate], }); expect(await savedNote.userPermissions).toHaveLength(0); expect( (await (await savedNote.groupPermissions)[0].group).name, ).toEqual(groupPermissionUpdate.groupName); expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpdate.canEdit, ); }); it('with new GroupPermissions and with new UserPermissions', async () => { jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user); jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group); const savedNote = await service.updateNotePermissions(note, { sharedToUsers: [userPermissionUpdate], sharedToGroups: [groupPermissionUpdate], }); expect( (await (await savedNote.userPermissions)[0].user).username, ).toEqual(userPermissionUpdate.username); expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); expect( (await (await savedNote.groupPermissions)[0].group).name, ).toEqual(groupPermissionUpdate.groupName); expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpdate.canEdit, ); }); it('with new GroupPermissions and with existing UserPermissions', async () => { const noteWithUserPermission: Note = { ...note }; noteWithUserPermission.userPermissions = Promise.resolve([ { id: 1, note: Promise.resolve(noteWithUserPermission), user: Promise.resolve(user), canEdit: !userPermissionUpdate.canEdit, }, ]); jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user); jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group); const savedNote = await service.updateNotePermissions( noteWithUserPermission, { sharedToUsers: [userPermissionUpdate], sharedToGroups: [groupPermissionUpdate], }, ); expect( (await (await savedNote.userPermissions)[0].user).username, ).toEqual(userPermissionUpdate.username); expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); expect( (await (await savedNote.groupPermissions)[0].group).name, ).toEqual(groupPermissionUpdate.groupName); expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpdate.canEdit, ); }); it('with existing GroupPermissions and with empty UserPermissions', async () => { const noteWithPreexistingPermissions: Note = { ...note }; noteWithPreexistingPermissions.groupPermissions = Promise.resolve([ { id: 1, note: Promise.resolve(noteWithPreexistingPermissions), group: Promise.resolve(group), canEdit: !groupPermissionUpdate.canEdit, }, ]); jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group); const savedNote = await service.updateNotePermissions( noteWithPreexistingPermissions, { sharedToUsers: [], sharedToGroups: [groupPermissionUpdate], }, ); expect(await savedNote.userPermissions).toHaveLength(0); expect( (await (await savedNote.groupPermissions)[0].group).name, ).toEqual(groupPermissionUpdate.groupName); expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpdate.canEdit, ); }); it('with existing GroupPermissions and with new UserPermissions', async () => { const noteWithPreexistingPermissions: Note = { ...note }; noteWithPreexistingPermissions.groupPermissions = Promise.resolve([ { id: 1, note: Promise.resolve(noteWithPreexistingPermissions), group: Promise.resolve(group), canEdit: !groupPermissionUpdate.canEdit, }, ]); jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user); jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group); const savedNote = await service.updateNotePermissions( noteWithPreexistingPermissions, { sharedToUsers: [userPermissionUpdate], sharedToGroups: [groupPermissionUpdate], }, ); expect( (await (await savedNote.userPermissions)[0].user).username, ).toEqual(userPermissionUpdate.username); expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); expect( (await (await savedNote.groupPermissions)[0].group).name, ).toEqual(groupPermissionUpdate.groupName); expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpdate.canEdit, ); }); it('with existing GroupPermissions and with existing UserPermissions', async () => { const noteWithPreexistingPermissions: Note = { ...note }; noteWithPreexistingPermissions.groupPermissions = Promise.resolve([ { id: 1, note: Promise.resolve(noteWithPreexistingPermissions), group: Promise.resolve(group), canEdit: !groupPermissionUpdate.canEdit, }, ]); noteWithPreexistingPermissions.userPermissions = Promise.resolve([ { id: 1, note: Promise.resolve(noteWithPreexistingPermissions), user: Promise.resolve(user), canEdit: !userPermissionUpdate.canEdit, }, ]); jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user); jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group); const savedNote = await service.updateNotePermissions( noteWithPreexistingPermissions, { sharedToUsers: [userPermissionUpdate], sharedToGroups: [groupPermissionUpdate], }, ); expect( (await (await savedNote.userPermissions)[0].user).username, ).toEqual(userPermissionUpdate.username); expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); expect( (await (await savedNote.groupPermissions)[0].group).name, ).toEqual(groupPermissionUpdate.groupName); expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpdate.canEdit, ); }); }); describe('fails:', () => { it('userPermissions has duplicate entries', async () => { await expect( service.updateNotePermissions(note, { sharedToUsers: [userPermissionUpdate, userPermissionUpdate], sharedToGroups: [], }), ).rejects.toThrow(PermissionsUpdateInconsistentError); }); it('groupPermissions has duplicate entries', async () => { await expect( service.updateNotePermissions(note, { sharedToUsers: [], sharedToGroups: [groupPermissionUpdate, groupPermissionUpdate], }), ).rejects.toThrow(PermissionsUpdateInconsistentError); }); it('userPermissions and groupPermissions have duplicate entries', async () => { await expect( service.updateNotePermissions(note, { sharedToUsers: [userPermissionUpdate, userPermissionUpdate], sharedToGroups: [groupPermissionUpdate, groupPermissionUpdate], }), ).rejects.toThrow(PermissionsUpdateInconsistentError); }); }); }); describe('setUserPermission', () => { it('emits PERMISSION_CHANGE event', async () => { const note = Note.create(null) as Note; const user = User.create('test', 'Testy') as User; expect(eventEmitterEmitSpy).not.toHaveBeenCalled(); await service.setUserPermission(note, user, true); expect(eventEmitterEmitSpy).toHaveBeenCalled(); }); describe('works', () => { it('with user not added if owner', async () => { const user = User.create('test', 'Testy') as User; const note = Note.create(user) as Note; const resultNote = await service.setUserPermission(note, user, true); expect(await resultNote.userPermissions).toHaveLength(0); }); it('with user not added before and editable', async () => { const note = Note.create(null) as Note; const user = User.create('test', 'Testy') as User; const resultNote = await service.setUserPermission(note, user, true); const noteUserPermission = NoteUserPermission.create(user, note, true); expect((await resultNote.userPermissions)[0]).toStrictEqual( noteUserPermission, ); }); it('with user not added before and not editable', async () => { const note = Note.create(null) as Note; const user = User.create('test', 'Testy') as User; const resultNote = await service.setUserPermission(note, user, false); const noteUserPermission = NoteUserPermission.create(user, note, false); expect((await resultNote.userPermissions)[0]).toStrictEqual( noteUserPermission, ); }); it('with user added before and editable', async () => { const note = Note.create(null) as Note; const user = User.create('test', 'Testy') as User; note.userPermissions = Promise.resolve([ NoteUserPermission.create(user, note, false), ]); const resultNote = await service.setUserPermission(note, user, true); const noteUserPermission = NoteUserPermission.create(user, note, true); expect((await resultNote.userPermissions)[0]).toStrictEqual( noteUserPermission, ); }); it('with user added before and not editable', async () => { const note = Note.create(null) as Note; const user = User.create('test', 'Testy') as User; note.userPermissions = Promise.resolve([ NoteUserPermission.create(user, note, true), ]); const resultNote = await service.setUserPermission(note, user, false); const noteUserPermission = NoteUserPermission.create(user, note, false); expect((await resultNote.userPermissions)[0]).toStrictEqual( noteUserPermission, ); }); }); }); describe('removeUserPermission', () => { it('emits PERMISSION_CHANGE event', async () => { const note = Note.create(null) as Note; const user = User.create('test', 'Testy') as User; note.userPermissions = Promise.resolve([ NoteUserPermission.create(user, note, true), ]); expect(eventEmitterEmitSpy).not.toHaveBeenCalled(); await service.removeUserPermission(note, user); expect(eventEmitterEmitSpy).toHaveBeenCalled(); }); describe('works', () => { const note = Note.create(null) as Note; const user1 = Mock.of({ id: 1 }); const user2 = Mock.of({ id: 2 }); it('with user added before and editable', async () => { const noteUserPermission1 = NoteUserPermission.create( user1, note, true, ); const noteUserPermission2 = NoteUserPermission.create( user2, note, true, ); note.userPermissions = Promise.resolve([ noteUserPermission1, noteUserPermission2, ]); const resultNote = await service.removeUserPermission(note, user1); expect(await resultNote.userPermissions).toStrictEqual([ noteUserPermission2, ]); }); it('with user added before and not editable', async () => { const noteUserPermission1 = NoteUserPermission.create( user1, note, false, ); const noteUserPermission2 = NoteUserPermission.create( user2, note, false, ); note.userPermissions = Promise.resolve([ noteUserPermission1, noteUserPermission2, ]); const resultNote = await service.removeUserPermission(note, user1); expect(await resultNote.userPermissions).toStrictEqual([ noteUserPermission2, ]); }); }); }); describe('setGroupPermission', () => { it('emits PERMISSION_CHANGE event', async () => { const note = Note.create(null) as Note; const group = Group.create('test', 'Testy', false) as Group; expect(eventEmitterEmitSpy).not.toHaveBeenCalled(); await service.setGroupPermission(note, group, true); expect(eventEmitterEmitSpy).toHaveBeenCalled(); }); describe('works', () => { it('with group not added before and editable', async () => { const note = Note.create(null) as Note; const group = Group.create('test', 'Testy', false) as Group; const resultNote = await service.setGroupPermission(note, group, true); const noteGroupPermission = NoteGroupPermission.create( group, note, true, ); expect((await resultNote.groupPermissions)[0]).toStrictEqual( noteGroupPermission, ); }); it('with group not added before and not editable', async () => { const note = Note.create(null) as Note; const group = Group.create('test', 'Testy', false) as Group; const resultNote = await service.setGroupPermission(note, group, false); const noteGroupPermission = NoteGroupPermission.create( group, note, false, ); expect((await resultNote.groupPermissions)[0]).toStrictEqual( noteGroupPermission, ); }); it('with group added before and editable', async () => { const note = Note.create(null) as Note; const group = Group.create('test', 'Testy', false) as Group; note.groupPermissions = Promise.resolve([ NoteGroupPermission.create(group, note, false), ]); const resultNote = await service.setGroupPermission(note, group, true); const noteGroupPermission = NoteGroupPermission.create( group, note, true, ); expect((await resultNote.groupPermissions)[0]).toStrictEqual( noteGroupPermission, ); }); it('with group added before and not editable', async () => { const note = Note.create(null) as Note; const group = Group.create('test', 'Testy', false) as Group; note.groupPermissions = Promise.resolve([ NoteGroupPermission.create(group, note, true), ]); const resultNote = await service.setGroupPermission(note, group, false); const noteGroupPermission = NoteGroupPermission.create( group, note, false, ); expect((await resultNote.groupPermissions)[0]).toStrictEqual( noteGroupPermission, ); }); }); }); describe('removeGroupPermission', () => { it('emits PERMISSION_CHANGE event', async () => { const note = Note.create(null) as Note; const group = Group.create('test', 'Testy', false) as Group; note.groupPermissions = Promise.resolve([ NoteGroupPermission.create(group, note, true), ]); expect(eventEmitterEmitSpy).not.toHaveBeenCalled(); await service.removeGroupPermission(note, group); expect(eventEmitterEmitSpy).toHaveBeenCalled(); }); describe('works', () => { const note = Note.create(null) as Note; const group1 = Mock.of({ id: 1 }); const group2 = Mock.of({ id: 2 }); it('with editable group', async () => { const noteGroupPermission1 = NoteGroupPermission.create( group1, note, true, ); const noteGroupPermission2 = NoteGroupPermission.create( group2, note, true, ); note.groupPermissions = Promise.resolve([ noteGroupPermission1, noteGroupPermission2, ]); const resultNote = await service.removeGroupPermission(note, group1); expect(await resultNote.groupPermissions).toStrictEqual([ noteGroupPermission2, ]); }); it('with not editable group', async () => { const noteGroupPermission1 = NoteGroupPermission.create( group1, note, false, ); const noteGroupPermission2 = NoteGroupPermission.create( group2, note, false, ); note.groupPermissions = Promise.resolve([ noteGroupPermission1, noteGroupPermission2, ]); const resultNote = await service.removeGroupPermission(note, group1); expect(await resultNote.groupPermissions).toStrictEqual([ noteGroupPermission2, ]); }); }); }); describe('changeOwner', () => { it('works', async () => { const note = Note.create(null) as Note; const user = User.create('test', 'Testy') as User; const resultNote = await service.changeOwner(note, user); expect(await resultNote.owner).toStrictEqual(user); }); }); });