Files
electric-cherry/tests/client/user-audit.test.ts
T
2026-10-17 09:54:01 -04:00

96 lines
3.6 KiB
TypeScript

import { afterEach, expect, it, vi } from 'vitest';
import { flushSync, mount, unmount } from 'svelte';
import { writable } from 'svelte/store';
import UsersPage from '../../src/routes/settings/users/+page.svelte';
const page = writable({
data: { user: { id: 'admin', username: 'administrator', role: 'admin' } }
});
vi.mock('$app/stores', () => ({
page: { subscribe: (run: Parameters<typeof page.subscribe>[0]) => page.subscribe(run) }
}));
vi.mock('$app/navigation', () => ({ invalidateAll: vi.fn() }));
let component: ReturnType<typeof mount> | undefined;
afterEach(async () => {
if (component) await unmount(component);
component = undefined;
document.body.replaceChildren();
vi.unstubAllGlobals();
});
it('describes requests without presenting routes as accounts and preserves their diagnostic evidence', async () => {
const events = [
{
action: 'request',
targetId: '/api/configs/inspect',
details: JSON.stringify({ method: 'POST', status: 200, path: '/api/configs/inspect' })
},
{
action: 'request',
targetId: '/api/servers/[name]',
details: JSON.stringify({ method: 'DELETE', status: 202, path: '/api/servers/demo' })
},
{ action: 'request', targetId: '/api/provisioning', details: '{old malformed details' },
{ action: 'request', targetId: '/api/future/action', details: 'null' },
{ action: 'login', targetId: 'admin', details: '{}' }
].map((event, index) => ({
...event,
id: index + 1,
actorId: 'admin',
actor: 'administrator',
createdAt: Date.now()
}));
vi.stubGlobal(
'fetch',
vi.fn(async (path: string) =>
Response.json(
path.endsWith('/audit')
? { events }
: {
users: [
{
id: 'admin',
username: 'administrator',
displayName: 'Administrator',
role: 'admin',
disabled: false,
mustChangePassword: false,
lastLoginAt: null
}
]
}
)
)
);
component = mount(UsersPage, { target: document.body });
flushSync();
await vi.waitFor(() => expect(document.querySelectorAll('tbody tr')).toHaveLength(5));
const rows = Array.from(document.querySelectorAll<HTMLTableRowElement>('tbody tr'));
expect(rows.map((row) => row.cells[1].textContent)).toEqual([
'Action requested',
'Action requested',
'Action requested',
'Action requested',
'Signed in'
]);
expect(rows.map((row) => row.cells[3].textContent)).toEqual([
'Inspect config',
'Remove server',
'Manage server operation',
'Other action',
'administrator'
]);
expect(document.querySelector('thead')?.textContent).toContain('Account or action');
expect(rows[1].querySelector('details')?.open).toBe(false);
expect(rows[1].querySelector('summary')?.textContent).toBe('Technical details');
expect(JSON.parse(rows[1].querySelector('pre')!.textContent!)).toEqual({
method: 'DELETE',
status: 202,
path: '/api/servers/demo',
routeId: '/api/servers/[name]'
});
expect(rows[2].querySelector('pre')?.textContent).toContain('{old malformed details');
expect(rows[3].querySelector('pre')?.textContent).toContain('/api/future/action');
});