import { BadRequestException } from '@nestjs/common'; import { MemoryService } from 'src/services/memory.service'; import { factory, newUuid, newUuids } from 'test/small.factory'; import { newTestService, ServiceMocks } from 'test/utils'; describe(MemoryService.name, () => { let sut: MemoryService; let mocks: ServiceMocks; beforeEach(() => { ({ sut, mocks } = newTestService(MemoryService)); }); it('should be defined', () => { expect(sut).toBeDefined(); }); describe('onMemoryCleanup', () => { it('should clean up memories', async () => { mocks.memory.cleanup.mockResolvedValue([]); await sut.onMemoriesCleanup(); expect(mocks.memory.cleanup).toHaveBeenCalled(); }); }); describe('search', () => { it('should search memories', async () => { const [userId] = newUuids(); const asset = factory.asset(); const memory1 = factory.memory({ ownerId: userId, assets: [asset] }); const memory2 = factory.memory({ ownerId: userId }); mocks.memory.search.mockResolvedValue([memory1, memory2]); await expect(sut.search(factory.auth({ user: { id: userId } }), {})).resolves.toEqual( expect.arrayContaining([ expect.objectContaining({ id: memory1.id, assets: [expect.objectContaining({ id: asset.id })] }), expect.objectContaining({ id: memory2.id, assets: [] }), ]), ); }); it('should map ', async () => { mocks.memory.search.mockResolvedValue([]); await expect(sut.search(factory.auth(), {})).resolves.toEqual([]); }); }); describe('get', () => { it('should throw an error when no access', async () => { await expect(sut.get(factory.auth(), 'not-found')).rejects.toBeInstanceOf(BadRequestException); }); it('should throw an error when the memory is not found', async () => { const [memoryId] = newUuids(); mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memoryId])); mocks.memory.get.mockResolvedValue(void 0); await expect(sut.get(factory.auth(), memoryId)).rejects.toBeInstanceOf(BadRequestException); }); it('should get a memory by id', async () => { const userId = newUuid(); const memory = factory.memory({ ownerId: userId }); mocks.memory.get.mockResolvedValue(memory); mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memory.id])); await expect(sut.get(factory.auth({ user: { id: userId } }), memory.id)).resolves.toMatchObject({ id: memory.id, }); expect(mocks.memory.get).toHaveBeenCalledWith(memory.id); expect(mocks.access.memory.checkOwnerAccess).toHaveBeenCalledWith(memory.ownerId, new Set([memory.id])); }); }); describe('create', () => { it('should skip assets the user does not have access to', async () => { const [assetId, userId] = newUuids(); const memory = factory.memory({ ownerId: userId }); mocks.memory.create.mockResolvedValue(memory); await expect( sut.create(factory.auth({ user: { id: userId } }), { type: memory.type, data: memory.data, memoryAt: memory.memoryAt, isSaved: memory.isSaved, assetIds: [assetId], }), ).resolves.toMatchObject({ assets: [] }); expect(mocks.memory.create).toHaveBeenCalledWith( { type: memory.type, data: memory.data, ownerId: memory.ownerId, memoryAt: memory.memoryAt, isSaved: memory.isSaved, }, new Set(), ); }); it('should create a memory', async () => { const [assetId, userId] = newUuids(); const asset = factory.asset({ id: assetId, ownerId: userId }); const memory = factory.memory({ assets: [asset] }); mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id])); mocks.memory.create.mockResolvedValue(memory); await expect( sut.create(factory.auth({ user: { id: userId } }), { type: memory.type, data: memory.data, assetIds: memory.assets.map((asset) => asset.id), memoryAt: memory.memoryAt, }), ).resolves.toBeDefined(); expect(mocks.memory.create).toHaveBeenCalledWith( expect.objectContaining({ ownerId: userId }), new Set([assetId]), ); }); it('should create a memory without assets', async () => { const memory = factory.memory(); mocks.memory.create.mockResolvedValue(memory); await expect( sut.create(factory.auth(), { type: memory.type, data: memory.data, memoryAt: memory.memoryAt }), ).resolves.toBeDefined(); }); }); describe('update', () => { it('should require access', async () => { await expect(sut.update(factory.auth(), 'not-found', { isSaved: true })).rejects.toBeInstanceOf( BadRequestException, ); expect(mocks.memory.update).not.toHaveBeenCalled(); }); it('should update a memory', async () => { const memory = factory.memory(); mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memory.id])); mocks.memory.update.mockResolvedValue(memory); await expect(sut.update(factory.auth(), memory.id, { isSaved: true })).resolves.toBeDefined(); expect(mocks.memory.update).toHaveBeenCalledWith(memory.id, expect.objectContaining({ isSaved: true })); }); }); describe('remove', () => { it('should require access', async () => { await expect(sut.remove(factory.auth(), newUuid())).rejects.toBeInstanceOf(BadRequestException); expect(mocks.memory.delete).not.toHaveBeenCalled(); }); it('should delete a memory', async () => { const memoryId = newUuid(); mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memoryId])); mocks.memory.delete.mockResolvedValue(); await expect(sut.remove(factory.auth(), memoryId)).resolves.toBeUndefined(); expect(mocks.memory.delete).toHaveBeenCalledWith(memoryId); }); }); describe('addAssets', () => { it('should require memory access', async () => { const [memoryId, assetId] = newUuids(); await expect(sut.addAssets(factory.auth(), memoryId, { ids: [assetId] })).rejects.toBeInstanceOf( BadRequestException, ); expect(mocks.memory.addAssetIds).not.toHaveBeenCalled(); }); it('should require asset access', async () => { const assetId = newUuid(); const memory = factory.memory(); mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memory.id])); mocks.memory.get.mockResolvedValue(memory); mocks.memory.getAssetIds.mockResolvedValue(new Set()); await expect(sut.addAssets(factory.auth(), memory.id, { ids: [assetId] })).resolves.toEqual([ { error: 'no_permission', id: assetId, success: false }, ]); expect(mocks.memory.addAssetIds).not.toHaveBeenCalled(); }); it('should skip assets already in the memory', async () => { const asset = factory.asset(); const memory = factory.memory({ assets: [asset] }); mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memory.id])); mocks.memory.get.mockResolvedValue(memory); mocks.memory.getAssetIds.mockResolvedValue(new Set([asset.id])); await expect(sut.addAssets(factory.auth(), memory.id, { ids: [asset.id] })).resolves.toEqual([ { error: 'duplicate', id: asset.id, success: false }, ]); expect(mocks.memory.addAssetIds).not.toHaveBeenCalled(); }); it('should add assets', async () => { const assetId = newUuid(); const memory = factory.memory(); mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memory.id])); mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetId])); mocks.memory.get.mockResolvedValue(memory); mocks.memory.update.mockResolvedValue(memory); mocks.memory.getAssetIds.mockResolvedValue(new Set()); mocks.memory.addAssetIds.mockResolvedValue(); await expect(sut.addAssets(factory.auth(), memory.id, { ids: [assetId] })).resolves.toEqual([ { id: assetId, success: true }, ]); expect(mocks.memory.addAssetIds).toHaveBeenCalledWith(memory.id, [assetId]); }); }); describe('removeAssets', () => { it('should require memory access', async () => { await expect(sut.removeAssets(factory.auth(), 'not-found', { ids: ['asset1'] })).rejects.toBeInstanceOf( BadRequestException, ); expect(mocks.memory.removeAssetIds).not.toHaveBeenCalled(); }); it('should skip assets not in the memory', async () => { mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set(['memory1'])); mocks.memory.getAssetIds.mockResolvedValue(new Set()); await expect(sut.removeAssets(factory.auth(), 'memory1', { ids: ['not-found'] })).resolves.toEqual([ { error: 'not_found', id: 'not-found', success: false }, ]); expect(mocks.memory.removeAssetIds).not.toHaveBeenCalled(); }); it('should remove assets', async () => { const memory = factory.memory(); const asset = factory.asset(); mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memory.id])); mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id])); mocks.memory.getAssetIds.mockResolvedValue(new Set([asset.id])); mocks.memory.removeAssetIds.mockResolvedValue(); mocks.memory.update.mockResolvedValue(memory); await expect(sut.removeAssets(factory.auth(), memory.id, { ids: [asset.id] })).resolves.toEqual([ { id: asset.id, success: true }, ]); expect(mocks.memory.removeAssetIds).toHaveBeenCalledWith(memory.id, [asset.id]); }); }); });