mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
Give three more admin surfaces back to the domains that own them
default-user-settings, notification-preferences and oidc leave AdminController. The first two were pure pass-throughs on AdminService that did nothing but forward and cast; the third had its SQL there for a domain that already had a module of its own. AdminService loses SettingsService and NotificationPreferencesService entirely. Two things came out of the move rather than going into it. The OIDC lockout guard now has tests. It refuses to clear the SSO config while password login is disabled — otherwise the instance has no SSO to log in through and no password form either — and nothing covered that branch before, because it sat among admin tests that only exercised the happy path. The admin e2e app had to learn about the split. It assembles AdminModule alone, so the moved routes 404'd there while working in production; it now imports the three domains too. That is the same failure mode the module-provider guards catch for RPC classes, and it is worth knowing that an e2e app which builds a subset will find it first. Also fixes the audit route note: audit-log stays on AdminController on purpose. AuthModule imports AuditModule, so an admin-gated controller in AuditModule closes a cycle — the same trap the addons toggles hit.
This commit is contained in:
@@ -84,10 +84,6 @@ describe('AdminController permissions + oidc + misc', () => {
|
||||
expect(c.savePermissions(user, { permissions: {} }, req)).toEqual({ success: true, permissions: {}, skipped: ['bad'] });
|
||||
});
|
||||
|
||||
it('oidc update maps error, else audits', () => {
|
||||
expect(thrown(() => adminCtl(svc({ updateOidcSettings: vi.fn().mockReturnValue({ error: 'bad issuer', status: 400 }) } as Partial<AdminService>)).updateOidc(user, {}, req))).toEqual({ status: 400, body: { error: 'bad issuer' } });
|
||||
expect(adminCtl(svc({ updateOidcSettings: vi.fn().mockReturnValue({}) } as Partial<AdminService>)).updateOidc(user, { issuer: 'https://idp' }, req)).toEqual({ success: true });
|
||||
});
|
||||
|
||||
it('save-demo-baseline maps error, else returns message', () => {
|
||||
expect(thrown(() => adminCtl(svc({ saveDemoBaseline: vi.fn().mockReturnValue({ error: 'not demo', status: 400 }) } as Partial<AdminService>)).saveDemoBaseline(user, req))).toEqual({ status: 400, body: { error: 'not demo' } });
|
||||
@@ -138,12 +134,6 @@ describe('AdminController addons + sessions + jwt + defaults', () => {
|
||||
expect(adminCtl(svc({ rotateJwtSecret: vi.fn().mockReturnValue({}) } as Partial<AdminService>)).rotateJwtSecret(user, req)).toEqual({ success: true });
|
||||
});
|
||||
|
||||
it('default-user-settings: sets + audits', () => {
|
||||
const setAdminUserDefaults = vi.fn();
|
||||
const c = adminCtl(svc({ setAdminUserDefaults, getAdminUserDefaults: vi.fn().mockReturnValue({ theme: 'dark' }) } as Partial<AdminService>));
|
||||
expect(c.setDefaultUserSettings(user, { theme: 'dark' }, req)).toEqual({ theme: 'dark' });
|
||||
expect(setAdminUserDefaults).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('AdminController error envelope fallbacks', () => {
|
||||
@@ -151,14 +141,7 @@ describe('AdminController error envelope fallbacks', () => {
|
||||
expect(thrown(() => adminCtl(svc({ createUser: vi.fn().mockReturnValue({ error: 'boom' }) } as Partial<AdminService>)).createUser(user, {}, req))).toEqual({ status: 400, body: { error: 'boom' } });
|
||||
});
|
||||
|
||||
it('updateOidc defaults to 400 when the service error omits a status', () => {
|
||||
expect(thrown(() => adminCtl(svc({ updateOidcSettings: vi.fn().mockReturnValue({ error: 'nope' }) } as Partial<AdminService>)).updateOidc(user, {}, req))).toEqual({ status: 400, body: { error: 'nope' } });
|
||||
});
|
||||
|
||||
it('updateOidc audits issuer_set=false when no issuer is supplied', () => {
|
||||
expect(adminCtl(svc({ updateOidcSettings: vi.fn().mockReturnValue({}) } as Partial<AdminService>)).updateOidc(user, {}, req)).toEqual({ success: true });
|
||||
expect(writeAudit).toHaveBeenCalledWith(expect.objectContaining({ action: 'admin.oidc_update', details: { issuer_set: false } }));
|
||||
});
|
||||
});
|
||||
|
||||
describe('AdminController read-only getters', () => {
|
||||
@@ -167,22 +150,13 @@ describe('AdminController read-only getters', () => {
|
||||
expect(adminCtl(svc({ getStats: vi.fn().mockReturnValue({ users: 3 }) } as Partial<AdminService>)).stats()).toEqual({ users: 3 });
|
||||
expect(adminCtl(svc({ getPermissions: vi.fn().mockReturnValue({ a: 1 }) } as Partial<AdminService>)).permissions()).toEqual({ a: 1 });
|
||||
expect(adminCtl(svc({ getAuditLog: vi.fn().mockReturnValue({ entries: [] }) } as Partial<AdminService>)).auditLog({})).toEqual({ entries: [] });
|
||||
expect(adminCtl(svc({ getOidcSettings: vi.fn().mockReturnValue({ issuer: 'x' }) } as Partial<AdminService>)).getOidc()).toEqual({ issuer: 'x' });
|
||||
expect(adminCtl(svc({ checkVersion: vi.fn().mockResolvedValue({ current: '1' }) } as Partial<AdminService>)).versionCheck()).resolves.toEqual({ current: '1' });
|
||||
expect(adminCtl(svc({ getPreferencesMatrix: vi.fn().mockReturnValue({ rows: [] }) } as Partial<AdminService>)).getNotificationPrefs(user)).toEqual({ rows: [] });
|
||||
expect(adminCtl(svc({ listInvites: vi.fn().mockReturnValue([{ id: 1 }]) } as Partial<AdminService>)).listInvites()).toEqual({ invites: [{ id: 1 }] });
|
||||
expect(adminCtl(svc({ listAddons: vi.fn().mockReturnValue([{ id: 'mcp' }]) } as Partial<AdminService>)).listAddons()).toEqual({ addons: [{ id: 'mcp' }] });
|
||||
expect(adminCtl(svc({ listMcpTokens: vi.fn().mockReturnValue([{ id: 1 }]) } as Partial<AdminService>)).listMcpTokens()).toEqual({ tokens: [{ id: 1 }] });
|
||||
expect(adminCtl(svc({ listOAuthSessions: vi.fn().mockReturnValue([{ id: 1 }]) } as Partial<AdminService>)).listOAuthSessions()).toEqual({ sessions: [{ id: 1 }] });
|
||||
expect(adminCtl(svc({ getAdminUserDefaults: vi.fn().mockReturnValue({ theme: 'dark' }) } as Partial<AdminService>)).getDefaultUserSettings()).toEqual({ theme: 'dark' });
|
||||
});
|
||||
|
||||
it('setNotificationPrefs persists then returns the refreshed matrix', () => {
|
||||
const setAdminPreferences = vi.fn();
|
||||
const c = adminCtl(svc({ setAdminPreferences, getPreferencesMatrix: vi.fn().mockReturnValue({ rows: [1] }) } as Partial<AdminService>));
|
||||
expect(c.setNotificationPrefs(user, { x: 1 })).toEqual({ rows: [1] });
|
||||
expect(setAdminPreferences).toHaveBeenCalledWith(user.id, { x: 1 });
|
||||
});
|
||||
|
||||
it('githubReleases falls back to default paging when no query is given', async () => {
|
||||
const getGithubReleases = vi.fn().mockResolvedValue([{ tag: 'v1' }]);
|
||||
@@ -204,18 +178,6 @@ describe('AdminController tokens + sessions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('AdminController default-user-settings error path', () => {
|
||||
it('400 with an Error message when setAdminUserDefaults throws an Error', () => {
|
||||
const c = adminCtl(svc({ setAdminUserDefaults: vi.fn(() => { throw new Error('bad default'); }) } as Partial<AdminService>));
|
||||
expect(thrown(() => c.setDefaultUserSettings(user, { theme: 'x' }, req))).toEqual({ status: 400, body: { error: 'bad default' } });
|
||||
});
|
||||
|
||||
it('400 stringifies a non-Error throw', () => {
|
||||
const c = adminCtl(svc({ setAdminUserDefaults: vi.fn(() => { throw 'plain string'; }) } as Partial<AdminService>));
|
||||
expect(thrown(() => c.setDefaultUserSettings(user, { theme: 'x' }, req))).toEqual({ status: 400, body: { error: 'plain string' } });
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('AdminController dev test-notification', () => {
|
||||
it('404 outside development', async () => {
|
||||
|
||||
Reference in New Issue
Block a user