9ad2152816
新增图片编辑器请求重试配置 生成请求遇到429等临时错误自动重试 上传凭证、资产确认和直传上传共用重试策略 补充图片编辑器重试参数和直传429测试
153 lines
4.7 KiB
TypeScript
153 lines
4.7 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { requestJson } from '../apiClient';
|
|
import { getSignedAssetReadUrl } from '../assetReadUrlService';
|
|
import { uploadEditorSeedanceReferenceFile } from './editorReferenceUploadClient';
|
|
|
|
vi.mock('../apiClient', () => ({
|
|
requestJson: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../assetReadUrlService', () => ({
|
|
getSignedAssetReadUrl: vi.fn(),
|
|
}));
|
|
|
|
const requestJsonMock = vi.mocked(requestJson);
|
|
const getSignedAssetReadUrlMock = vi.mocked(getSignedAssetReadUrl);
|
|
const editorRetryOptionsExpectation = expect.objectContaining({
|
|
maxRetries: 2,
|
|
retryUnsafeMethods: true,
|
|
retryableStatusCodes: expect.arrayContaining([429]),
|
|
});
|
|
|
|
describe('editorReferenceUploadClient', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.spyOn(Date, 'now').mockReturnValue(1771400000000);
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(async () => new Response(null, { status: 200 })),
|
|
);
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it('uploads Seedance reference videos through direct OSS upload and returns object metadata', async () => {
|
|
requestJsonMock
|
|
.mockResolvedValueOnce({
|
|
upload: {
|
|
bucket: 'bucket',
|
|
host: 'https://oss.example.com',
|
|
objectKey:
|
|
'generated-character-drafts/editor/seedance-references/video/ref.mp4',
|
|
legacyPublicPath:
|
|
'/generated-character-drafts/editor/seedance-references/video/ref.mp4',
|
|
formFields: {
|
|
key: 'generated-character-drafts/editor/seedance-references/video/ref.mp4',
|
|
policy: 'policy',
|
|
'x-oss-signature': 'signature',
|
|
},
|
|
},
|
|
})
|
|
.mockResolvedValueOnce({
|
|
assetObject: {
|
|
assetObjectId: 'assetobj-video',
|
|
objectKey:
|
|
'generated-character-drafts/editor/seedance-references/video/ref.mp4',
|
|
assetKind: 'editor_seedance_reference_video',
|
|
},
|
|
});
|
|
getSignedAssetReadUrlMock.mockResolvedValueOnce(
|
|
'https://signed.example.com/ref.mp4',
|
|
);
|
|
|
|
const result = await uploadEditorSeedanceReferenceFile(
|
|
new File(['video'], 'ref.mp4', { type: 'video/mp4' }),
|
|
'video',
|
|
);
|
|
|
|
expect(requestJsonMock).toHaveBeenNthCalledWith(
|
|
1,
|
|
'/api/assets/direct-upload-tickets',
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: expect.any(String),
|
|
}),
|
|
'创建参考素材上传凭证失败',
|
|
expect.objectContaining({ retry: editorRetryOptionsExpectation }),
|
|
);
|
|
const ticketBody = JSON.parse(
|
|
(requestJsonMock.mock.calls[0]?.[1] as RequestInit).body as string,
|
|
);
|
|
expect(ticketBody).toMatchObject({
|
|
legacyPrefix: 'generated-character-drafts',
|
|
pathSegments: [
|
|
'editor',
|
|
'seedance-references',
|
|
'video',
|
|
'1771400000000',
|
|
],
|
|
fileName: 'ref.mp4',
|
|
contentType: 'video/mp4',
|
|
access: 'private',
|
|
maxSizeBytes: 5,
|
|
successActionStatus: 200,
|
|
metadata: {
|
|
asset_kind: 'editor_seedance_reference_video',
|
|
editor_seedance_reference_type: 'video',
|
|
},
|
|
});
|
|
expect(fetch).toHaveBeenCalledWith(
|
|
'https://oss.example.com',
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
body: expect.any(FormData),
|
|
}),
|
|
);
|
|
expect(requestJsonMock).toHaveBeenNthCalledWith(
|
|
2,
|
|
'/api/assets/objects/confirm',
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: expect.any(String),
|
|
}),
|
|
'确认参考素材资产失败',
|
|
expect.objectContaining({ retry: editorRetryOptionsExpectation }),
|
|
);
|
|
const confirmBody = JSON.parse(
|
|
(requestJsonMock.mock.calls[1]?.[1] as RequestInit).body as string,
|
|
);
|
|
expect(confirmBody).toMatchObject({
|
|
bucket: 'bucket',
|
|
objectKey: 'generated-character-drafts/editor/seedance-references/video/ref.mp4',
|
|
contentType: 'video/mp4',
|
|
contentLength: 5,
|
|
assetKind: 'editor_seedance_reference_video',
|
|
accessPolicy: 'private',
|
|
entityId: 'editor-seedance-reference-video',
|
|
});
|
|
expect(getSignedAssetReadUrlMock).toHaveBeenCalledWith(
|
|
{
|
|
objectKey:
|
|
'generated-character-drafts/editor/seedance-references/video/ref.mp4',
|
|
expireSeconds: 3600,
|
|
},
|
|
undefined,
|
|
{ bypassCache: true },
|
|
);
|
|
expect(result).toEqual({
|
|
src: 'https://signed.example.com/ref.mp4',
|
|
objectKey:
|
|
'generated-character-drafts/editor/seedance-references/video/ref.mp4',
|
|
assetObjectId: 'assetobj-video',
|
|
legacyPublicPath:
|
|
'/generated-character-drafts/editor/seedance-references/video/ref.mp4',
|
|
});
|
|
});
|
|
});
|