Files
TREK/server/tests/unit/nest/admin-default-user-settings.controller.test.ts
T
Maurice 724c88c201 style: format server and shared with prettier
A mechanical prettier --write over server/src, server/tests, server/scripts
and shared/src with the workspaces' .prettierrc; nothing but layout and
import order changes, and every file keeps the same set of imports. shared
is formatted throughout and its format baseline is empty. The server
baseline keeps the 35 files that Prettier would grow past their lint:size
limit (they need a split first, which is not a formatting change), and the
generated entity files stay as their generator writes them.
2026-10-10 21:23:37 +02:00

106 lines
4.0 KiB
TypeScript

/**
* The account defaults, after the two routes moved off AdminController next to
* SettingsService, which owns them. Same path, same audit action, same 400 envelope on
* a rejected write — these cases came over with the routes.
*/
import type { RuntimeEnvService } from '../../../src/nest/app-config/runtime-env.service';
import type { AuditService } from '../../../src/nest/audit/audit.service';
import { AdminDefaultUserSettingsController } from '../../../src/nest/settings/settings.controller';
import { SettingsModule } from '../../../src/nest/settings/settings.module';
import type { SettingsService } from '../../../src/nest/settings/settings.service';
import type { User } from '../../../src/types';
import { expectRegisteredController } from '../../helpers/module-providers';
import { HttpException } from '@nestjs/common';
import { describe, it, expect, vi, beforeEach } from 'vitest';
const user = { id: 1, role: 'admin' } as User;
const req = { headers: {}, socket: {} } as never;
const writeAudit = vi.fn();
function controller(over: Partial<SettingsService> = {}) {
const settings = {
getAdminUserDefaults: vi.fn(() => ({ theme: 'dark' })),
setAdminUserDefaults: vi.fn(),
...over,
} as unknown as SettingsService;
return {
c: new AdminDefaultUserSettingsController(
settings,
{ writeAudit } as unknown as AuditService,
{ isManaged: () => false } as unknown as RuntimeEnvService,
),
settings,
};
}
const thrown = async (run: () => unknown) => {
try {
await run();
return null;
} catch (e) {
return e instanceof HttpException ? { status: e.getStatus(), body: e.getResponse() } : e;
}
};
describe('AdminDefaultUserSettingsController', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('DEFAULTS-001 GET returns the stored defaults verbatim', async () => {
expect(await controller().c.get()).toEqual({ theme: 'dark' });
});
it('DEFAULTS-002 PUT writes, audits, and answers with the STORED defaults', async () => {
const { c, settings } = controller();
// Not the request body: the service normalises and drops unknown keys, and the
// admin panel renders straight from this response.
expect(await c.update(user, { theme: 'light' } as never, req)).toEqual({ theme: 'dark' });
expect(settings.setAdminUserDefaults).toHaveBeenCalledWith({ theme: 'light' });
expect(writeAudit).toHaveBeenCalledWith(
expect.objectContaining({ userId: 1, action: 'admin.default_user_settings_update', details: { theme: 'light' } }),
);
});
it('DEFAULTS-003 a rejected write is a 400 carrying the message, and is not audited', async () => {
const { c } = controller({
setAdminUserDefaults: vi.fn(() => {
throw new Error('unknown setting: nope');
}),
} as Partial<SettingsService>);
expect(await thrown(() => c.update(user, { nope: 1 } as never, req))).toEqual({
status: 400,
body: { error: 'unknown setting: nope' },
});
expect(writeAudit).not.toHaveBeenCalled();
});
it('DEFAULTS-004 a non-Error throw is stringified rather than swallowed', async () => {
const { c } = controller({
setAdminUserDefaults: vi.fn(() => {
throw 'plain string';
}),
} as Partial<SettingsService>);
expect(await thrown(() => c.update(user, {} as never, req))).toEqual({
status: 400,
body: { error: 'plain string' },
});
});
it('DEFAULTS-005 the class is listed in its module controllers', () => {
expectRegisteredController(SettingsModule, AdminDefaultUserSettingsController);
});
it('DEFAULTS-006 a rejected read-back after a successful write is still a 400, not a 500', async () => {
const { c, settings } = controller({
getAdminUserDefaults: vi.fn(() => Promise.reject(new Error('read-back failed'))),
} as Partial<SettingsService>);
expect(await thrown(() => c.update(user, { theme: 'light' } as never, req))).toEqual({
status: 400,
body: { error: 'read-back failed' },
});
expect(settings.setAdminUserDefaults).toHaveBeenCalledWith({ theme: 'light' });
});
});