mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
724c88c201
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.
106 lines
4.0 KiB
TypeScript
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' });
|
|
});
|
|
});
|