From ee50d0b42aeb97620e4c89f12dca0a95c035509c Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Tue, 15 Oct 2024 01:18:41 +0200 Subject: [PATCH] chore: stack service unit tests (#13441) --- server/src/services/stack.service.spec.ts | 193 ++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 server/src/services/stack.service.spec.ts diff --git a/server/src/services/stack.service.spec.ts b/server/src/services/stack.service.spec.ts new file mode 100644 index 0000000000000..4e8813145cd89 --- /dev/null +++ b/server/src/services/stack.service.spec.ts @@ -0,0 +1,193 @@ +import { BadRequestException } from '@nestjs/common'; +import { IEventRepository } from 'src/interfaces/event.interface'; +import { IStackRepository } from 'src/interfaces/stack.interface'; +import { StackService } from 'src/services/stack.service'; +import { assetStub, stackStub } from 'test/fixtures/asset.stub'; +import { authStub } from 'test/fixtures/auth.stub'; +import { IAccessRepositoryMock } from 'test/repositories/access.repository.mock'; +import { newTestService } from 'test/utils'; +import { Mocked } from 'vitest'; + +describe(StackService.name, () => { + let sut: StackService; + + let accessMock: IAccessRepositoryMock; + let eventMock: Mocked; + let stackMock: Mocked; + + beforeEach(() => { + ({ sut, accessMock, eventMock, stackMock } = newTestService(StackService)); + }); + + it('should be defined', () => { + expect(sut).toBeDefined(); + }); + + describe('search', () => { + it('should search stacks', async () => { + stackMock.search.mockResolvedValue([stackStub('stack-id', [assetStub.image])]); + + await sut.search(authStub.admin, { primaryAssetId: assetStub.image.id }); + expect(stackMock.search).toHaveBeenCalledWith({ + ownerId: authStub.admin.user.id, + primaryAssetId: assetStub.image.id, + }); + }); + }); + + describe('create', () => { + it('should require asset.update permissions', async () => { + await expect( + sut.create(authStub.admin, { assetIds: [assetStub.image.id, assetStub.image1.id] }), + ).rejects.toBeInstanceOf(BadRequestException); + + expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalled(); + expect(stackMock.create).not.toHaveBeenCalled(); + }); + + it('should create a stack', async () => { + accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set([assetStub.image.id, assetStub.image1.id])); + stackMock.create.mockResolvedValue(stackStub('stack-id', [assetStub.image, assetStub.image1])); + await expect( + sut.create(authStub.admin, { assetIds: [assetStub.image.id, assetStub.image1.id] }), + ).resolves.toEqual({ + id: 'stack-id', + primaryAssetId: assetStub.image.id, + assets: [ + expect.objectContaining({ id: assetStub.image.id }), + expect.objectContaining({ id: assetStub.image1.id }), + ], + }); + + expect(eventMock.emit).toHaveBeenCalledWith('stack.create', { + stackId: 'stack-id', + userId: authStub.admin.user.id, + }); + expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalled(); + }); + }); + + describe('get', () => { + it('should require stack.read permissions', async () => { + await expect(sut.get(authStub.admin, 'stack-id')).rejects.toBeInstanceOf(BadRequestException); + + expect(accessMock.stack.checkOwnerAccess).toHaveBeenCalled(); + expect(stackMock.getById).not.toHaveBeenCalled(); + }); + + it('should fail if stack could not be found', async () => { + accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + + await expect(sut.get(authStub.admin, 'stack-id')).rejects.toBeInstanceOf(Error); + + expect(accessMock.stack.checkOwnerAccess).toHaveBeenCalled(); + expect(stackMock.getById).toHaveBeenCalledWith('stack-id'); + }); + + it('should get stack', async () => { + accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + stackMock.getById.mockResolvedValue(stackStub('stack-id', [assetStub.image, assetStub.image1])); + + await expect(sut.get(authStub.admin, 'stack-id')).resolves.toEqual({ + id: 'stack-id', + primaryAssetId: assetStub.image.id, + assets: [ + expect.objectContaining({ id: assetStub.image.id }), + expect.objectContaining({ id: assetStub.image1.id }), + ], + }); + expect(accessMock.stack.checkOwnerAccess).toHaveBeenCalled(); + expect(stackMock.getById).toHaveBeenCalledWith('stack-id'); + }); + }); + + describe('update', () => { + it('should require stack.update permissions', async () => { + await expect(sut.update(authStub.admin, 'stack-id', {})).rejects.toBeInstanceOf(BadRequestException); + + expect(stackMock.getById).not.toHaveBeenCalled(); + expect(stackMock.update).not.toHaveBeenCalled(); + expect(eventMock.emit).not.toHaveBeenCalled(); + }); + + it('should fail if stack could not be found', async () => { + accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + + await expect(sut.update(authStub.admin, 'stack-id', {})).rejects.toBeInstanceOf(Error); + + expect(stackMock.getById).toHaveBeenCalledWith('stack-id'); + expect(stackMock.update).not.toHaveBeenCalled(); + expect(eventMock.emit).not.toHaveBeenCalled(); + }); + + it('should fail if the provided primary asset id is not in the stack', async () => { + accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + stackMock.getById.mockResolvedValue(stackStub('stack-id', [assetStub.image, assetStub.image1])); + + await expect(sut.update(authStub.admin, 'stack-id', { primaryAssetId: 'unknown-asset' })).rejects.toBeInstanceOf( + BadRequestException, + ); + + expect(stackMock.getById).toHaveBeenCalledWith('stack-id'); + expect(stackMock.update).not.toHaveBeenCalled(); + expect(eventMock.emit).not.toHaveBeenCalled(); + }); + + it('should update stack', async () => { + accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + stackMock.getById.mockResolvedValue(stackStub('stack-id', [assetStub.image, assetStub.image1])); + stackMock.update.mockResolvedValue(stackStub('stack-id', [assetStub.image, assetStub.image1])); + + await sut.update(authStub.admin, 'stack-id', { primaryAssetId: assetStub.image1.id }); + + expect(stackMock.getById).toHaveBeenCalledWith('stack-id'); + expect(stackMock.update).toHaveBeenCalledWith({ id: 'stack-id', primaryAssetId: assetStub.image1.id }); + expect(eventMock.emit).toHaveBeenCalledWith('stack.update', { + stackId: 'stack-id', + userId: authStub.admin.user.id, + }); + }); + }); + + describe('delete', () => { + it('should require stack.delete permissions', async () => { + await expect(sut.delete(authStub.admin, 'stack-id')).rejects.toBeInstanceOf(BadRequestException); + + expect(stackMock.delete).not.toHaveBeenCalled(); + expect(eventMock.emit).not.toHaveBeenCalled(); + }); + + it('should delete stack', async () => { + accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + + await sut.delete(authStub.admin, 'stack-id'); + + expect(stackMock.delete).toHaveBeenCalledWith('stack-id'); + expect(eventMock.emit).toHaveBeenCalledWith('stack.delete', { + stackId: 'stack-id', + userId: authStub.admin.user.id, + }); + }); + }); + + describe('deleteAll', () => { + it('should require stack.delete permissions', async () => { + await expect(sut.deleteAll(authStub.admin, { ids: ['stack-id'] })).rejects.toBeInstanceOf(BadRequestException); + + expect(stackMock.deleteAll).not.toHaveBeenCalled(); + expect(eventMock.emit).not.toHaveBeenCalled(); + }); + + it('should delete all stacks', async () => { + accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + + await sut.deleteAll(authStub.admin, { ids: ['stack-id'] }); + + expect(stackMock.deleteAll).toHaveBeenCalledWith(['stack-id']); + expect(eventMock.emit).toHaveBeenCalledWith('stacks.delete', { + stackIds: ['stack-id'], + userId: authStub.admin.user.id, + }); + }); + }); +});