Files
Agent-System/web/src/api/authUserMap.test.ts
T
weichenandCursor c8dbefe8a1 fix(web): align settings CAS, PDF preview, and collaboration UX guards
Wire expected_revision, blob PDF framing, mustChangePassword session rewrite,
client_key project create, no-project send disable, and right-rail persistence.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-20 07:05:06 +00:00

161 lines
5.1 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { mapLoginResult, mapMeResult, mapUserAccount, mapUserAccountList } from './authUserMap';
import { AuthClient } from './auth';
const snakeAdminUser = {
id: 1,
username: 'admin',
name: '系统管理员',
status: 'active',
must_change_password: false,
role_id: 'role-admin',
position: '系统管理员',
employee_id: 'SYS-000',
department: '系统运维部',
created_at: '2026-09-13T00:00:00Z',
updated_at: '2026-09-13T00:00:00Z',
};
test('mapUserAccount: snake_case Go contract → camelCase', () => {
const u = mapUserAccount(snakeAdminUser);
assert.equal(u.roleId, 'role-admin');
assert.equal(u.employeeId, 'SYS-000');
assert.equal(u.mustChangePassword, false);
assert.equal(u.createdAt, '2026-09-13T00:00:00Z');
assert.equal(u.updatedAt, '2026-09-13T00:00:00Z');
assert.equal(u.username, 'admin');
});
test('mapUserAccount: prefers snake_case when both present', () => {
const u = mapUserAccount({
...snakeAdminUser,
roleId: 'role-guest',
role_id: 'role-admin',
employeeId: 'WRONG',
employee_id: 'SYS-000',
});
assert.equal(u.roleId, 'role-admin');
assert.equal(u.employeeId, 'SYS-000');
});
test('mapLoginResult/mapMeResult use top-level must_change_password as sole authority', () => {
// Nested true must not revive when top-level is false (post password-change cache).
const me = mapMeResult({
user: { ...snakeAdminUser, must_change_password: true },
must_change_password: false,
env: 'dev',
capabilities: [],
});
assert.equal(me.mustChangePassword, false);
assert.equal(me.user.mustChangePassword, false);
const login = mapLoginResult({
token: 'tok-2',
user: { ...snakeAdminUser, must_change_password: true },
must_change_password: false,
env: 'dev',
});
assert.equal(login.mustChangePassword, false);
assert.equal(login.user.mustChangePassword, false);
});
test('mapUserAccountList maps admin users list', () => {
const list = mapUserAccountList([
snakeAdminUser,
{
id: 2,
username: 'eng',
name: '工程师',
status: 'active',
must_change_password: true,
role_id: 'role-engineer',
position: '助理工程师',
employee_id: 'RD-010',
department: '研发',
created_at: '2026-09-13T01:00:00Z',
updated_at: '2026-09-13T01:00:00Z',
},
]);
assert.equal(list.length, 2);
assert.equal(list[0].roleId, 'role-admin');
assert.equal(list[1].roleId, 'role-engineer');
assert.equal(list[1].mustChangePassword, true);
assert.equal(list[1].employeeId, 'RD-010');
});
test('AuthClient.login + fetchMe map real snake_case wire payloads', async () => {
const originalFetch = globalThis.fetch;
const originalWindow = (globalThis as { window?: unknown }).window;
const store: Record<string, string> = {};
const localStorage = {
getItem: (k: string) => store[k] ?? null,
setItem: (k: string, v: string) => {
store[k] = v;
},
removeItem: (k: string) => {
delete store[k];
},
};
const mockFetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
const url = String(input);
if (url.includes('/api/v1/auth/login')) {
return new Response(
JSON.stringify({
token: 'wire-token',
user: snakeAdminUser,
must_change_password: false,
env: 'dev',
}),
{ status: 200, headers: { 'Content-Type': 'application/json' } },
);
}
if (url.includes('/api/v1/auth/me')) {
return new Response(
JSON.stringify({
user: snakeAdminUser,
must_change_password: false,
env: 'dev',
capabilities: ['admin:manage_users'],
}),
{ status: 200, headers: { 'Content-Type': 'application/json' } },
);
}
if (url.includes('/api/v1/admin/users') && (!init?.method || init.method === 'GET')) {
return new Response(JSON.stringify({ users: [snakeAdminUser] }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
return new Response('not found', { status: 404 });
}) as typeof fetch;
globalThis.fetch = mockFetch;
(globalThis as { window: unknown }).window = { fetch: mockFetch, localStorage };
try {
const client = new AuthClient();
const session = await client.login({ username: 'admin', password: 'x' });
assert.equal(session.user.roleId, 'role-admin');
assert.equal(session.user.employeeId, 'SYS-000');
assert.equal(session.mustChangePassword, false);
assert.ok(session.capabilities?.includes('admin:manage_users'));
const me = await client.fetchMe();
assert.equal(me.user.roleId, 'role-admin');
assert.equal(me.user.employeeId, 'SYS-000');
assert.equal(me.mustChangePassword, false);
const users = await client.getAdminUsers();
assert.equal(users[0].roleId, 'role-admin');
assert.equal(users[0].employeeId, 'SYS-000');
} finally {
globalThis.fetch = originalFetch;
if (originalWindow === undefined) {
delete (globalThis as { window?: unknown }).window;
} else {
(globalThis as { window: unknown }).window = originalWindow;
}
}
});