Files
geo/apps/desktop-client/src/main/adapters/toutiao-platform.test.ts
T
root a9591143e8 fix(toutiao): fail publish when body images fail to upload
Route content/cover images through the desktop media-image asset
pipeline and abort before submitting when any body image was not
replaced with a Toutiao-hosted URL, so we don't publish articles
that reference our own private asset endpoints.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 20:24:28 +08:00

113 lines
3.2 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest'
import {
publishToutiaoArticle,
type ToutiaoPublishTransport,
} from '../../../../../packages/publisher-platforms/src/toutiao'
function createTransport(
overrides: Partial<ToutiaoPublishTransport> = {},
): ToutiaoPublishTransport & { submitCalls: URLSearchParams[] } {
const submitCalls: URLSearchParams[] = []
const transport: ToutiaoPublishTransport & { submitCalls: URLSearchParams[] } = {
submitCalls,
async fetchJson(input, init) {
if (input.includes('/media/get_media_info')) {
return {
data: {
user: {
id_str: 'uid-1',
screen_name: '头条号测试账号',
},
media: {},
},
} as never
}
if (input.includes('/spice/image')) {
return {
data: {
image_url: 'https://p3-sign.toutiaoimg.com/tos-cn-i-body/image.png',
image_uri: 'tos-cn-i-body/image.png',
image_width: 640,
image_height: 360,
},
} as never
}
if (input.includes('/article/publish')) {
submitCalls.push(init?.body as URLSearchParams)
return {
code: 0,
data: {
pgc_id: 'pgc-1',
},
} as never
}
return {} as never
},
async fetchImageBlob() {
return new Blob(['image'], { type: 'image/png' })
},
async uploadHtmlImages(html, uploader) {
const source = '/api/public/assets/body-token'
const target = await uploader(source)
return {
html: target ? html.split(source).join(target) : html,
uploaded: target ? new Map([[source, target]]) : new Map(),
}
},
...overrides,
}
return transport
}
describe('publishToutiaoArticle', () => {
it('submits content with body images replaced by Toutiao image URLs', async () => {
const transport = createTransport()
const result = await publishToutiaoArticle(
{
title: '标题',
html: '<p><img src="/api/public/assets/body-token" alt="" /></p>',
publishType: 'publish',
},
transport,
)
expect(result.success).toBe(true)
expect(transport.submitCalls).toHaveLength(1)
expect(transport.submitCalls[0]?.get('content')).toContain('https://p3-sign.toutiaoimg.com')
expect(transport.submitCalls[0]?.get('content')).not.toContain('/api/public/assets/body-token')
})
it('fails before submit when any body image cannot be uploaded to Toutiao', async () => {
const transport = createTransport({
async uploadHtmlImages(html) {
return {
html,
uploaded: new Map(),
}
},
})
const fetchJson = vi.spyOn(transport, 'fetchJson')
const result = await publishToutiaoArticle(
{
title: '标题',
html: '<p><img src="/api/public/assets/body-token" alt="" /></p>',
publishType: 'publish',
},
transport,
)
expect(result).toMatchObject({
success: false,
status: 'failed',
code: 'toutiaohao_image_upload_failed',
})
expect(fetchJson.mock.calls.some(([input]) => input.includes('/article/publish'))).toBe(false)
})
})