mirror of
https://github.com/immich-app/yucca.git
synced 2026-09-30 13:33:00 +08:00
224 lines
7.5 KiB
TypeScript
224 lines
7.5 KiB
TypeScript
import { ModuleConfigRepository } from 'src/repositories/moduleConfig.repository';
|
|
import { RepositoryIntegrationImmichRepository } from 'src/repositories/repositoryIntegrationImmich.repository';
|
|
import { RepositoryPathRepository } from 'src/repositories/repositoryPath.repository';
|
|
import { IntegrationsService } from 'src/services/integrations.service';
|
|
import { RepositoryService } from 'src/services/repository.service';
|
|
import { ScheduleService } from 'src/services/schedule.service';
|
|
import { newImmichHooksMock } from './mocks';
|
|
import { createTestingModule, TestContext } from './testUtils';
|
|
|
|
let ctx: TestContext;
|
|
|
|
beforeAll(async () => {
|
|
ctx = await createTestingModule();
|
|
}, 15_000);
|
|
|
|
afterAll(async () => {
|
|
await ctx.app.close();
|
|
});
|
|
|
|
describe('Integrations', () => {
|
|
it('rejects configuring Immich when not enabled', async () => {
|
|
const integrationsService = ctx.module.get(IntegrationsService);
|
|
|
|
await expect(
|
|
integrationsService.configureImmichIntegration({
|
|
name: 'Immich Backup',
|
|
worm: false,
|
|
cron: '0 2 * * *',
|
|
dataFolders: [],
|
|
backupConfiguration: false,
|
|
libraries: 'all',
|
|
}),
|
|
).rejects.toThrow('Immich integration is not enabled');
|
|
});
|
|
|
|
it('configures Immich integration with new repository and schedule', async () => {
|
|
const moduleConfig = ctx.module.get(ModuleConfigRepository);
|
|
const integrationsService = ctx.module.get(IntegrationsService);
|
|
const immichRepository = ctx.module.get(RepositoryIntegrationImmichRepository);
|
|
|
|
moduleConfig.update({
|
|
immichIntegration: {
|
|
dataPath: '/data/immich',
|
|
dataFolders: ['upload', 'library'],
|
|
libraries: [],
|
|
hooks: newImmichHooksMock(),
|
|
},
|
|
});
|
|
|
|
await integrationsService.configureImmichIntegration({
|
|
name: 'Immich Backup',
|
|
worm: false,
|
|
cron: '0 2 * * *',
|
|
dataFolders: ['upload'],
|
|
backupConfiguration: false,
|
|
libraries: 'all',
|
|
});
|
|
|
|
const integration = await immichRepository.get();
|
|
expect(integration).toBeDefined();
|
|
expect(integration?.configuration.dataFolders).toEqual(['upload']);
|
|
|
|
const config = await integrationsService.getIntegrationsConfig();
|
|
expect(config.immichIntegration).toBeDefined();
|
|
|
|
await immichRepository.delete();
|
|
moduleConfig.update({ immichIntegration: undefined });
|
|
});
|
|
|
|
it('syncs repository paths from Immich data folders and libraries', async () => {
|
|
const moduleConfig = ctx.module.get(ModuleConfigRepository);
|
|
const integrationsService = ctx.module.get(IntegrationsService);
|
|
const immichRepository = ctx.module.get(RepositoryIntegrationImmichRepository);
|
|
const repositoryPaths = ctx.module.get(RepositoryPathRepository);
|
|
|
|
moduleConfig.update({
|
|
immichIntegration: {
|
|
dataPath: '/data/immich',
|
|
dataFolders: ['upload', 'library'],
|
|
libraries: [
|
|
{ id: 'lib-1', name: 'Photos', importPaths: ['/mnt/photos'], exclusionPatterns: [] },
|
|
{ id: 'lib-2', name: 'Videos', importPaths: ['/mnt/videos'], exclusionPatterns: [] },
|
|
],
|
|
hooks: newImmichHooksMock(),
|
|
},
|
|
});
|
|
|
|
await integrationsService.configureImmichIntegration({
|
|
name: 'Immich Paths Test',
|
|
worm: false,
|
|
cron: '0 2 * * *',
|
|
dataFolders: ['upload', 'library'],
|
|
backupConfiguration: true,
|
|
libraries: ['lib-1'],
|
|
});
|
|
|
|
const integration = await immichRepository.get();
|
|
const paths = await repositoryPaths.get(integration!.id);
|
|
|
|
expect(paths).toEqual(
|
|
expect.arrayContaining([
|
|
'/data/immich/upload',
|
|
'/data/immich/library',
|
|
'/mnt/photos',
|
|
moduleConfig.get().statePath,
|
|
]),
|
|
);
|
|
expect(paths).not.toContain('/mnt/videos');
|
|
|
|
await immichRepository.delete();
|
|
moduleConfig.update({ immichIntegration: undefined });
|
|
});
|
|
|
|
it('updates existing Immich integration instead of creating new', async () => {
|
|
const moduleConfig = ctx.module.get(ModuleConfigRepository);
|
|
const integrationsService = ctx.module.get(IntegrationsService);
|
|
const immichRepository = ctx.module.get(RepositoryIntegrationImmichRepository);
|
|
const scheduleService = ctx.module.get(ScheduleService);
|
|
|
|
moduleConfig.update({
|
|
immichIntegration: {
|
|
dataPath: '/data/immich',
|
|
dataFolders: ['upload'],
|
|
libraries: [],
|
|
hooks: newImmichHooksMock(),
|
|
},
|
|
});
|
|
|
|
await integrationsService.configureImmichIntegration({
|
|
name: 'Original Name',
|
|
worm: false,
|
|
cron: '0 2 * * *',
|
|
dataFolders: ['upload'],
|
|
backupConfiguration: false,
|
|
libraries: 'all',
|
|
});
|
|
|
|
const firstIntegration = await immichRepository.get();
|
|
|
|
await integrationsService.configureImmichIntegration({
|
|
name: 'Updated Name',
|
|
worm: false,
|
|
cron: '0 4 * * *',
|
|
dataFolders: ['upload'],
|
|
backupConfiguration: false,
|
|
libraries: 'all',
|
|
});
|
|
|
|
const secondIntegration = await immichRepository.get();
|
|
|
|
expect(secondIntegration?.id).toBe(firstIntegration?.id);
|
|
expect(secondIntegration?.scheduleId).toBe(firstIntegration?.scheduleId);
|
|
|
|
const { schedules } = await scheduleService.getSchedules();
|
|
const immichSchedule = schedules.find((entry) => entry.id === secondIntegration?.scheduleId);
|
|
expect(immichSchedule?.cron).toBe('0 4 * * *');
|
|
|
|
await immichRepository.delete();
|
|
moduleConfig.update({ immichIntegration: undefined });
|
|
});
|
|
|
|
const hooks = newImmichHooksMock();
|
|
it('pauses and resumes the managed schedule', async () => {
|
|
const moduleConfig = ctx.module.get(ModuleConfigRepository);
|
|
const integrationsService = ctx.module.get(IntegrationsService);
|
|
const immichRepository = ctx.module.get(RepositoryIntegrationImmichRepository);
|
|
const scheduleService = ctx.module.get(ScheduleService);
|
|
|
|
moduleConfig.update({
|
|
immichIntegration: {
|
|
dataPath: '/data/immich',
|
|
dataFolders: ['upload'],
|
|
libraries: [],
|
|
hooks,
|
|
},
|
|
});
|
|
|
|
const configuration = {
|
|
name: 'Immich Backup',
|
|
worm: false,
|
|
cron: '0 2 * * *',
|
|
dataFolders: ['upload'],
|
|
backupConfiguration: false,
|
|
libraries: 'all' as const,
|
|
};
|
|
|
|
await integrationsService.configureImmichIntegration(configuration);
|
|
|
|
const getSchedule = async () => {
|
|
const integration = await immichRepository.get();
|
|
const { schedules } = await scheduleService.getSchedules();
|
|
|
|
return schedules.find((entry) => entry.id === integration?.scheduleId);
|
|
};
|
|
|
|
const initialSchedule = await getSchedule();
|
|
expect(initialSchedule?.paused).toBe(false);
|
|
|
|
const status = await integrationsService.getImmichBackupStatus();
|
|
expect(status.databaseDump).toEqual({ enabled: true, keepLastAmount: 14 });
|
|
|
|
await integrationsService.configureImmichDatabaseDump({ enabled: false });
|
|
expect(hooks.configureImmichDatabaseDump).toHaveBeenCalledWith({ enabled: false });
|
|
|
|
expect(status.databaseDumpWarningIgnored).toBe(false);
|
|
await integrationsService.ignoreImmichDatabaseDumpWarning();
|
|
const ignoredStatus = await integrationsService.getImmichBackupStatus();
|
|
expect(ignoredStatus.databaseDumpWarningIgnored).toBe(true);
|
|
|
|
await integrationsService.configureImmichIntegration({ ...configuration, paused: true });
|
|
|
|
const pausedSchedule = await getSchedule();
|
|
expect(pausedSchedule?.paused).toBe(true);
|
|
|
|
await integrationsService.configureImmichIntegration({ ...configuration, paused: false });
|
|
|
|
const resumedSchedule = await getSchedule();
|
|
expect(resumedSchedule?.paused).toBe(false);
|
|
|
|
await immichRepository.delete();
|
|
moduleConfig.update({ immichIntegration: undefined });
|
|
});
|
|
});
|