|
@@ -1,6 +1,13 @@
|
|
import { AssetEntity } from '@app/infra/db/entities';
|
|
import { AssetEntity } from '@app/infra/db/entities';
|
|
-import { newJobRepositoryMock, newMachineLearningRepositoryMock, newSmartInfoRepositoryMock } from '../../test';
|
|
|
|
-import { IJobRepository } from '../job';
|
|
|
|
|
|
+import {
|
|
|
|
+ assetEntityStub,
|
|
|
|
+ newAssetRepositoryMock,
|
|
|
|
+ newJobRepositoryMock,
|
|
|
|
+ newMachineLearningRepositoryMock,
|
|
|
|
+ newSmartInfoRepositoryMock,
|
|
|
|
+} from '../../test';
|
|
|
|
+import { IAssetRepository, WithoutProperty } from '../asset';
|
|
|
|
+import { IJobRepository, JobName } from '../job';
|
|
import { IMachineLearningRepository } from './machine-learning.interface';
|
|
import { IMachineLearningRepository } from './machine-learning.interface';
|
|
import { ISmartInfoRepository } from './smart-info.repository';
|
|
import { ISmartInfoRepository } from './smart-info.repository';
|
|
import { SmartInfoService } from './smart-info.service';
|
|
import { SmartInfoService } from './smart-info.service';
|
|
@@ -12,35 +19,63 @@ const asset = {
|
|
|
|
|
|
describe(SmartInfoService.name, () => {
|
|
describe(SmartInfoService.name, () => {
|
|
let sut: SmartInfoService;
|
|
let sut: SmartInfoService;
|
|
|
|
+ let assetMock: jest.Mocked<IAssetRepository>;
|
|
let jobMock: jest.Mocked<IJobRepository>;
|
|
let jobMock: jest.Mocked<IJobRepository>;
|
|
let smartMock: jest.Mocked<ISmartInfoRepository>;
|
|
let smartMock: jest.Mocked<ISmartInfoRepository>;
|
|
let machineMock: jest.Mocked<IMachineLearningRepository>;
|
|
let machineMock: jest.Mocked<IMachineLearningRepository>;
|
|
|
|
|
|
beforeEach(async () => {
|
|
beforeEach(async () => {
|
|
|
|
+ assetMock = newAssetRepositoryMock();
|
|
smartMock = newSmartInfoRepositoryMock();
|
|
smartMock = newSmartInfoRepositoryMock();
|
|
jobMock = newJobRepositoryMock();
|
|
jobMock = newJobRepositoryMock();
|
|
machineMock = newMachineLearningRepositoryMock();
|
|
machineMock = newMachineLearningRepositoryMock();
|
|
- sut = new SmartInfoService(jobMock, smartMock, machineMock);
|
|
|
|
|
|
+ sut = new SmartInfoService(assetMock, jobMock, smartMock, machineMock);
|
|
});
|
|
});
|
|
|
|
|
|
it('should work', () => {
|
|
it('should work', () => {
|
|
expect(sut).toBeDefined();
|
|
expect(sut).toBeDefined();
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ describe('handleQueueObjectTagging', () => {
|
|
|
|
+ it('should queue the assets without tags', async () => {
|
|
|
|
+ assetMock.getWithout.mockResolvedValue([assetEntityStub.image]);
|
|
|
|
+
|
|
|
|
+ await sut.handleQueueObjectTagging({ force: false });
|
|
|
|
+
|
|
|
|
+ expect(jobMock.queue.mock.calls).toEqual([
|
|
|
|
+ [{ name: JobName.CLASSIFY_IMAGE, data: { asset: assetEntityStub.image } }],
|
|
|
|
+ [{ name: JobName.DETECT_OBJECTS, data: { asset: assetEntityStub.image } }],
|
|
|
|
+ ]);
|
|
|
|
+ expect(assetMock.getWithout).toHaveBeenCalledWith(WithoutProperty.OBJECT_TAGS);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('should queue all the assets', async () => {
|
|
|
|
+ assetMock.getAll.mockResolvedValue([assetEntityStub.image]);
|
|
|
|
+
|
|
|
|
+ await sut.handleQueueObjectTagging({ force: true });
|
|
|
|
+
|
|
|
|
+ expect(jobMock.queue.mock.calls).toEqual([
|
|
|
|
+ [{ name: JobName.CLASSIFY_IMAGE, data: { asset: assetEntityStub.image } }],
|
|
|
|
+ [{ name: JobName.DETECT_OBJECTS, data: { asset: assetEntityStub.image } }],
|
|
|
|
+ ]);
|
|
|
|
+ expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+
|
|
describe('handleTagImage', () => {
|
|
describe('handleTagImage', () => {
|
|
it('should skip assets without a resize path', async () => {
|
|
it('should skip assets without a resize path', async () => {
|
|
- await sut.handleTagImage({ asset: { resizePath: '' } as AssetEntity });
|
|
|
|
|
|
+ await sut.handleClassifyImage({ asset: { resizePath: '' } as AssetEntity });
|
|
|
|
|
|
expect(smartMock.upsert).not.toHaveBeenCalled();
|
|
expect(smartMock.upsert).not.toHaveBeenCalled();
|
|
- expect(machineMock.tagImage).not.toHaveBeenCalled();
|
|
|
|
|
|
+ expect(machineMock.classifyImage).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
|
|
it('should save the returned tags', async () => {
|
|
it('should save the returned tags', async () => {
|
|
- machineMock.tagImage.mockResolvedValue(['tag1', 'tag2', 'tag3']);
|
|
|
|
|
|
+ machineMock.classifyImage.mockResolvedValue(['tag1', 'tag2', 'tag3']);
|
|
|
|
|
|
- await sut.handleTagImage({ asset });
|
|
|
|
|
|
+ await sut.handleClassifyImage({ asset });
|
|
|
|
|
|
- expect(machineMock.tagImage).toHaveBeenCalledWith({ thumbnailPath: 'path/to/resize.ext' });
|
|
|
|
|
|
+ expect(machineMock.classifyImage).toHaveBeenCalledWith({ thumbnailPath: 'path/to/resize.ext' });
|
|
expect(smartMock.upsert).toHaveBeenCalledWith({
|
|
expect(smartMock.upsert).toHaveBeenCalledWith({
|
|
assetId: 'asset-1',
|
|
assetId: 'asset-1',
|
|
tags: ['tag1', 'tag2', 'tag3'],
|
|
tags: ['tag1', 'tag2', 'tag3'],
|
|
@@ -48,19 +83,19 @@ describe(SmartInfoService.name, () => {
|
|
});
|
|
});
|
|
|
|
|
|
it('should handle an error with the machine learning pipeline', async () => {
|
|
it('should handle an error with the machine learning pipeline', async () => {
|
|
- machineMock.tagImage.mockRejectedValue(new Error('Unable to read thumbnail'));
|
|
|
|
|
|
+ machineMock.classifyImage.mockRejectedValue(new Error('Unable to read thumbnail'));
|
|
|
|
|
|
- await sut.handleTagImage({ asset });
|
|
|
|
|
|
+ await sut.handleClassifyImage({ asset });
|
|
|
|
|
|
expect(smartMock.upsert).not.toHaveBeenCalled();
|
|
expect(smartMock.upsert).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
|
|
it('should no update the smart info if no tags were returned', async () => {
|
|
it('should no update the smart info if no tags were returned', async () => {
|
|
- machineMock.tagImage.mockResolvedValue([]);
|
|
|
|
|
|
+ machineMock.classifyImage.mockResolvedValue([]);
|
|
|
|
|
|
- await sut.handleTagImage({ asset });
|
|
|
|
|
|
+ await sut.handleClassifyImage({ asset });
|
|
|
|
|
|
- expect(machineMock.tagImage).toHaveBeenCalled();
|
|
|
|
|
|
+ expect(machineMock.classifyImage).toHaveBeenCalled();
|
|
expect(smartMock.upsert).not.toHaveBeenCalled();
|
|
expect(smartMock.upsert).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
@@ -102,4 +137,53 @@ describe(SmartInfoService.name, () => {
|
|
expect(smartMock.upsert).not.toHaveBeenCalled();
|
|
expect(smartMock.upsert).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
+
|
|
|
|
+ describe('handleQueueEncodeClip', () => {
|
|
|
|
+ it('should queue the assets without clip embeddings', async () => {
|
|
|
|
+ assetMock.getWithout.mockResolvedValue([assetEntityStub.image]);
|
|
|
|
+
|
|
|
|
+ await sut.handleQueueEncodeClip({ force: false });
|
|
|
|
+
|
|
|
|
+ expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.ENCODE_CLIP, data: { asset: assetEntityStub.image } });
|
|
|
|
+ expect(assetMock.getWithout).toHaveBeenCalledWith(WithoutProperty.CLIP_ENCODING);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('should queue all the assets', async () => {
|
|
|
|
+ assetMock.getAll.mockResolvedValue([assetEntityStub.image]);
|
|
|
|
+
|
|
|
|
+ await sut.handleQueueEncodeClip({ force: true });
|
|
|
|
+
|
|
|
|
+ expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.ENCODE_CLIP, data: { asset: assetEntityStub.image } });
|
|
|
|
+ expect(assetMock.getAll).toHaveBeenCalled();
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ describe('handleEncodeClip', () => {
|
|
|
|
+ it('should skip assets without a resize path', async () => {
|
|
|
|
+ await sut.handleEncodeClip({ asset: { resizePath: '' } as AssetEntity });
|
|
|
|
+
|
|
|
|
+ expect(smartMock.upsert).not.toHaveBeenCalled();
|
|
|
|
+ expect(machineMock.encodeImage).not.toHaveBeenCalled();
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('should save the returned objects', async () => {
|
|
|
|
+ machineMock.encodeImage.mockResolvedValue([0.01, 0.02, 0.03]);
|
|
|
|
+
|
|
|
|
+ await sut.handleEncodeClip({ asset });
|
|
|
|
+
|
|
|
|
+ expect(machineMock.encodeImage).toHaveBeenCalledWith({ thumbnailPath: 'path/to/resize.ext' });
|
|
|
|
+ expect(smartMock.upsert).toHaveBeenCalledWith({
|
|
|
|
+ assetId: 'asset-1',
|
|
|
|
+ clipEmbedding: [0.01, 0.02, 0.03],
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('should handle an error with the machine learning pipeline', async () => {
|
|
|
|
+ machineMock.encodeImage.mockRejectedValue(new Error('Unable to read thumbnail'));
|
|
|
|
+
|
|
|
|
+ await sut.handleEncodeClip({ asset });
|
|
|
|
+
|
|
|
|
+ expect(smartMock.upsert).not.toHaveBeenCalled();
|
|
|
|
+ });
|
|
|
|
+ });
|
|
});
|
|
});
|