Files
metapi/src/web/api.test.ts
T

158 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { api, type ProxyTestRequestEnvelope } from './api.js';
import { persistAuthSession } from './authSession.js';
function createMemoryStorage() {
const store = new Map<string, string>();
return {
getItem(key: string) {
return store.has(key) ? store.get(key)! : null;
},
setItem(key: string, value: string) {
store.set(key, value);
},
removeItem(key: string) {
store.delete(key);
},
};
}
function installPendingFetch() {
const fetchMock = vi.fn((_input: RequestInfo | URL, init?: RequestInit) => new Promise<Response>((_resolve, reject) => {
const signal = init?.signal;
if (!signal) return;
if (signal.aborted) {
reject(new DOMException('Aborted', 'AbortError'));
return;
}
signal.addEventListener('abort', () => reject(new DOMException('Aborted', 'AbortError')), { once: true });
}));
vi.stubGlobal('fetch', fetchMock);
return fetchMock;
}
describe('api proxy test timeout handling', () => {
beforeEach(() => {
vi.useFakeTimers();
vi.stubGlobal('localStorage', createMemoryStorage());
persistAuthSession(globalThis.localStorage as Storage, 'token-1');
});
afterEach(() => {
vi.useRealTimers();
vi.unstubAllGlobals();
vi.restoreAllMocks();
});
it('keeps image generation proxy tests alive past the default 30 second timeout', async () => {
installPendingFetch();
const payload: ProxyTestRequestEnvelope = {
method: 'POST',
path: '/v1/images/generations',
requestKind: 'json',
jsonBody: {
model: 'gemini-imagen',
prompt: 'banana cat',
},
};
let settled = false;
const promise = api.proxyTest(payload);
const handled = promise
.then(() => ({ ok: true as const }))
.catch((error: Error) => ({ ok: false as const, error }))
.finally(() => {
settled = true;
});
await vi.advanceTimersByTimeAsync(30_000);
expect(settled).toBe(false);
await vi.advanceTimersByTimeAsync(120_000);
const result = await handled;
expect(result.ok).toBe(false);
if (result.ok) {
throw new Error('Expected image generation proxy test to time out');
}
expect(result.error.message).toBe('请求超时(150s)');
});
it('still uses the default 30 second timeout for generic proxy tests', async () => {
installPendingFetch();
const payload: ProxyTestRequestEnvelope = {
method: 'POST',
path: '/v1/embeddings',
requestKind: 'json',
jsonBody: {
model: 'text-embedding-3-small',
input: 'hello',
},
};
const promise = api.proxyTest(payload).catch((error: Error) => error);
await vi.advanceTimersByTimeAsync(30_000);
await expect(promise).resolves.toMatchObject({ message: '请求超时(30s)' });
});
it('times out replay hydration file-content fetches after 30 seconds', async () => {
installPendingFetch();
const getProxyFileContentDataUrl = (api as Record<string, any>).getProxyFileContentDataUrl;
let settled = false;
const handled = getProxyFileContentDataUrl?.('file-metapi-123')
.then(() => ({ ok: true as const }))
.catch((error: Error) => ({ ok: false as const, error }))
.finally(() => {
settled = true;
});
await vi.advanceTimersByTimeAsync(30_000);
expect(settled).toBe(true);
const result = await handled;
expect(result.ok).toBe(false);
if (result.ok) {
throw new Error('Expected replay hydration file-content fetch to time out');
}
expect(result.error.message).toBe('请求超时(30s)');
});
it('loads proxy file content as a data URL for replay hydration', async () => {
const fetchMock = vi.fn().mockResolvedValue(new Response(
new Blob([Buffer.from('PDF')], { type: 'application/pdf' }),
{
status: 200,
headers: {
'content-type': 'application/pdf',
'content-disposition': 'inline; filename="brief.pdf"',
},
},
));
vi.stubGlobal('fetch', fetchMock);
const getProxyFileContentDataUrl = (api as Record<string, any>).getProxyFileContentDataUrl;
const result = await getProxyFileContentDataUrl?.('file-metapi-123');
expect(fetchMock).toHaveBeenCalledWith('/v1/files/file-metapi-123/content', expect.objectContaining({
method: 'GET',
headers: expect.objectContaining({
Authorization: 'Bearer token-1',
}),
}));
expect(result).toEqual({
filename: 'brief.pdf',
mimeType: 'application/pdf',
data: 'data:application/pdf;base64,UERG',
});
});
it('reuses the same proxy test implementations for legacy aliases', () => {
expect(api.proxyTest).toBe(api.testProxy);
expect(api.proxyTestStream).toBe(api.testProxyStream);
});
});