2023-03-20 16:55:28 +01:00
|
|
|
import { BadRequestException } from '@nestjs/common';
|
2024-03-20 21:20:38 +01:00
|
|
|
import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core';
|
2024-03-20 19:32:04 +01:00
|
|
|
import { JobCommand, JobName, QueueName } from 'src/domain/job/job.constants';
|
|
|
|
import { JobService } from 'src/domain/job/job.service';
|
|
|
|
import { IAssetRepository } from 'src/domain/repositories/asset.repository';
|
|
|
|
import { ICommunicationRepository } from 'src/domain/repositories/communication.repository';
|
|
|
|
import { IJobRepository, JobHandler, JobItem, JobStatus } from 'src/domain/repositories/job.repository';
|
|
|
|
import { IPersonRepository } from 'src/domain/repositories/person.repository';
|
|
|
|
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
|
|
|
|
import { SystemConfig, SystemConfigKey } from 'src/infra/entities/system-config.entity';
|
|
|
|
import { assetStub } from 'test/fixtures/asset.stub';
|
|
|
|
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
|
|
|
|
import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock';
|
|
|
|
import { newJobRepositoryMock } from 'test/repositories/job.repository.mock';
|
|
|
|
import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock';
|
|
|
|
import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock';
|
2023-06-01 22:07:45 +02:00
|
|
|
|
2024-03-15 14:16:54 +01:00
|
|
|
const makeMockHandlers = (status: JobStatus) => {
|
|
|
|
const mock = jest.fn().mockResolvedValue(status);
|
2024-02-02 04:18:00 +01:00
|
|
|
return Object.fromEntries(Object.values(JobName).map((jobName) => [jobName, mock])) as unknown as Record<
|
2023-06-01 22:07:45 +02:00
|
|
|
JobName,
|
|
|
|
JobHandler
|
|
|
|
>;
|
|
|
|
};
|
2023-03-20 16:55:28 +01:00
|
|
|
|
|
|
|
describe(JobService.name, () => {
|
|
|
|
let sut: JobService;
|
2023-05-26 21:43:24 +02:00
|
|
|
let assetMock: jest.Mocked<IAssetRepository>;
|
2023-06-01 12:32:51 +02:00
|
|
|
let configMock: jest.Mocked<ISystemConfigRepository>;
|
2023-05-26 21:43:24 +02:00
|
|
|
let communicationMock: jest.Mocked<ICommunicationRepository>;
|
2023-03-20 16:55:28 +01:00
|
|
|
let jobMock: jest.Mocked<IJobRepository>;
|
2023-10-06 22:48:11 +02:00
|
|
|
let personMock: jest.Mocked<IPersonRepository>;
|
2023-03-20 16:55:28 +01:00
|
|
|
|
2024-03-05 23:23:06 +01:00
|
|
|
beforeEach(() => {
|
2023-05-26 21:43:24 +02:00
|
|
|
assetMock = newAssetRepositoryMock();
|
2023-06-01 12:32:51 +02:00
|
|
|
configMock = newSystemConfigRepositoryMock();
|
2023-05-26 21:43:24 +02:00
|
|
|
communicationMock = newCommunicationRepositoryMock();
|
2023-03-20 16:55:28 +01:00
|
|
|
jobMock = newJobRepositoryMock();
|
2023-10-06 22:48:11 +02:00
|
|
|
personMock = newPersonRepositoryMock();
|
|
|
|
sut = new JobService(assetMock, communicationMock, jobMock, configMock, personMock);
|
2023-03-20 16:55:28 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should work', () => {
|
|
|
|
expect(sut).toBeDefined();
|
|
|
|
});
|
|
|
|
|
2023-05-17 19:07:17 +02:00
|
|
|
describe('handleNightlyJobs', () => {
|
|
|
|
it('should run the scheduled jobs', async () => {
|
|
|
|
await sut.handleNightlyJobs();
|
|
|
|
|
2024-01-01 21:45:42 +01:00
|
|
|
expect(jobMock.queueAll).toHaveBeenCalledWith([
|
|
|
|
{ name: JobName.ASSET_DELETION_CHECK },
|
|
|
|
{ name: JobName.USER_DELETE_CHECK },
|
|
|
|
{ name: JobName.PERSON_CLEANUP },
|
|
|
|
{ name: JobName.QUEUE_GENERATE_THUMBNAILS, data: { force: false } },
|
|
|
|
{ name: JobName.CLEAN_OLD_AUDIT_LOGS },
|
2024-01-13 01:43:36 +01:00
|
|
|
{ name: JobName.USER_SYNC_USAGE },
|
2024-01-25 07:27:39 +01:00
|
|
|
{ name: JobName.QUEUE_FACIAL_RECOGNITION, data: { force: false } },
|
2023-05-17 19:07:17 +02:00
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-03-20 16:55:28 +01:00
|
|
|
describe('getAllJobStatus', () => {
|
|
|
|
it('should get all job statuses', async () => {
|
|
|
|
jobMock.getJobCounts.mockResolvedValue({
|
|
|
|
active: 1,
|
|
|
|
completed: 1,
|
|
|
|
failed: 1,
|
|
|
|
delayed: 1,
|
|
|
|
waiting: 1,
|
2023-03-29 17:33:03 +02:00
|
|
|
paused: 1,
|
2023-03-20 16:55:28 +01:00
|
|
|
});
|
2023-04-01 22:46:07 +02:00
|
|
|
jobMock.getQueueStatus.mockResolvedValue({
|
|
|
|
isActive: true,
|
|
|
|
isPaused: true,
|
|
|
|
});
|
2023-03-20 16:55:28 +01:00
|
|
|
|
2023-04-01 22:46:07 +02:00
|
|
|
const expectedJobStatus = {
|
|
|
|
jobCounts: {
|
2023-03-20 16:55:28 +01:00
|
|
|
active: 1,
|
|
|
|
completed: 1,
|
|
|
|
delayed: 1,
|
|
|
|
failed: 1,
|
|
|
|
waiting: 1,
|
2023-03-29 17:33:03 +02:00
|
|
|
paused: 1,
|
2023-03-20 16:55:28 +01:00
|
|
|
},
|
2023-04-01 22:46:07 +02:00
|
|
|
queueStatus: {
|
|
|
|
isActive: true,
|
|
|
|
isPaused: true,
|
2023-03-20 16:55:28 +01:00
|
|
|
},
|
2023-04-01 22:46:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
await expect(sut.getAllJobsStatus()).resolves.toEqual({
|
2023-06-01 12:32:51 +02:00
|
|
|
[QueueName.BACKGROUND_TASK]: expectedJobStatus,
|
2023-12-16 17:50:46 +01:00
|
|
|
[QueueName.SMART_SEARCH]: expectedJobStatus,
|
2023-06-01 12:32:51 +02:00
|
|
|
[QueueName.METADATA_EXTRACTION]: expectedJobStatus,
|
|
|
|
[QueueName.SEARCH]: expectedJobStatus,
|
|
|
|
[QueueName.STORAGE_TEMPLATE_MIGRATION]: expectedJobStatus,
|
2023-09-25 17:07:21 +02:00
|
|
|
[QueueName.MIGRATION]: expectedJobStatus,
|
2023-06-01 12:32:51 +02:00
|
|
|
[QueueName.THUMBNAIL_GENERATION]: expectedJobStatus,
|
|
|
|
[QueueName.VIDEO_CONVERSION]: expectedJobStatus,
|
2024-01-18 06:08:48 +01:00
|
|
|
[QueueName.FACE_DETECTION]: expectedJobStatus,
|
|
|
|
[QueueName.FACIAL_RECOGNITION]: expectedJobStatus,
|
2023-06-01 12:32:51 +02:00
|
|
|
[QueueName.SIDECAR]: expectedJobStatus,
|
2023-09-20 13:16:33 +02:00
|
|
|
[QueueName.LIBRARY]: expectedJobStatus,
|
2023-03-20 16:55:28 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('handleCommand', () => {
|
|
|
|
it('should handle a pause command', async () => {
|
|
|
|
await sut.handleCommand(QueueName.METADATA_EXTRACTION, { command: JobCommand.PAUSE, force: false });
|
|
|
|
|
|
|
|
expect(jobMock.pause).toHaveBeenCalledWith(QueueName.METADATA_EXTRACTION);
|
|
|
|
});
|
|
|
|
|
2023-03-28 20:25:22 +02:00
|
|
|
it('should handle a resume command', async () => {
|
|
|
|
await sut.handleCommand(QueueName.METADATA_EXTRACTION, { command: JobCommand.RESUME, force: false });
|
|
|
|
|
|
|
|
expect(jobMock.resume).toHaveBeenCalledWith(QueueName.METADATA_EXTRACTION);
|
|
|
|
});
|
|
|
|
|
2023-03-20 16:55:28 +01:00
|
|
|
it('should handle an empty command', async () => {
|
|
|
|
await sut.handleCommand(QueueName.METADATA_EXTRACTION, { command: JobCommand.EMPTY, force: false });
|
|
|
|
|
|
|
|
expect(jobMock.empty).toHaveBeenCalledWith(QueueName.METADATA_EXTRACTION);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should not start a job that is already running', async () => {
|
2023-04-01 22:46:07 +02:00
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: true, isPaused: false });
|
2023-03-20 16:55:28 +01:00
|
|
|
|
|
|
|
await expect(
|
|
|
|
sut.handleCommand(QueueName.VIDEO_CONVERSION, { command: JobCommand.START, force: false }),
|
|
|
|
).rejects.toBeInstanceOf(BadRequestException);
|
|
|
|
|
|
|
|
expect(jobMock.queue).not.toHaveBeenCalled();
|
2024-01-01 21:45:42 +01:00
|
|
|
expect(jobMock.queueAll).not.toHaveBeenCalled();
|
2023-03-20 16:55:28 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle a start video conversion command', async () => {
|
2023-04-01 22:46:07 +02:00
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
2023-03-20 16:55:28 +01:00
|
|
|
|
|
|
|
await sut.handleCommand(QueueName.VIDEO_CONVERSION, { command: JobCommand.START, force: false });
|
|
|
|
|
|
|
|
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.QUEUE_VIDEO_CONVERSION, data: { force: false } });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle a start storage template migration command', async () => {
|
2023-04-01 22:46:07 +02:00
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
2023-03-20 16:55:28 +01:00
|
|
|
|
|
|
|
await sut.handleCommand(QueueName.STORAGE_TEMPLATE_MIGRATION, { command: JobCommand.START, force: false });
|
|
|
|
|
|
|
|
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.STORAGE_TEMPLATE_MIGRATION });
|
|
|
|
});
|
|
|
|
|
2024-01-29 15:51:22 +01:00
|
|
|
it('should handle a start smart search command', async () => {
|
2023-04-01 22:46:07 +02:00
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
2023-03-20 16:55:28 +01:00
|
|
|
|
2023-12-16 17:50:46 +01:00
|
|
|
await sut.handleCommand(QueueName.SMART_SEARCH, { command: JobCommand.START, force: false });
|
2023-03-20 16:55:28 +01:00
|
|
|
|
2024-01-29 15:51:22 +01:00
|
|
|
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.QUEUE_SMART_SEARCH, data: { force: false } });
|
2023-03-20 16:55:28 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle a start metadata extraction command', async () => {
|
2023-04-01 22:46:07 +02:00
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
2023-03-20 16:55:28 +01:00
|
|
|
|
|
|
|
await sut.handleCommand(QueueName.METADATA_EXTRACTION, { command: JobCommand.START, force: false });
|
|
|
|
|
|
|
|
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.QUEUE_METADATA_EXTRACTION, data: { force: false } });
|
|
|
|
});
|
|
|
|
|
2023-06-01 12:32:51 +02:00
|
|
|
it('should handle a start sidecar command', async () => {
|
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
|
|
|
|
|
|
|
await sut.handleCommand(QueueName.SIDECAR, { command: JobCommand.START, force: false });
|
|
|
|
|
|
|
|
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.QUEUE_SIDECAR, data: { force: false } });
|
|
|
|
});
|
|
|
|
|
2023-03-20 16:55:28 +01:00
|
|
|
it('should handle a start thumbnail generation command', async () => {
|
2023-04-01 22:46:07 +02:00
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
2023-03-20 16:55:28 +01:00
|
|
|
|
|
|
|
await sut.handleCommand(QueueName.THUMBNAIL_GENERATION, { command: JobCommand.START, force: false });
|
|
|
|
|
|
|
|
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.QUEUE_GENERATE_THUMBNAILS, data: { force: false } });
|
|
|
|
});
|
|
|
|
|
2024-01-18 06:08:48 +01:00
|
|
|
it('should handle a start face detection command', async () => {
|
2023-06-01 12:32:51 +02:00
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
|
|
|
|
2024-01-18 06:08:48 +01:00
|
|
|
await sut.handleCommand(QueueName.FACE_DETECTION, { command: JobCommand.START, force: false });
|
2023-06-01 12:32:51 +02:00
|
|
|
|
2024-01-18 06:08:48 +01:00
|
|
|
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.QUEUE_FACE_DETECTION, data: { force: false } });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle a start facial recognition command', async () => {
|
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
|
|
|
|
|
|
|
await sut.handleCommand(QueueName.FACIAL_RECOGNITION, { command: JobCommand.START, force: false });
|
|
|
|
|
|
|
|
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.QUEUE_FACIAL_RECOGNITION, data: { force: false } });
|
2023-06-01 12:32:51 +02:00
|
|
|
});
|
|
|
|
|
2023-03-20 16:55:28 +01:00
|
|
|
it('should throw a bad request when an invalid queue is used', async () => {
|
2023-04-01 22:46:07 +02:00
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
2023-03-20 16:55:28 +01:00
|
|
|
|
|
|
|
await expect(
|
|
|
|
sut.handleCommand(QueueName.BACKGROUND_TASK, { command: JobCommand.START, force: false }),
|
|
|
|
).rejects.toBeInstanceOf(BadRequestException);
|
|
|
|
|
|
|
|
expect(jobMock.queue).not.toHaveBeenCalled();
|
2024-01-01 21:45:42 +01:00
|
|
|
expect(jobMock.queueAll).not.toHaveBeenCalled();
|
2023-03-20 16:55:28 +01:00
|
|
|
});
|
|
|
|
});
|
2023-06-01 12:32:51 +02:00
|
|
|
|
2024-01-01 19:16:44 +01:00
|
|
|
describe('init', () => {
|
2023-06-01 12:32:51 +02:00
|
|
|
it('should register a handler for each queue', async () => {
|
2024-03-15 14:16:54 +01:00
|
|
|
await sut.init(makeMockHandlers(JobStatus.SUCCESS));
|
2023-06-01 12:32:51 +02:00
|
|
|
expect(configMock.load).toHaveBeenCalled();
|
|
|
|
expect(jobMock.addHandler).toHaveBeenCalledTimes(Object.keys(QueueName).length);
|
|
|
|
});
|
2023-06-01 22:07:45 +02:00
|
|
|
|
|
|
|
it('should subscribe to config changes', async () => {
|
2024-03-15 14:16:54 +01:00
|
|
|
await sut.init(makeMockHandlers(JobStatus.FAILED));
|
2023-06-01 22:07:45 +02:00
|
|
|
|
2023-10-09 02:51:03 +02:00
|
|
|
SystemConfigCore.create(newSystemConfigRepositoryMock(false)).config$.next({
|
2023-06-01 22:07:45 +02:00
|
|
|
job: {
|
|
|
|
[QueueName.BACKGROUND_TASK]: { concurrency: 10 },
|
2023-12-16 17:50:46 +01:00
|
|
|
[QueueName.SMART_SEARCH]: { concurrency: 10 },
|
2023-06-01 22:07:45 +02:00
|
|
|
[QueueName.METADATA_EXTRACTION]: { concurrency: 10 },
|
2024-01-18 06:08:48 +01:00
|
|
|
[QueueName.FACE_DETECTION]: { concurrency: 10 },
|
2023-06-01 22:07:45 +02:00
|
|
|
[QueueName.SEARCH]: { concurrency: 10 },
|
|
|
|
[QueueName.SIDECAR]: { concurrency: 10 },
|
2023-09-20 13:16:33 +02:00
|
|
|
[QueueName.LIBRARY]: { concurrency: 10 },
|
2023-09-25 17:07:21 +02:00
|
|
|
[QueueName.MIGRATION]: { concurrency: 10 },
|
2023-06-01 22:07:45 +02:00
|
|
|
[QueueName.THUMBNAIL_GENERATION]: { concurrency: 10 },
|
|
|
|
[QueueName.VIDEO_CONVERSION]: { concurrency: 10 },
|
|
|
|
},
|
|
|
|
} as SystemConfig);
|
|
|
|
|
|
|
|
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.BACKGROUND_TASK, 10);
|
2023-12-16 17:50:46 +01:00
|
|
|
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.SMART_SEARCH, 10);
|
2023-06-01 22:07:45 +02:00
|
|
|
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.METADATA_EXTRACTION, 10);
|
2024-01-18 06:08:48 +01:00
|
|
|
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.FACE_DETECTION, 10);
|
2023-06-01 22:07:45 +02:00
|
|
|
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.SIDECAR, 10);
|
2023-09-20 13:16:33 +02:00
|
|
|
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.LIBRARY, 10);
|
2023-09-25 17:07:21 +02:00
|
|
|
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.MIGRATION, 10);
|
2023-06-01 22:07:45 +02:00
|
|
|
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.THUMBNAIL_GENERATION, 10);
|
|
|
|
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.VIDEO_CONVERSION, 10);
|
|
|
|
});
|
|
|
|
|
|
|
|
const tests: Array<{ item: JobItem; jobs: JobName[] }> = [
|
|
|
|
{
|
|
|
|
item: { name: JobName.SIDECAR_SYNC, data: { id: 'asset-1' } },
|
|
|
|
jobs: [JobName.METADATA_EXTRACTION],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
item: { name: JobName.SIDECAR_DISCOVERY, data: { id: 'asset-1' } },
|
|
|
|
jobs: [JobName.METADATA_EXTRACTION],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
item: { name: JobName.METADATA_EXTRACTION, data: { id: 'asset-1' } },
|
2023-08-16 03:34:57 +02:00
|
|
|
jobs: [JobName.LINK_LIVE_PHOTOS],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
item: { name: JobName.LINK_LIVE_PHOTOS, data: { id: 'asset-1' } },
|
2023-12-08 17:15:46 +01:00
|
|
|
jobs: [JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE],
|
2023-06-01 22:07:45 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
item: { name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: { id: 'asset-1', source: 'upload' } },
|
|
|
|
jobs: [JobName.GENERATE_JPEG_THUMBNAIL],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
item: { name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: { id: 'asset-1' } },
|
|
|
|
jobs: [],
|
|
|
|
},
|
2023-12-08 17:15:46 +01:00
|
|
|
{
|
|
|
|
item: { name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: 'asset-1' } },
|
|
|
|
jobs: [],
|
|
|
|
},
|
2023-06-01 22:07:45 +02:00
|
|
|
{
|
|
|
|
item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1' } },
|
2024-02-29 05:23:21 +01:00
|
|
|
jobs: [JobName.GENERATE_WEBP_THUMBNAIL, JobName.GENERATE_THUMBHASH_THUMBNAIL],
|
2023-06-01 22:07:45 +02:00
|
|
|
},
|
2023-07-05 07:36:16 +02:00
|
|
|
{
|
|
|
|
item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1', source: 'upload' } },
|
|
|
|
jobs: [
|
|
|
|
JobName.GENERATE_WEBP_THUMBNAIL,
|
2024-01-01 21:45:42 +01:00
|
|
|
JobName.GENERATE_THUMBHASH_THUMBNAIL,
|
2024-01-29 15:51:22 +01:00
|
|
|
JobName.SMART_SEARCH,
|
2024-01-18 06:08:48 +01:00
|
|
|
JobName.FACE_DETECTION,
|
2023-07-05 07:36:16 +02:00
|
|
|
JobName.VIDEO_CONVERSION,
|
|
|
|
],
|
|
|
|
},
|
2023-09-11 17:56:38 +02:00
|
|
|
{
|
|
|
|
item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-live-image', source: 'upload' } },
|
|
|
|
jobs: [
|
|
|
|
JobName.GENERATE_WEBP_THUMBNAIL,
|
|
|
|
JobName.GENERATE_THUMBHASH_THUMBNAIL,
|
2024-01-29 15:51:22 +01:00
|
|
|
JobName.SMART_SEARCH,
|
2024-01-18 06:08:48 +01:00
|
|
|
JobName.FACE_DETECTION,
|
2023-09-11 17:56:38 +02:00
|
|
|
JobName.VIDEO_CONVERSION,
|
|
|
|
],
|
|
|
|
},
|
2023-06-01 22:07:45 +02:00
|
|
|
{
|
2024-01-29 15:51:22 +01:00
|
|
|
item: { name: JobName.SMART_SEARCH, data: { id: 'asset-1' } },
|
2023-12-08 17:15:46 +01:00
|
|
|
jobs: [],
|
2023-06-01 22:07:45 +02:00
|
|
|
},
|
|
|
|
{
|
2024-01-18 06:08:48 +01:00
|
|
|
item: { name: JobName.FACE_DETECTION, data: { id: 'asset-1' } },
|
2024-01-25 07:27:39 +01:00
|
|
|
jobs: [],
|
2024-01-18 06:08:48 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
item: { name: JobName.FACIAL_RECOGNITION, data: { id: 'asset-1' } },
|
2023-12-08 17:15:46 +01:00
|
|
|
jobs: [],
|
2023-06-01 22:07:45 +02:00
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
for (const { item, jobs } of tests) {
|
|
|
|
it(`should queue ${jobs.length} jobs when a ${item.name} job finishes successfully`, async () => {
|
2023-07-05 07:36:16 +02:00
|
|
|
if (item.name === JobName.GENERATE_JPEG_THUMBNAIL && item.data.source === 'upload') {
|
2023-09-11 17:56:38 +02:00
|
|
|
if (item.data.id === 'asset-live-image') {
|
|
|
|
assetMock.getByIds.mockResolvedValue([assetStub.livePhotoStillAsset]);
|
|
|
|
} else {
|
|
|
|
assetMock.getByIds.mockResolvedValue([assetStub.livePhotoMotionAsset]);
|
|
|
|
}
|
2023-07-05 07:36:16 +02:00
|
|
|
}
|
2023-06-01 22:07:45 +02:00
|
|
|
|
2024-03-15 14:16:54 +01:00
|
|
|
await sut.init(makeMockHandlers(JobStatus.SUCCESS));
|
2023-06-01 22:07:45 +02:00
|
|
|
await jobMock.addHandler.mock.calls[0][2](item);
|
|
|
|
|
2024-01-01 21:45:42 +01:00
|
|
|
if (jobs.length > 1) {
|
|
|
|
expect(jobMock.queueAll).toHaveBeenCalledWith(
|
|
|
|
jobs.map((jobName) => ({ name: jobName, data: expect.anything() })),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
expect(jobMock.queue).toHaveBeenCalledTimes(jobs.length);
|
|
|
|
for (const jobName of jobs) {
|
|
|
|
expect(jobMock.queue).toHaveBeenCalledWith({ name: jobName, data: expect.anything() });
|
|
|
|
}
|
2023-06-01 22:07:45 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it(`should not queue any jobs when ${item.name} finishes with 'false'`, async () => {
|
2024-03-15 14:16:54 +01:00
|
|
|
await sut.init(makeMockHandlers(JobStatus.FAILED));
|
2023-06-01 22:07:45 +02:00
|
|
|
await jobMock.addHandler.mock.calls[0][2](item);
|
|
|
|
|
2024-01-01 21:45:42 +01:00
|
|
|
expect(jobMock.queueAll).not.toHaveBeenCalled();
|
2023-06-01 22:07:45 +02:00
|
|
|
});
|
|
|
|
}
|
2023-12-08 17:15:46 +01:00
|
|
|
|
|
|
|
const featureTests: Array<{ queue: QueueName; feature: FeatureFlag; configKey: SystemConfigKey }> = [
|
|
|
|
{
|
2023-12-16 17:50:46 +01:00
|
|
|
queue: QueueName.SMART_SEARCH,
|
2024-01-29 15:51:22 +01:00
|
|
|
feature: FeatureFlag.SMART_SEARCH,
|
2023-12-08 17:15:46 +01:00
|
|
|
configKey: SystemConfigKey.MACHINE_LEARNING_CLIP_ENABLED,
|
|
|
|
},
|
|
|
|
{
|
2024-01-18 06:08:48 +01:00
|
|
|
queue: QueueName.FACE_DETECTION,
|
|
|
|
feature: FeatureFlag.FACIAL_RECOGNITION,
|
|
|
|
configKey: SystemConfigKey.MACHINE_LEARNING_FACIAL_RECOGNITION_ENABLED,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
queue: QueueName.FACIAL_RECOGNITION,
|
2023-12-08 17:15:46 +01:00
|
|
|
feature: FeatureFlag.FACIAL_RECOGNITION,
|
|
|
|
configKey: SystemConfigKey.MACHINE_LEARNING_FACIAL_RECOGNITION_ENABLED,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
for (const { queue, feature, configKey } of featureTests) {
|
|
|
|
it(`should throw an error if attempting to queue ${queue} when ${feature} is disabled`, async () => {
|
|
|
|
configMock.load.mockResolvedValue([{ key: configKey, value: false }]);
|
|
|
|
jobMock.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false });
|
|
|
|
|
|
|
|
await expect(sut.handleCommand(queue, { command: JobCommand.START, force: false })).rejects.toThrow();
|
|
|
|
});
|
|
|
|
}
|
2023-06-01 12:32:51 +02:00
|
|
|
});
|
2023-03-20 16:55:28 +01:00
|
|
|
});
|