99 lines
4.0 KiB
TypeScript
99 lines
4.0 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
import {
|
|
deleteOIDCBrowserSession,
|
|
GatewayApiError,
|
|
gatewayErrorMessage,
|
|
getCurrentUser,
|
|
getOpsManagementSkillMetadata,
|
|
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_EXPIRED', '统一认证会话已过期,请重新登录'],
|
|
['OIDC_SESSION_REFRESH_UNAVAILABLE', '认证中心暂时不可用,请稍后重试'],
|
|
['OIDC_SESSION_STORE_UNAVAILABLE', '登录会话存储暂时不可用,请稍后重试'],
|
|
['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('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);
|
|
});
|
|
});
|
|
|
|
describe('Public Agent resources', () => {
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it('loads operations skill metadata without authorization', async () => {
|
|
const metadata = {
|
|
name: 'ai-gateway-ops-management',
|
|
version: '1.0.1',
|
|
displayName: 'AI Gateway 运维管理',
|
|
modules: ['model-runtime'],
|
|
fileName: 'ai-gateway-ops-management-v1.0.1.zip',
|
|
downloadPath: '/api/v1/public/skills/ai-gateway-ops-management/download',
|
|
apiDocsJsonPath: '/api-docs-json',
|
|
apiDocsYamlPath: '/api-docs-yaml',
|
|
};
|
|
const fetchMock = vi.fn().mockResolvedValue(new Response(JSON.stringify(metadata), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}));
|
|
vi.stubGlobal('fetch', fetchMock);
|
|
|
|
await expect(getOpsManagementSkillMetadata()).resolves.toEqual(metadata);
|
|
|
|
const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit];
|
|
expect(url).toContain('/api/v1/public/skills/ai-gateway-ops-management/metadata');
|
|
expect(new Headers(init.headers).has('Authorization')).toBe(false);
|
|
});
|
|
});
|