From 9e889bd4dc452a64c5f857162464465a416e0477 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 22 Oct 2026 20:48:48 +0300 Subject: [PATCH] Adapt local regression tests to new configuration queries --- server/src/dtos/config.dto.spec.ts | 11 ++++ .../lib/queries/release-regressions.spec.ts | 53 ++++++++++++------- .../lib/services/user-admin.service.spec.ts | 8 +-- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/server/src/dtos/config.dto.spec.ts b/server/src/dtos/config.dto.spec.ts index 9d2d63d55..8eca6053e 100644 --- a/server/src/dtos/config.dto.spec.ts +++ b/server/src/dtos/config.dto.spec.ts @@ -39,6 +39,17 @@ describe('config visibility', () => { expect(getKeysDeep(mapUserConfig(defaults))).toEqual(expect.arrayContaining(PUBLIC_PROPERTIES)); }); + it('exposes the OAuth account link to users without exposing client secrets or the link publicly', () => { + const config = { + ...defaults, + oauth: { ...defaults.oauth, accountManagementUrl: 'https://accounts.example.com', clientSecret: 'private' }, + }; + expect(mapUserConfig(config).oauth.accountManagementUrl).toBe('https://accounts.example.com'); + expect(mapUserConfig(config).oauth).not.toHaveProperty('clientSecret'); + expect(mapPublicConfig(config).oauth).not.toHaveProperty('accountManagementUrl'); + expect(mapPublicConfig(config).oauth).not.toHaveProperty('clientSecret'); + }); + it('should accept the defaults with the admin schema', () => { expect(AdminConfigDto.schema.safeParse(defaults)).toEqual(expect.objectContaining({ success: true })); }); diff --git a/web/src/lib/queries/release-regressions.spec.ts b/web/src/lib/queries/release-regressions.spec.ts index 06d2cad83..84f27c2c0 100644 --- a/web/src/lib/queries/release-regressions.spec.ts +++ b/web/src/lib/queries/release-regressions.spec.ts @@ -1,22 +1,23 @@ import { deleteLibrary, - getConfig, + getAdminConfig, getStorage, - updateConfig, + updateAdminConfig, updateLibrary, type AdminConfigDto, type LibraryResponseDto, } from '@immich/sdk'; import { eventManager } from '$lib/managers/event-manager.svelte'; import { executeMutation, queryClient } from '$lib/queries/client'; +import { getAdminConfigQuery, updateAdminConfigMutation } from '$lib/queries/config-admin.query'; +import { getPublicConfigQuery } from '$lib/queries/config-public.query'; import { deleteLibraryMutation, getAllLibrariesQuery, getLibraryStatisticsQuery, updateLibraryMutation, } from '$lib/queries/library.query'; -import { getStorageQuery } from '$lib/queries/server.query'; -import { getConfigQuery, updateConfigMutation } from '$lib/queries/system-config.query'; +import { getServerConfigQuery, getStorageQuery } from '$lib/queries/server.query'; import { handleError } from '$lib/utils/handle-error'; vi.mock('@immich/ui', () => ({ toastManager: { primary: vi.fn(), info: vi.fn() } })); @@ -33,9 +34,12 @@ vi.mock('@immich/sdk', () => ({ getVersionHistory: vi.fn(), getStorage: vi.fn(), getServerStatistics: vi.fn(), - getConfig: vi.fn(), - getConfigDefaults: vi.fn(), - updateConfig: vi.fn(), + getServerConfig: vi.fn(), + getPublicConfig: vi.fn(), + getUserConfig: vi.fn(), + getAdminConfig: vi.fn(), + getAdminConfigDefaults: vi.fn(), + updateAdminConfig: vi.fn(), })); const first = { id: 'first', name: 'Photos' } as LibraryResponseDto; @@ -51,11 +55,24 @@ describe('shared queries after the upstream migration', () => { it('clears account data on logout', () => { queryClient.setQueryData(getAllLibrariesQuery().queryKey, [first]); - queryClient.setQueryData(getConfigQuery().queryKey, {} as AdminConfigDto); + queryClient.setQueryData(getAdminConfigQuery().queryKey, {} as AdminConfigDto); eventManager.emit('AuthLogout'); expect(queryClient.getQueryCache().getAll()).toHaveLength(0); }); + it('retains public login configuration while clearing private settings on logout', () => { + queryClient.setQueryData(getPublicConfigQuery().queryKey, { passwordLogin: { enabled: true } } as never); + queryClient.setQueryData(getServerConfigQuery().queryKey, { maintenanceMode: false } as never); + queryClient.setQueryData(getAdminConfigQuery().queryKey, {} as AdminConfigDto); + queryClient.setQueryData(['config', 'user'], { oauth: { accountManagementUrl: 'https://accounts.example.com' } }); + eventManager.emit('AuthLogout'); + expect(queryClient.getQueryData(getPublicConfigQuery().queryKey)).toEqual({ passwordLogin: { enabled: true } }); + expect(queryClient.getQueryData(getServerConfigQuery().queryKey)).toEqual({ maintenanceMode: false }); + expect(queryClient.getQueryData(getAdminConfigQuery().queryKey)).toBeUndefined(); + expect(queryClient.getQueryData(['config', 'user'])).toBeUndefined(); + expect(queryClient.getQueryCache().getAll()).toHaveLength(2); + }); + it('shares a storage request between consumers', async () => { const storage = { diskSizeRaw: 100, diskUseRaw: 20 } as Awaited>; vi.mocked(getStorage).mockResolvedValue(storage); @@ -112,20 +129,20 @@ describe('shared queries after the upstream migration', () => { const current = { server: { externalDomain: 'https://example.com' }, trash: { enabled: true, days: 30 } }; const update = { trash: { enabled: true, days: 60 } }; const saved = { ...current, ...update } as AdminConfigDto; - vi.mocked(getConfig).mockResolvedValue(current as AdminConfigDto); - vi.mocked(updateConfig).mockResolvedValue(saved); - await executeMutation(updateConfigMutation, update); - expect(updateConfig).toHaveBeenCalledWith({ adminConfigDto: saved }); - expect(queryClient.getQueryData(getConfigQuery().queryKey)).toEqual(saved); + vi.mocked(getAdminConfig).mockResolvedValue(current as AdminConfigDto); + vi.mocked(updateAdminConfig).mockResolvedValue(saved); + await executeMutation(updateAdminConfigMutation, update); + expect(updateAdminConfig).toHaveBeenCalledWith({ adminConfigDto: saved }); + expect(queryClient.getQueryData(getAdminConfigQuery().queryKey)).toEqual(saved); }); it('keeps the previous settings if saving fails', async () => { const current = { trash: { enabled: true, days: 30 } } as AdminConfigDto; - queryClient.setQueryData(getConfigQuery().queryKey, current); - vi.mocked(getConfig).mockResolvedValue(current); - vi.mocked(updateConfig).mockRejectedValue(new Error('offline')); - await executeMutation(updateConfigMutation, { trash: { enabled: true, days: 60 } }); - expect(queryClient.getQueryData(getConfigQuery().queryKey)).toEqual(current); + queryClient.setQueryData(getAdminConfigQuery().queryKey, current); + vi.mocked(getAdminConfig).mockResolvedValue(current); + vi.mocked(updateAdminConfig).mockRejectedValue(new Error('offline')); + await executeMutation(updateAdminConfigMutation, { trash: { enabled: true, days: 60 } }); + expect(queryClient.getQueryData(getAdminConfigQuery().queryKey)).toEqual(current); expect(handleError).toHaveBeenCalledOnce(); }); }); diff --git a/web/src/lib/services/user-admin.service.spec.ts b/web/src/lib/services/user-admin.service.spec.ts index 8ebc08cdb..6c4ef6cbb 100644 --- a/web/src/lib/services/user-admin.service.spec.ts +++ b/web/src/lib/services/user-admin.service.spec.ts @@ -5,13 +5,13 @@ import { getUserAdminActions } from '$lib/services/user-admin.service'; vi.mock('$app/navigation', () => ({ goto: vi.fn() })); vi.mock('@immich/ui', () => ({ modalManager: { show: vi.fn(), showDialog: vi.fn() } })); vi.mock('$lib/managers/auth-manager.svelte', () => ({ authManager: { user: { id: 'self' } } })); -vi.mock('$lib/managers/server-config-manager.svelte', () => ({ - serverConfigManager: { value: { userDeleteDelay: 7 } }, -})); vi.mock('$lib/modals/PasswordResetSuccessModal.svelte', () => ({ default: {} })); vi.mock('$lib/modals/UserDeleteConfirmModal.svelte', () => ({ default: {} })); vi.mock('$lib/modals/UserRestoreConfirmModal.svelte', () => ({ default: {} })); -vi.mock('$lib/queries/client', () => ({ executeMutation: vi.fn() })); +vi.mock('$lib/queries/client', () => ({ + executeMutation: vi.fn(), + readPreloadedQuery: () => ({ user: { deleteDelay: 7 } }), +})); vi.mock('$lib/queries/user-admin.query', () => ({ updateUserAdminMutation: vi.fn() })); vi.mock('$lib/route', () => ({ Route: { newUser: vi.fn(), viewUser: vi.fn(), editUser: vi.fn() } })); vi.mock('$lib/utils/i18n', () => ({ getFormatter: vi.fn() }));