import { assetEntityStub, newAssetRepositoryMock, newJobRepositoryMock, newStorageRepositoryMock } from '@test'; import { constants } from 'fs/promises'; import { IAssetRepository, WithoutProperty, WithProperty } from '../asset'; import { IJobRepository, JobName } from '../job'; import { IStorageRepository } from '../storage'; import { MetadataService } from './metadata.service'; describe(MetadataService.name, () => { let sut: MetadataService; let assetMock: jest.Mocked; let jobMock: jest.Mocked; let storageMock: jest.Mocked; beforeEach(async () => { assetMock = newAssetRepositoryMock(); jobMock = newJobRepositoryMock(); storageMock = newStorageRepositoryMock(); sut = new MetadataService(assetMock, jobMock, storageMock); }); it('should be defined', () => { expect(sut).toBeDefined(); }); describe('handleQueueSidecar', () => { it('should queue assets with sidecar files', async () => { assetMock.getWith.mockResolvedValue({ items: [assetEntityStub.sidecar], hasNextPage: false }); await sut.handleQueueSidecar({ force: true }); expect(assetMock.getWith).toHaveBeenCalledWith({ take: 1000, skip: 0 }, WithProperty.SIDECAR); expect(assetMock.getWithout).not.toHaveBeenCalled(); expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.SIDECAR_SYNC, data: { id: assetEntityStub.sidecar.id }, }); }); it('should queue assets without sidecar files', async () => { assetMock.getWithout.mockResolvedValue({ items: [assetEntityStub.image], hasNextPage: false }); await sut.handleQueueSidecar({ force: false }); expect(assetMock.getWithout).toHaveBeenCalledWith({ take: 1000, skip: 0 }, WithoutProperty.SIDECAR); expect(assetMock.getWith).not.toHaveBeenCalled(); expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.SIDECAR_DISCOVERY, data: { id: assetEntityStub.image.id }, }); }); }); describe('handleSidecarSync', () => { it('should not error', async () => { await sut.handleSidecarSync(); }); }); describe('handleSidecarDiscovery', () => { it('should skip hidden assets', async () => { assetMock.getByIds.mockResolvedValue([assetEntityStub.livePhotoMotionAsset]); await sut.handleSidecarDiscovery({ id: assetEntityStub.livePhotoMotionAsset.id }); expect(storageMock.checkFileExists).not.toHaveBeenCalled(); }); it('should skip assets with a sidecar path', async () => { assetMock.getByIds.mockResolvedValue([assetEntityStub.sidecar]); await sut.handleSidecarDiscovery({ id: assetEntityStub.sidecar.id }); expect(storageMock.checkFileExists).not.toHaveBeenCalled(); }); it('should do nothing when a sidecar is not found ', async () => { assetMock.getByIds.mockResolvedValue([assetEntityStub.image]); storageMock.checkFileExists.mockResolvedValue(false); await sut.handleSidecarDiscovery({ id: assetEntityStub.image.id }); expect(assetMock.save).not.toHaveBeenCalled(); }); it('should update a image asset when a sidecar is found', async () => { assetMock.getByIds.mockResolvedValue([assetEntityStub.image]); assetMock.save.mockResolvedValue(assetEntityStub.image); storageMock.checkFileExists.mockResolvedValue(true); await sut.handleSidecarDiscovery({ id: assetEntityStub.image.id }); expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.jpg.xmp', constants.R_OK); expect(assetMock.save).toHaveBeenCalledWith({ id: assetEntityStub.image.id, sidecarPath: '/original/path.jpg.xmp', }); }); it('should update a video asset when a sidecar is found', async () => { assetMock.getByIds.mockResolvedValue([assetEntityStub.video]); assetMock.save.mockResolvedValue(assetEntityStub.video); storageMock.checkFileExists.mockResolvedValue(true); await sut.handleSidecarDiscovery({ id: assetEntityStub.video.id }); expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.R_OK); expect(assetMock.save).toHaveBeenCalledWith({ id: assetEntityStub.image.id, sidecarPath: '/original/path.ext.xmp', }); }); }); });