a9591143e8
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>
113 lines
3.2 KiB
TypeScript
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)
|
|
})
|
|
})
|