mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
refactor(server): system config (#1353)
* refactor(server): system config * fix: jest circular import * chore: ignore migrations in coverage report * chore: tests * chore: tests * chore: todo note * chore: remove vite config backup * chore: fix redis hostname
This commit is contained in:
parent
66cd7dd809
commit
c0a6b3d5a3
92 changed files with 842 additions and 614 deletions
|
|
@ -0,0 +1,156 @@
|
|||
import { SystemConfigEntity, SystemConfigKey } from '@app/infra';
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { newJobRepositoryMock, newSystemConfigRepositoryMock, systemConfigStub } from '../../test';
|
||||
import { IJobRepository, JobName } from '../job';
|
||||
import { SystemConfigValidator } from './system-config.core';
|
||||
import { ISystemConfigRepository } from './system-config.repository';
|
||||
import { SystemConfigService } from './system-config.service';
|
||||
|
||||
const updates: SystemConfigEntity[] = [
|
||||
{ key: SystemConfigKey.FFMPEG_CRF, value: 'a new value' },
|
||||
{ key: SystemConfigKey.OAUTH_AUTO_LAUNCH, value: true },
|
||||
];
|
||||
|
||||
const updatedConfig = Object.freeze({
|
||||
ffmpeg: {
|
||||
crf: 'a new value',
|
||||
preset: 'ultrafast',
|
||||
targetAudioCodec: 'mp3',
|
||||
targetScaling: '1280:-2',
|
||||
targetVideoCodec: 'libx264',
|
||||
},
|
||||
oauth: {
|
||||
autoLaunch: true,
|
||||
autoRegister: true,
|
||||
buttonText: 'Login with OAuth',
|
||||
clientId: '',
|
||||
clientSecret: '',
|
||||
enabled: false,
|
||||
issuerUrl: '',
|
||||
mobileOverrideEnabled: false,
|
||||
mobileRedirectUri: '',
|
||||
scope: 'openid email profile',
|
||||
},
|
||||
passwordLogin: {
|
||||
enabled: true,
|
||||
},
|
||||
storageTemplate: {
|
||||
template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
|
||||
},
|
||||
});
|
||||
|
||||
describe(SystemConfigService.name, () => {
|
||||
let sut: SystemConfigService;
|
||||
let configMock: jest.Mocked<ISystemConfigRepository>;
|
||||
let jobMock: jest.Mocked<IJobRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
configMock = newSystemConfigRepositoryMock();
|
||||
jobMock = newJobRepositoryMock();
|
||||
sut = new SystemConfigService(configMock, jobMock);
|
||||
});
|
||||
|
||||
it('should work', () => {
|
||||
expect(sut).toBeDefined();
|
||||
});
|
||||
|
||||
describe('getDefaults', () => {
|
||||
it('should return the default config', () => {
|
||||
configMock.load.mockResolvedValue(updates);
|
||||
|
||||
expect(sut.getDefaults()).toEqual(systemConfigStub.defaults);
|
||||
expect(configMock.load).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('addValidator', () => {
|
||||
it('should call the validator on config changes', async () => {
|
||||
const validator: SystemConfigValidator = jest.fn();
|
||||
|
||||
sut.addValidator(validator);
|
||||
|
||||
await sut.updateConfig(systemConfigStub.defaults);
|
||||
|
||||
expect(validator).toHaveBeenCalledWith(systemConfigStub.defaults);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConfig', () => {
|
||||
it('should return the default config', async () => {
|
||||
configMock.load.mockResolvedValue([]);
|
||||
|
||||
await expect(sut.getConfig()).resolves.toEqual(systemConfigStub.defaults);
|
||||
});
|
||||
|
||||
it('should merge the overrides', async () => {
|
||||
configMock.load.mockResolvedValue([
|
||||
{ key: SystemConfigKey.FFMPEG_CRF, value: 'a new value' },
|
||||
{ key: SystemConfigKey.OAUTH_AUTO_LAUNCH, value: true },
|
||||
]);
|
||||
|
||||
await expect(sut.getConfig()).resolves.toEqual(updatedConfig);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStorageTemplateOptions', () => {
|
||||
it('should send back the datetime variables', () => {
|
||||
expect(sut.getStorageTemplateOptions()).toEqual({
|
||||
dayOptions: ['d', 'dd'],
|
||||
hourOptions: ['h', 'hh', 'H', 'HH'],
|
||||
minuteOptions: ['m', 'mm'],
|
||||
monthOptions: ['M', 'MM', 'MMM', 'MMMM'],
|
||||
presetOptions: [
|
||||
'{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
|
||||
'{{y}}/{{MM}}-{{dd}}/{{filename}}',
|
||||
'{{y}}/{{MMMM}}-{{dd}}/{{filename}}',
|
||||
'{{y}}/{{MM}}/{{filename}}',
|
||||
'{{y}}/{{MMM}}/{{filename}}',
|
||||
'{{y}}/{{MMMM}}/{{filename}}',
|
||||
'{{y}}/{{MM}}/{{dd}}/{{filename}}',
|
||||
'{{y}}/{{MMMM}}/{{dd}}/{{filename}}',
|
||||
'{{y}}/{{y}}-{{MM}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
|
||||
'{{y}}-{{MM}}-{{dd}}/{{filename}}',
|
||||
'{{y}}-{{MMM}}-{{dd}}/{{filename}}',
|
||||
'{{y}}-{{MMMM}}-{{dd}}/{{filename}}',
|
||||
],
|
||||
secondOptions: ['s', 'ss'],
|
||||
yearOptions: ['y', 'yy'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateConfig', () => {
|
||||
it('should notify the microservices process', async () => {
|
||||
configMock.load.mockResolvedValue(updates);
|
||||
|
||||
await expect(sut.updateConfig(updatedConfig)).resolves.toEqual(updatedConfig);
|
||||
|
||||
expect(configMock.saveAll).toHaveBeenCalledWith(updates);
|
||||
expect(jobMock.add).toHaveBeenCalledWith({ name: JobName.CONFIG_CHANGE });
|
||||
});
|
||||
|
||||
it('should throw an error if the config is not valid', async () => {
|
||||
const validator = jest.fn().mockRejectedValue('invalid config');
|
||||
|
||||
sut.addValidator(validator);
|
||||
|
||||
await expect(sut.updateConfig(updatedConfig)).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(validator).toHaveBeenCalledWith(updatedConfig);
|
||||
expect(configMock.saveAll).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('refreshConfig', () => {
|
||||
it('should notify the subscribers', async () => {
|
||||
const changeMock = jest.fn();
|
||||
const subscription = sut.config$.subscribe(changeMock);
|
||||
|
||||
await sut.refreshConfig();
|
||||
|
||||
expect(changeMock).toHaveBeenCalledWith(systemConfigStub.defaults);
|
||||
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue