import { afterEach, describe, expect, it, vi } from 'vitest'; import { createOIDCBrowserSession, deleteOIDCBrowserSession, GatewayApiError, gatewayErrorMessage, getCurrentUser, OIDC_BROWSER_SESSION_CREDENTIAL, } from './api'; describe('Gateway provisioning errors', () => { const cases = [ ['GATEWAY_USER_NOT_PROVISIONED', '该账号尚未开通 EasyAI Gateway'], ['GATEWAY_USER_DISABLED', '该 Gateway 账号已停用,请联系管理员'], ['GATEWAY_TENANT_UNAVAILABLE', 'Gateway 租户尚未就绪,请联系管理员'], ['GATEWAY_USER_PROVISIONING_FAILED', 'Gateway 账号初始化失败,请稍后重试'], ['OIDC_BROWSER_SESSION_DISABLED', 'Gateway 浏览器会话尚未启用'], ['OIDC_SESSION_INVALID', '统一认证会话无效,请重新登录'], ['OIDC_SESSION_TOKEN_TOO_LARGE', '统一认证凭证过大,无法建立浏览器会话'], ['OIDC_SESSION_CSRF_REJECTED', '登录会话来源校验失败,请刷新后重试'], ] as const; for (const [code, expected] of cases) { it(`maps ${code} to a stable Chinese status`, () => { expect(gatewayErrorMessage({ code, message: 'internal server message', status: 503 })).toBe(expected); expect(new GatewayApiError({ code, message: 'internal server message', status: 503 }).message).toContain(expected); expect(new GatewayApiError({ code, message: 'internal server message', status: 503 }).message).not.toContain('internal server message'); }); } it('keeps the server message for unrelated errors', () => { expect(gatewayErrorMessage({ code: 'OTHER_ERROR', message: '模型不可用', status: 503 })).toBe('模型不可用'); }); }); describe('OIDC browser session transport', () => { afterEach(() => { vi.unstubAllGlobals(); }); it('exchanges the in-memory access token for a credentialed HttpOnly session', async () => { const fetchMock = vi.fn().mockResolvedValue(new Response(null, { status: 204 })); vi.stubGlobal('fetch', fetchMock); await createOIDCBrowserSession('auth-center-access-token'); expect(fetchMock).toHaveBeenCalledOnce(); const [, init] = fetchMock.mock.calls[0] as [string, RequestInit]; expect(init.credentials).toBe('include'); expect(new Headers(init.headers).get('Authorization')).toBe('Bearer auth-center-access-token'); expect(init.body).toBeUndefined(); }); it('uses the shared cookie without sending a synthetic bearer token', async () => { const fetchMock = vi.fn().mockResolvedValue(new Response(JSON.stringify({ sub: 'oidc-user' }), { status: 200, headers: { 'Content-Type': 'application/json' }, })); vi.stubGlobal('fetch', fetchMock); await getCurrentUser(OIDC_BROWSER_SESSION_CREDENTIAL); const [, init] = fetchMock.mock.calls[0] as [string, RequestInit]; expect(init.credentials).toBe('include'); expect(new Headers(init.headers).has('Authorization')).toBe(false); }); it('deletes the shared cookie with a credentialed request', async () => { const fetchMock = vi.fn().mockResolvedValue(new Response(null, { status: 204 })); vi.stubGlobal('fetch', fetchMock); await deleteOIDCBrowserSession(); const [, init] = fetchMock.mock.calls[0] as [string, RequestInit]; expect(init.method).toBe('DELETE'); expect(init.credentials).toBe('include'); expect(new Headers(init.headers).has('Authorization')).toBe(false); }); });