mirror of
https://github.com/immich-app/yucca.git
synced 2026-09-30 13:33:00 +08:00
fix(yucca sdk): tsconfig for api tests (#706)
fix(yucca sdk): tsconfig for api test: update lint issues in tests
This commit is contained in:
@@ -50,6 +50,7 @@ export default typescriptEslint.config([
|
|||||||
'unicorn/import-style': 'off',
|
'unicorn/import-style': 'off',
|
||||||
'unicorn/prefer-structured-clone': 'off',
|
'unicorn/prefer-structured-clone': 'off',
|
||||||
'unicorn/no-for-loop': 'off',
|
'unicorn/no-for-loop': 'off',
|
||||||
|
'unicorn/no-useless-undefined': ['error', { checkArguments: false }],
|
||||||
'@typescript-eslint/await-thenable': 'error',
|
'@typescript-eslint/await-thenable': 'error',
|
||||||
'@typescript-eslint/no-misused-promises': 'error',
|
'@typescript-eslint/no-misused-promises': 'error',
|
||||||
'require-await': 'off',
|
'require-await': 'off',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ describe(ConfigRepository.name, () => {
|
|||||||
selectFrom: jest.fn(() => ({
|
selectFrom: jest.fn(() => ({
|
||||||
where: jest.fn(() => ({
|
where: jest.fn(() => ({
|
||||||
select: jest.fn(() => ({
|
select: jest.fn(() => ({
|
||||||
executeTakeFirst: jest.fn().mockResolvedValue(),
|
executeTakeFirst: jest.fn(),
|
||||||
})),
|
})),
|
||||||
})),
|
})),
|
||||||
})),
|
})),
|
||||||
@@ -32,8 +32,8 @@ describe(ConfigRepository.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to global config and core count without placement', async () => {
|
it('falls back to global config and core count without placement', async () => {
|
||||||
const connections = jest.spyOn(yuccaWellKnown, 'getConnections').mockResolvedValue();
|
const connections = jest.spyOn(yuccaWellKnown, 'getConnections').mockResolvedValue(undefined);
|
||||||
const packSize = jest.spyOn(yuccaWellKnown, 'getPackSizeMib').mockResolvedValue();
|
const packSize = jest.spyOn(yuccaWellKnown, 'getPackSizeMib').mockResolvedValue(undefined);
|
||||||
const repository = new ConfigRepository(db as never, { statePath: '/state' }, LoggingRepository.create());
|
const repository = new ConfigRepository(db as never, { statePath: '/state' }, LoggingRepository.create());
|
||||||
|
|
||||||
await expect(repository.getResticOptions({ siteCode: null, storageClusterCode: null })).resolves.toEqual({
|
await expect(repository.getResticOptions({ siteCode: null, storageClusterCode: null })).resolves.toEqual({
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { BootstrapRepository } from 'src/repositories/bootstrap.repository';
|
|||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { RepositoryRepository } from 'src/repositories/repository.repository';
|
import { RepositoryRepository } from 'src/repositories/repository.repository';
|
||||||
import { RunHistoryRepository } from 'src/repositories/runHistory.repository';
|
import { RunHistoryRepository } from 'src/repositories/runHistory.repository';
|
||||||
|
import { type RunType } from 'src/schema/tables/runHistory.table';
|
||||||
import { BootstrapService } from 'src/services/bootstrap.service';
|
import { BootstrapService } from 'src/services/bootstrap.service';
|
||||||
import { createTestingModule, TestContext, waitFor } from './testUtils';
|
import { createTestingModule, TestContext, waitFor } from './testUtils';
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ const apiSubmitStructuredLog = submitStructuredLog as jest.Mock;
|
|||||||
|
|
||||||
let ctx: TestContext;
|
let ctx: TestContext;
|
||||||
|
|
||||||
const createInterruptedRun = async (type = TaskType.Backup) => {
|
const createInterruptedRun = async (type: RunType = TaskType.Backup) => {
|
||||||
const repositoryId = randomUUID();
|
const repositoryId = randomUUID();
|
||||||
const remoteId = `remote-${repositoryId}`;
|
const remoteId = `remote-${repositoryId}`;
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ describe('Database', () => {
|
|||||||
|
|
||||||
const { schedule } = await scheduleService.createSchedule({
|
const { schedule } = await scheduleService.createSchedule({
|
||||||
name: 'Snapshot Schedule',
|
name: 'Snapshot Schedule',
|
||||||
|
paused: false,
|
||||||
cron: '0 0 * * *',
|
cron: '0 0 * * *',
|
||||||
repositories: [repository.id],
|
repositories: [repository.id],
|
||||||
});
|
});
|
||||||
@@ -43,6 +44,7 @@ describe('Database', () => {
|
|||||||
|
|
||||||
await scheduleService.createSchedule({
|
await scheduleService.createSchedule({
|
||||||
name: 'Post-Snapshot Schedule',
|
name: 'Post-Snapshot Schedule',
|
||||||
|
paused: false,
|
||||||
cron: '0 6 * * *',
|
cron: '0 6 * * *',
|
||||||
repositories: [],
|
repositories: [],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { RepositoryPathRepository } from 'src/repositories/repositoryPath.reposi
|
|||||||
import { IntegrationsService } from 'src/services/integrations.service';
|
import { IntegrationsService } from 'src/services/integrations.service';
|
||||||
import { RepositoryService } from 'src/services/repository.service';
|
import { RepositoryService } from 'src/services/repository.service';
|
||||||
import { ScheduleService } from 'src/services/schedule.service';
|
import { ScheduleService } from 'src/services/schedule.service';
|
||||||
import { createRemoteRepository, createTestingModule, TestContext } from './testUtils';
|
import { newImmichHooksMock } from './mocks';
|
||||||
|
import { createTestingModule, TestContext } from './testUtils';
|
||||||
|
|
||||||
let ctx: TestContext;
|
let ctx: TestContext;
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ describe('Integrations', () => {
|
|||||||
dataPath: '/data/immich',
|
dataPath: '/data/immich',
|
||||||
dataFolders: ['upload', 'library'],
|
dataFolders: ['upload', 'library'],
|
||||||
libraries: [],
|
libraries: [],
|
||||||
|
hooks: newImmichHooksMock(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -79,6 +81,7 @@ describe('Integrations', () => {
|
|||||||
{ id: 'lib-1', name: 'Photos', importPaths: ['/mnt/photos'], exclusionPatterns: [] },
|
{ id: 'lib-1', name: 'Photos', importPaths: ['/mnt/photos'], exclusionPatterns: [] },
|
||||||
{ id: 'lib-2', name: 'Videos', importPaths: ['/mnt/videos'], exclusionPatterns: [] },
|
{ id: 'lib-2', name: 'Videos', importPaths: ['/mnt/videos'], exclusionPatterns: [] },
|
||||||
],
|
],
|
||||||
|
hooks: newImmichHooksMock(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -119,6 +122,7 @@ describe('Integrations', () => {
|
|||||||
dataPath: '/data/immich',
|
dataPath: '/data/immich',
|
||||||
dataFolders: ['upload'],
|
dataFolders: ['upload'],
|
||||||
libraries: [],
|
libraries: [],
|
||||||
|
hooks: newImmichHooksMock(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -155,10 +159,7 @@ describe('Integrations', () => {
|
|||||||
moduleConfig.update({ immichIntegration: undefined });
|
moduleConfig.update({ immichIntegration: undefined });
|
||||||
});
|
});
|
||||||
|
|
||||||
const hooks = {
|
const hooks = newImmichHooksMock();
|
||||||
getImmichDatabaseDumpConfig: jest.fn().mockResolvedValue({ enabled: true, keepLastAmount: 14 }),
|
|
||||||
configureImmichDatabaseDump: jest.fn(),
|
|
||||||
};
|
|
||||||
it('pauses and resumes the managed schedule', async () => {
|
it('pauses and resumes the managed schedule', async () => {
|
||||||
const moduleConfig = ctx.module.get(ModuleConfigRepository);
|
const moduleConfig = ctx.module.get(ModuleConfigRepository);
|
||||||
const integrationsService = ctx.module.get(IntegrationsService);
|
const integrationsService = ctx.module.get(IntegrationsService);
|
||||||
@@ -170,7 +171,7 @@ describe('Integrations', () => {
|
|||||||
dataPath: '/data/immich',
|
dataPath: '/data/immich',
|
||||||
dataFolders: ['upload'],
|
dataFolders: ['upload'],
|
||||||
libraries: [],
|
libraries: [],
|
||||||
hooks: hooks as never,
|
hooks,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,7 +193,8 @@ describe('Integrations', () => {
|
|||||||
return schedules.find((entry) => entry.id === integration?.scheduleId);
|
return schedules.find((entry) => entry.id === integration?.scheduleId);
|
||||||
};
|
};
|
||||||
|
|
||||||
expect((await getSchedule())?.paused).toBe(false);
|
const initialSchedule = await getSchedule();
|
||||||
|
expect(initialSchedule?.paused).toBe(false);
|
||||||
|
|
||||||
const status = await integrationsService.getImmichBackupStatus();
|
const status = await integrationsService.getImmichBackupStatus();
|
||||||
expect(status.databaseDump).toEqual({ enabled: true, keepLastAmount: 14 });
|
expect(status.databaseDump).toEqual({ enabled: true, keepLastAmount: 14 });
|
||||||
@@ -202,15 +204,18 @@ describe('Integrations', () => {
|
|||||||
|
|
||||||
expect(status.databaseDumpWarningIgnored).toBe(false);
|
expect(status.databaseDumpWarningIgnored).toBe(false);
|
||||||
await integrationsService.ignoreImmichDatabaseDumpWarning();
|
await integrationsService.ignoreImmichDatabaseDumpWarning();
|
||||||
expect((await integrationsService.getImmichBackupStatus()).databaseDumpWarningIgnored).toBe(true);
|
const ignoredStatus = await integrationsService.getImmichBackupStatus();
|
||||||
|
expect(ignoredStatus.databaseDumpWarningIgnored).toBe(true);
|
||||||
|
|
||||||
await integrationsService.configureImmichIntegration({ ...configuration, paused: true });
|
await integrationsService.configureImmichIntegration({ ...configuration, paused: true });
|
||||||
|
|
||||||
expect((await getSchedule())?.paused).toBe(true);
|
const pausedSchedule = await getSchedule();
|
||||||
|
expect(pausedSchedule?.paused).toBe(true);
|
||||||
|
|
||||||
await integrationsService.configureImmichIntegration({ ...configuration, paused: false });
|
await integrationsService.configureImmichIntegration({ ...configuration, paused: false });
|
||||||
|
|
||||||
expect((await getSchedule())?.paused).toBe(false);
|
const resumedSchedule = await getSchedule();
|
||||||
|
expect(resumedSchedule?.paused).toBe(false);
|
||||||
|
|
||||||
await immichRepository.delete();
|
await immichRepository.delete();
|
||||||
moduleConfig.update({ immichIntegration: undefined });
|
moduleConfig.update({ immichIntegration: undefined });
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { ImmichIntegration } from 'src/moduleConfig';
|
||||||
import type { ResticRepository } from 'src/repositories/restic.repository';
|
import type { ResticRepository } from 'src/repositories/restic.repository';
|
||||||
|
|
||||||
export type RepositoryInterface<T extends object> = Pick<T, keyof T>;
|
export type RepositoryInterface<T extends object> = Pick<T, keyof T>;
|
||||||
@@ -8,6 +9,7 @@ export const newResticRepositoryMock = (): jest.Mocked<RepositoryInterface<Resti
|
|||||||
backup: jest.fn(),
|
backup: jest.fn(),
|
||||||
restore: jest.fn(),
|
restore: jest.fn(),
|
||||||
snapshots: jest.fn().mockResolvedValue([]),
|
snapshots: jest.fn().mockResolvedValue([]),
|
||||||
|
snapshot: jest.fn(),
|
||||||
stats: jest.fn().mockResolvedValue({ total_size: 0, snapshots_count: 0 }),
|
stats: jest.fn().mockResolvedValue({ total_size: 0, snapshots_count: 0 }),
|
||||||
forget: jest.fn(),
|
forget: jest.fn(),
|
||||||
forgetByPolicy: jest.fn().mockResolvedValue([]),
|
forgetByPolicy: jest.fn().mockResolvedValue([]),
|
||||||
@@ -17,3 +19,11 @@ export const newResticRepositoryMock = (): jest.Mocked<RepositoryInterface<Resti
|
|||||||
unlockAll: jest.fn(),
|
unlockAll: jest.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const newImmichHooksMock = (): jest.Mocked<ImmichIntegration['hooks']> => ({
|
||||||
|
createDatabaseBackup: jest.fn().mockResolvedValue('dump.sql.gz'),
|
||||||
|
cleanupDatabaseBackups: jest.fn(),
|
||||||
|
getImmichDatabaseDumpConfig: jest.fn().mockResolvedValue({ enabled: true, keepLastAmount: 14 }),
|
||||||
|
configureImmichDatabaseDump: jest.fn(),
|
||||||
|
enterMaintenanceRollback: jest.fn().mockResolvedValue({ jwt: 'jwt' }),
|
||||||
|
});
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ describe('Schedule', () => {
|
|||||||
|
|
||||||
const { schedule } = await scheduleService.createSchedule({
|
const { schedule } = await scheduleService.createSchedule({
|
||||||
name: 'Locked Schedule',
|
name: 'Locked Schedule',
|
||||||
|
paused: false,
|
||||||
cron: '0 0 1 1 *',
|
cron: '0 0 1 1 *',
|
||||||
repositories: [repository.id],
|
repositories: [repository.id],
|
||||||
});
|
});
|
||||||
@@ -49,6 +50,7 @@ describe('Schedule', () => {
|
|||||||
it('skips task creation when schedule has no repositories', async () => {
|
it('skips task creation when schedule has no repositories', async () => {
|
||||||
const { schedule } = await ctx.module.get(ScheduleService).createSchedule({
|
const { schedule } = await ctx.module.get(ScheduleService).createSchedule({
|
||||||
name: 'Empty Schedule',
|
name: 'Empty Schedule',
|
||||||
|
paused: false,
|
||||||
cron: '0 0 1 1 *',
|
cron: '0 0 1 1 *',
|
||||||
repositories: [],
|
repositories: [],
|
||||||
});
|
});
|
||||||
@@ -85,6 +87,7 @@ describe('Schedule', () => {
|
|||||||
|
|
||||||
const { schedule } = await scheduleService.createSchedule({
|
const { schedule } = await scheduleService.createSchedule({
|
||||||
name: 'Multi Schedule',
|
name: 'Multi Schedule',
|
||||||
|
paused: false,
|
||||||
cron: '0 0 1 1 *',
|
cron: '0 0 1 1 *',
|
||||||
repositories: [repo1.id, repo2.id],
|
repositories: [repo1.id, repo2.id],
|
||||||
});
|
});
|
||||||
@@ -121,17 +124,11 @@ describe('Schedule', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ctx.resticMock.backup.mockReset();
|
ctx.resticMock.backup.mockReset();
|
||||||
let callCount = 0;
|
ctx.resticMock.backup.mockRejectedValueOnce(new Error('Simulated backup failure'));
|
||||||
ctx.resticMock.backup.mockImplementation(() => {
|
|
||||||
callCount++;
|
|
||||||
if (callCount === 1) {
|
|
||||||
return Promise.reject(new Error('Simulated backup failure'));
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
const { schedule } = await scheduleService.createSchedule({
|
const { schedule } = await scheduleService.createSchedule({
|
||||||
name: 'Resilient Schedule',
|
name: 'Resilient Schedule',
|
||||||
|
paused: false,
|
||||||
cron: '0 0 1 1 *',
|
cron: '0 0 1 1 *',
|
||||||
repositories: [failRepo.id, successRepo.id],
|
repositories: [failRepo.id, successRepo.id],
|
||||||
});
|
});
|
||||||
@@ -140,7 +137,6 @@ describe('Schedule', () => {
|
|||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||||
|
|
||||||
expect(callCount).toBe(2);
|
|
||||||
expect(ctx.resticMock.backup).toHaveBeenCalledTimes(2);
|
expect(ctx.resticMock.backup).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
ctx.resticMock.backup.mockReset();
|
ctx.resticMock.backup.mockReset();
|
||||||
@@ -171,6 +167,7 @@ describe('Schedule', () => {
|
|||||||
|
|
||||||
const { schedule } = await scheduleService.createSchedule({
|
const { schedule } = await scheduleService.createSchedule({
|
||||||
name: 'Warn Schedule',
|
name: 'Warn Schedule',
|
||||||
|
paused: false,
|
||||||
cron: '0 0 1 1 *',
|
cron: '0 0 1 1 *',
|
||||||
repositories: [warnRepo.id],
|
repositories: [warnRepo.id],
|
||||||
});
|
});
|
||||||
@@ -200,6 +197,7 @@ describe('Schedule', () => {
|
|||||||
|
|
||||||
const { schedule } = await scheduleService.createSchedule({
|
const { schedule } = await scheduleService.createSchedule({
|
||||||
name: 'Ordering Schedule',
|
name: 'Ordering Schedule',
|
||||||
|
paused: false,
|
||||||
cron: '0 0 1 1 *',
|
cron: '0 0 1 1 *',
|
||||||
repositories: [existing.id],
|
repositories: [existing.id],
|
||||||
});
|
});
|
||||||
@@ -223,6 +221,7 @@ describe('Schedule', () => {
|
|||||||
|
|
||||||
const { schedule } = await scheduleService.createSchedule({
|
const { schedule } = await scheduleService.createSchedule({
|
||||||
name: 'Immich Schedule',
|
name: 'Immich Schedule',
|
||||||
|
paused: false,
|
||||||
cron: '0 2 * * *',
|
cron: '0 2 * * *',
|
||||||
repositories: [repository.id],
|
repositories: [repository.id],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { ModuleConfigProvider } from 'src/moduleConfig';
|
|||||||
import { controllers, repositories, services } from 'src/orchestrationApi.module';
|
import { controllers, repositories, services } from 'src/orchestrationApi.module';
|
||||||
import { BackendRepository } from 'src/repositories/backend.repository';
|
import { BackendRepository } from 'src/repositories/backend.repository';
|
||||||
import { ResticRepository } from 'src/repositories/restic.repository';
|
import { ResticRepository } from 'src/repositories/restic.repository';
|
||||||
import { newResticRepositoryMock } from './mocks';
|
import { newResticRepositoryMock, RepositoryInterface } from './mocks';
|
||||||
|
|
||||||
type GatewayListener = (event: GatewayEvent) => void;
|
type GatewayListener = (event: GatewayEvent) => void;
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export interface TestContext {
|
|||||||
gateway: EventsGateway;
|
gateway: EventsGateway;
|
||||||
events: TestEventBus;
|
events: TestEventBus;
|
||||||
database: InstanceType<typeof Database>;
|
database: InstanceType<typeof Database>;
|
||||||
resticMock: jest.Mocked<ResticRepository>;
|
resticMock: jest.Mocked<RepositoryInterface<ResticRepository>>;
|
||||||
backendId: string;
|
backendId: string;
|
||||||
statePath: string;
|
statePath: string;
|
||||||
backendPath: string;
|
backendPath: string;
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "src"
|
||||||
|
},
|
||||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"strictBindCallApply": false,
|
"strictBindCallApply": false,
|
||||||
"noFallthroughCasesInSwitch": false,
|
"noFallthroughCasesInSwitch": false,
|
||||||
"rootDir": "src",
|
"baseUrl": ".",
|
||||||
|
"paths": { "src/*": ["src/*"] },
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"types": ["@types/node", "@types/jest"]
|
"types": ["@types/node", "@types/jest"]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user