test: cover user admin queries and config compatibility for local.4
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@ name: immich_mobile
|
||||
description: Immich - selfhosted backup media file on mobile phone
|
||||
|
||||
publish_to: 'none'
|
||||
version: 3.3.3-local.3+3030402
|
||||
version: 3.3.3-local.4+3030403
|
||||
|
||||
environment:
|
||||
sdk: '>=3.12.0 <4.0.0'
|
||||
|
||||
@@ -6,6 +6,51 @@ import 'package:openapi/api.dart';
|
||||
|
||||
void main() {
|
||||
group('Test OpenApi Patching', () {
|
||||
test('older public config deserializes with map and video defaults', () {
|
||||
final config = PublicConfigDto.fromJson({
|
||||
'oauth': {'autoLaunch': false, 'buttonText': 'Login', 'enabled': false},
|
||||
'passwordLogin': {'enabled': true},
|
||||
'server': {'loginPageMessage': ''},
|
||||
});
|
||||
expect(config, isNotNull);
|
||||
expect(config!.map.enabled, isTrue);
|
||||
expect(config.map.lightStyle, 'https://tiles.immich.cloud/v1/style/light.json');
|
||||
expect(config.ffmpeg.realtime.enabled, isFalse);
|
||||
});
|
||||
|
||||
test('public config keeps explicit server map and video settings', () {
|
||||
final config = PublicConfigDto.fromJson({
|
||||
'oauth': {'autoLaunch': false, 'buttonText': 'Login', 'enabled': false},
|
||||
'passwordLogin': {'enabled': true},
|
||||
'server': {'loginPageMessage': ''},
|
||||
'map': {'enabled': false, 'lightStyle': 'https://example.com/light', 'darkStyle': 'https://example.com/dark'},
|
||||
'ffmpeg': {
|
||||
'realtime': {'enabled': true},
|
||||
},
|
||||
});
|
||||
expect(config!.map.enabled, isFalse);
|
||||
expect(config.map.lightStyle, 'https://example.com/light');
|
||||
expect(config.ffmpeg.realtime.enabled, isTrue);
|
||||
});
|
||||
|
||||
test('older user config defaults SMTP to disabled', () {
|
||||
final value = <String, dynamic>{};
|
||||
upgradeDto(value, 'UserConfigDto');
|
||||
final notifications = UserConfigNotificationsDto.fromJson(value['notifications']);
|
||||
expect(notifications!.smtp.enabled, isFalse);
|
||||
});
|
||||
|
||||
test('user config preserves an enabled SMTP setting', () {
|
||||
final value = <String, dynamic>{
|
||||
'notifications': {
|
||||
'smtp': {'enabled': true},
|
||||
},
|
||||
};
|
||||
upgradeDto(value, 'UserConfigDto');
|
||||
final notifications = UserConfigNotificationsDto.fromJson(value['notifications']);
|
||||
expect(notifications!.smtp.enabled, isTrue);
|
||||
});
|
||||
|
||||
test('upgradeDto', () {
|
||||
dynamic value;
|
||||
String targetType;
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import {
|
||||
createUserAdmin,
|
||||
deleteUserAdmin,
|
||||
restoreUserAdmin,
|
||||
searchUsersAdmin,
|
||||
updateUserAdmin,
|
||||
type UserAdminResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { QueryObserver } from '@tanstack/svelte-query';
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import { executeMutation, queryClient } from '$lib/queries/client';
|
||||
import {
|
||||
createUserAdminMutation,
|
||||
deleteUserAdminMutation,
|
||||
getUserAdminQuery,
|
||||
restoreUserAdminMutation,
|
||||
searchUsersAdminQuery,
|
||||
updateUserAdminMutation,
|
||||
} from '$lib/queries/user-admin.query';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
|
||||
vi.mock('$lib', () => ({ getHeatmapRange: vi.fn() }));
|
||||
vi.mock('@immich/ui', () => ({ toastManager: { primary: vi.fn(), info: vi.fn() } }));
|
||||
vi.mock('$lib/utils/handle-error', () => ({ handleError: vi.fn() }));
|
||||
vi.mock('$lib/utils/i18n', () => ({ getFormatter: () => Promise.resolve((key: string) => key) }));
|
||||
vi.mock('@immich/sdk', () => ({
|
||||
CalendarHeatmapType: { Upload: 'upload' },
|
||||
createUserAdmin: vi.fn(),
|
||||
deleteUserAdmin: vi.fn(),
|
||||
restoreUserAdmin: vi.fn(),
|
||||
updateUserAdmin: vi.fn(),
|
||||
searchUsersAdmin: vi.fn(),
|
||||
getUserCalendarHeatmapAdmin: vi.fn(),
|
||||
getUserPreferencesAdmin: vi.fn(),
|
||||
getUserSessionsAdmin: vi.fn(),
|
||||
getUserStatisticsAdmin: vi.fn(),
|
||||
}));
|
||||
|
||||
const active = { id: 'active', name: 'Active', deletedAt: null } as UserAdminResponseDto;
|
||||
const deleted = { id: 'deleted', name: 'Deleted', deletedAt: '2026-10-16T00:00:00Z' } as UserAdminResponseDto;
|
||||
|
||||
describe('admin user query regressions', () => {
|
||||
beforeEach(() => {
|
||||
queryClient.clear();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
afterEach(() => queryClient.clear());
|
||||
|
||||
it('shares one request while filtering deleted users only for active-user consumers', async () => {
|
||||
vi.mocked(searchUsersAdmin).mockResolvedValue([active, deleted]);
|
||||
await Promise.all([queryClient.query(searchUsersAdminQuery()), queryClient.query(getUserAdminQuery(deleted.id))]);
|
||||
const activeUsers = new QueryObserver(queryClient, searchUsersAdminQuery());
|
||||
const allUsers = new QueryObserver(queryClient, searchUsersAdminQuery({ withDeleted: true }));
|
||||
const detail = new QueryObserver(queryClient, getUserAdminQuery(deleted.id));
|
||||
expect(searchUsersAdmin).toHaveBeenCalledTimes(1);
|
||||
expect(activeUsers.getCurrentResult().data).toEqual([active]);
|
||||
expect(allUsers.getCurrentResult().data).toEqual([active, deleted]);
|
||||
expect(detail.getCurrentResult().data).toEqual(deleted);
|
||||
activeUsers.destroy();
|
||||
allUsers.destroy();
|
||||
detail.destroy();
|
||||
});
|
||||
|
||||
it('preserves the user list and reports an error when deletion fails', async () => {
|
||||
queryClient.setQueryData(searchUsersAdminQuery().queryKey, [active, deleted]);
|
||||
vi.mocked(deleteUserAdmin).mockRejectedValue(new Error('offline'));
|
||||
await executeMutation(deleteUserAdminMutation, [{ id: active.id }]);
|
||||
expect(queryClient.getQueryData(searchUsersAdminQuery().queryKey)).toEqual([active, deleted]);
|
||||
expect(handleError).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('uses the server response for edits without changing another user', async () => {
|
||||
queryClient.setQueryData(searchUsersAdminQuery().queryKey, [active, deleted]);
|
||||
const saved = { ...active, name: 'Saved', quotaSizeInBytes: 1024 };
|
||||
vi.mocked(updateUserAdmin).mockResolvedValue(saved);
|
||||
await executeMutation(updateUserAdminMutation, [{ id: active.id, userAdminUpdateDto: { name: saved.name } }]);
|
||||
expect(queryClient.getQueryData(searchUsersAdminQuery().queryKey)).toEqual([saved, deleted]);
|
||||
});
|
||||
|
||||
it('makes a restored account available to active-user consumers', async () => {
|
||||
queryClient.setQueryData(searchUsersAdminQuery().queryKey, [active, deleted]);
|
||||
const restored = { ...deleted, deletedAt: null };
|
||||
vi.mocked(restoreUserAdmin).mockResolvedValue(restored);
|
||||
await executeMutation(restoreUserAdminMutation, [{ id: deleted.id }]);
|
||||
const observer = new QueryObserver(queryClient, searchUsersAdminQuery());
|
||||
expect(observer.getCurrentResult().data).toEqual([active, restored]);
|
||||
observer.destroy();
|
||||
});
|
||||
|
||||
it('refreshes the user list after account creation', async () => {
|
||||
queryClient.setQueryData(searchUsersAdminQuery().queryKey, [active]);
|
||||
vi.mocked(createUserAdmin).mockResolvedValue(deleted);
|
||||
await executeMutation(createUserAdminMutation, [
|
||||
{ userAdminCreateDto: { email: 'test@example.com', name: 'Test', password: 'test-only-password' } },
|
||||
]);
|
||||
expect(queryClient.getQueryState(searchUsersAdminQuery().queryKey)?.isInvalidated).toBe(true);
|
||||
});
|
||||
|
||||
it('clears admin user data on logout', () => {
|
||||
queryClient.setQueryData(searchUsersAdminQuery().queryKey, [active, deleted]);
|
||||
queryClient.setQueryData(['user-admin', active.id, 'sessions'], [{ id: 'session' }]);
|
||||
eventManager.emit('AuthLogout');
|
||||
expect(queryClient.getQueryCache().getAll()).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user