Files
BoosAPI/src/server/routes/proxy/videos.test.ts
T
cita 23922a5965 feat: Add normalized types and utility functions for handling chat formats
- Introduced `NormalizedContentBlock`, `NormalizedUsage`, and related types for structured data handling.
- Implemented utility functions for creating and merging normalized usage statistics.
- Added a new module for shared transformers to manage chat formats.

feat: Implement app local state management

- Created functions to clear application installation state and manage local storage keys.
- Added constants for theme and user profile storage keys.

test: Add tests for BrandIcon component rendering

- Implemented tests to ensure BrandIcon does not force a colored background for recognized brands.

test: Enhance ModelAnalysisPanel tests

- Added tests for rendering token summaries with compact units in ModelAnalysisPanel.

test: Add tests for ModernSelect component

- Implemented tests to verify icon rendering for selected options in ModernSelect.

test: Add number formatting tests

- Created tests for `formatCompactTokenMetric` to validate token formatting logic.

feat: Implement account creation intent handling

- Added logic to handle session and API key creation intents in the Accounts page.

test: Add tests for dashboard performance card

- Implemented tests to verify rendering of performance metrics in the dashboard.

feat: Implement default connection segment resolution

- Added logic to determine initial connection segments based on platform types.

test: Add tests for settings factory reset functionality

- Implemented tests to verify the factory reset process and local state clearing.

test: Add tests for site creation redirect logic

- Implemented tests to verify redirection based on site capabilities during creation.

test: Add tests for token routes refresh decision action

- Implemented tests to verify refresh decision actions in TokenRoutes.

test: Add tests for token routes snapshot load

- Implemented tests to verify loading of cached snapshot data in TokenRoutes.
2026-03-08 12:08:57 +08:00

249 lines
7.9 KiB
TypeScript

