合并主分支

合并画布素材导出主线,保留钱包与充值修复。
This commit is contained in:
2026-08-06 13:15:54 +08:00
9 changed files with 413 additions and 12 deletions
@@ -298,6 +298,78 @@ describe('ImageCanvasExportModel', () => {
}
});
it('falls back when OSS returns 206 but its response body fails during export', async () => {
const originalFetch = globalThis.fetch;
const signedResponseBlob = vi
.fn()
.mockRejectedValue(new TypeError('Failed to fetch'));
const fetchMock = vi.fn(async (url: string) => {
if (url.startsWith('/api/assets/read-url?')) {
return new Response(
JSON.stringify({
ok: true,
data: {
read: {
objectKey: 'generated/export-body-failure.png',
signedUrl: 'https://signed.example.com/export-body-failure.png',
expiresAt: '2099-01-01T00:10:00Z',
},
},
error: null,
meta: {
apiVersion: '2026-06-16',
routeVersion: '2026-06-16',
latencyMs: 1,
timestamp: '2099-01-01T00:00:00Z',
},
}),
{
headers: {
'Content-Type': 'application/json',
},
},
);
}
if (url === 'https://signed.example.com/export-body-failure.png') {
return {
ok: true,
status: 206,
statusText: 'Partial Content',
headers: new Headers({
'Content-Range': 'bytes 0-2/3',
'Content-Type': 'image/png',
}),
blob: signedResponseBlob,
} as unknown as Response;
}
if (url.startsWith('/api/assets/read-bytes?')) {
return new Response(new Blob(['fallback'], { type: 'image/png' }));
}
return new Response(null, { status: 404 });
});
globalThis.fetch = fetchMock as typeof fetch;
try {
const blob = await readLayerAssetBlob(
buildLayer({
src: '/generated-editor-images/export-body-failure.png',
objectKey: 'generated/export-body-failure.png',
}),
);
expect(signedResponseBlob).toHaveBeenCalledTimes(1);
await expect(blob.text()).resolves.toBe('fallback');
expect(fetchMock).toHaveBeenCalledWith(
expect.stringContaining(
'/api/assets/read-bytes?objectKey=generated%2Fexport-body-failure.png',
),
expect.any(Object),
);
} finally {
globalThis.fetch = originalFetch;
}
});
it('reads private image-sequence frame object keys through same-origin bytes', async () => {
const originalFetch = globalThis.fetch;
const fetchMock = vi.fn(async (url: string) => {