import Fastify, { type FastifyInstance } from 'fastify';
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
const fetchMock = vi.fn();
const selectChannelMock = vi.fn();
const selectNextChannelMock = vi.fn();
const recordSuccessMock = vi.fn();
const recordFailureMock = vi.fn();
const refreshModelsAndRebuildRoutesMock = vi.fn();
const reportProxyAllFailedMock = vi.fn();
const reportTokenExpiredMock = vi.fn();
const estimateProxyCostMock = vi.fn(async () => 0);
const saveProxyVideoTaskMock = vi.fn();
const getProxyVideoTaskByPublicIdMock = vi.fn();
const deleteProxyVideoTaskByPublicIdMock = vi.fn();
const refreshProxyVideoTaskSnapshotMock = vi.fn();
vi.mock('undici', async () => {
const actual = await vi.importActual<typeof import('undici')>('undici');
return {
...actual,
fetch: (...args: unknown[]) => fetchMock(...args),
};
});
vi.mock('../../services/tokenRouter.js', () => ({
tokenRouter: {
selectChannel: (...args: unknown[]) => selectChannelMock(...args),
selectNextChannel: (...args: unknown[]) => selectNextChannelMock(...args),
recordSuccess: (...args: unknown[]) => recordSuccessMock(...args),
recordFailure: (...args: unknown[]) => recordFailureMock(...args),
},
}));
vi.mock('../../services/modelService.js', () => ({
refreshModelsAndRebuildRoutes: (...args: unknown[]) => refreshModelsAndRebuildRoutesMock(...args),
}));
vi.mock('../../services/alertService.js', () => ({
reportProxyAllFailed: (...args: unknown[]) => reportProxyAllFailedMock(...args),
reportTokenExpired: (...args: unknown[]) => reportTokenExpiredMock(...args),
}));
vi.mock('../../services/alertRules.js', () => ({
isTokenExpiredError: () => false,
}));
vi.mock('../../services/modelPricingService.js', () => ({
estimateProxyCost: (arg: any) => estimateProxyCostMock(arg),
}));
vi.mock('../../services/proxyRetryPolicy.js', () => ({
shouldRetryProxyRequest: () => false,
}));
vi.mock('../../services/proxyVideoTaskStore.js', () => ({
saveProxyVideoTask: (...args: unknown[]) => saveProxyVideoTaskMock(...args),
getProxyVideoTaskByPublicId: (...args: unknown[]) => getProxyVideoTaskByPublicIdMock(...args),
deleteProxyVideoTaskByPublicId: (...args: unknown[]) => deleteProxyVideoTaskByPublicIdMock(...args),
refreshProxyVideoTaskSnapshot: (...args: unknown[]) => refreshProxyVideoTaskSnapshotMock(...args),
}));
describe('/v1/videos routes', () => {
let app: FastifyInstance;
const buildMultipartBody = (boundary: string) => Buffer.from(
`--${boundary}\r\n`
+ `Content-Disposition: form-data; name="model"\r\n\r\n`
+ `sora-2\r\n`
+ `--${boundary}\r\n`
+ `Content-Disposition: form-data; name="prompt"\r\n\r\n`
+ `a cat walking\r\n`
+ `--${boundary}\r\n`
+ `Content-Disposition: form-data; name="input_reference"; filename="cat.png"\r\n`
+ `Content-Type: image/png\r\n\r\n`
+ `pngdata\r\n`
+ `--${boundary}--\r\n`,
);
beforeAll(async () => {
const { videosProxyRoute } = await import('./videos.js');
app = Fastify();
await app.register(videosProxyRoute);
});
beforeEach(() => {
fetchMock.mockReset();
selectChannelMock.mockReset();
selectNextChannelMock.mockReset();
recordSuccessMock.mockReset();
recordFailureMock.mockReset();
refreshModelsAndRebuildRoutesMock.mockReset();
reportProxyAllFailedMock.mockReset();
reportTokenExpiredMock.mockReset();
estimateProxyCostMock.mockClear();
saveProxyVideoTaskMock.mockReset();
getProxyVideoTaskByPublicIdMock.mockReset();
deleteProxyVideoTaskByPublicIdMock.mockReset();
refreshProxyVideoTaskSnapshotMock.mockReset();
selectChannelMock.mockReturnValue({
channel: { id: 11, routeId: 22 },
site: { id: 44, name: 'demo-site', url: 'https://upstream.example.com', platform: 'openai' },
account: { id: 33, username: 'demo-user' },
tokenName: 'default',
tokenValue: 'sk-demo',
actualModel: 'sora-2',
});
selectNextChannelMock.mockReturnValue(null);
});
afterAll(async () => {
await app.close();
});
it('creates an upstream video task and stores a local public id mapping', async () => {
saveProxyVideoTaskMock.mockResolvedValue({
publicId: 'vid_local_123',
upstreamVideoId: 'vid_upstream_123',
});
fetchMock.mockResolvedValue(new Response(JSON.stringify({
id: 'vid_upstream_123',
object: 'video',
status: 'queued',
model: 'sora-2',
prompt: 'a cat walking',
}), {
status: 200,
headers: { 'content-type': 'application/json' },
}));
const response = await app.inject({
method: 'POST',
url: '/v1/videos',
payload: {
model: 'sora-2',
prompt: 'a cat walking',
},
});
expect(response.statusCode).toBe(200);
expect(saveProxyVideoTaskMock).toHaveBeenCalledWith(expect.objectContaining({
upstreamVideoId: 'vid_upstream_123',
requestedModel: 'sora-2',
actualModel: 'sora-2',
lastUpstreamStatus: 200,
statusSnapshot: expect.objectContaining({
id: 'vid_upstream_123',
status: 'queued',
}),
}));
expect(response.json()).toMatchObject({
id: 'vid_local_123',
object: 'video',
status: 'queued',
});
});
it('accepts multipart video create requests', async () => {
saveProxyVideoTaskMock.mockResolvedValue({
publicId: 'vid_local_456',
upstreamVideoId: 'vid_upstream_456',
});
fetchMock.mockResolvedValue(new Response(JSON.stringify({
id: 'vid_upstream_456',
object: 'video',
status: 'queued',
model: 'sora-2',
}), {
status: 200,
headers: { 'content-type': 'application/json' },
}));
const boundary = 'metapi-video-boundary';
const response = await app.inject({
method: 'POST',
url: '/v1/videos',
headers: {
'content-type': `multipart/form-data; boundary=${boundary}`,
},
payload: buildMultipartBody(boundary),
});
expect(response.statusCode).toBe(200);
expect(response.json()).toMatchObject({
id: 'vid_local_456',
object: 'video',
status: 'queued',
});
});
it('resolves local video ids back to the upstream task on GET', async () => {
getProxyVideoTaskByPublicIdMock.mockResolvedValue({
publicId: 'vid_local_123',
upstreamVideoId: 'vid_upstream_123',
siteUrl: 'https://upstream.example.com',
tokenValue: 'sk-demo',
});
fetchMock.mockResolvedValue(new Response(JSON.stringify({
id: 'vid_upstream_123',
object: 'video',
status: 'running',
model: 'sora-2',
}), {
status: 200,
headers: { 'content-type': 'application/json' },
}));
const response = await app.inject({
method: 'GET',
url: '/v1/videos/vid_local_123',
});
expect(response.statusCode).toBe(200);
const [targetUrl] = fetchMock.mock.calls[0] as [string, RequestInit];
expect(targetUrl).toBe('https://upstream.example.com/v1/videos/vid_upstream_123');
expect(refreshProxyVideoTaskSnapshotMock).toHaveBeenCalledWith('vid_local_123', expect.objectContaining({
lastUpstreamStatus: 200,
statusSnapshot: expect.objectContaining({
id: 'vid_upstream_123',
status: 'running',
}),
}));
expect(response.json()).toMatchObject({
id: 'vid_local_123',
object: 'video',
status: 'running',
});
});
it('deletes the upstream task and local mapping on DELETE', async () => {
getProxyVideoTaskByPublicIdMock.mockResolvedValue({
publicId: 'vid_local_123',
upstreamVideoId: 'vid_upstream_123',
siteUrl: 'https://upstream.example.com',
tokenValue: 'sk-demo',
});
fetchMock.mockResolvedValue(new Response(null, { status: 204 }));
const response = await app.inject({
method: 'DELETE',
url: '/v1/videos/vid_local_123',
});
expect(response.statusCode).toBe(204);
expect(deleteProxyVideoTaskByPublicIdMock).toHaveBeenCalledWith('vid_local_123');
});
});