fix: align media errors and serialize bigint server IDs

This commit is contained in:
yimizilu
2026-09-15 08:28:19 +08:00
parent 4f8435d0e2
commit 353df30ff8
5 changed files with 53 additions and 12 deletions
+23 -5
View File
@@ -213,20 +213,38 @@ describe('Local API authentication', () => {
}
})
it('maps media lookup failures to stable API statuses', async () => {
it.each([
['NOT_FOUND', '未找到图片消息', 404],
['NOT_FOUND', '图片文件不存在', 404],
['NOT_IMAGE', '消息不是可读取的图片消息', 422]
] as const)('maps %s (%s) to HTTP %s', async (code, message, status) => {
const handle = await startFixtureServer(
() => VALID_TOKEN,
async () => {
throw new HttpMediaError('NOT_IMAGE', '消息不是可读取的图片消息')
throw new HttpMediaError(code, message)
}
)
const response = await fetch(`${baseUrl(handle)}/api/v1/media/message-1`, {
const response = await fetch(`${baseUrl(handle)}/api/v1/media/image%3Aunresolved`, {
headers: { Authorization: `Bearer ${VALID_TOKEN}` }
})
expect(response.status).toBe(422)
await expect(response.json()).resolves.toMatchObject({ status: 422 })
expect(response.status).toBe(status)
await expect(response.json()).resolves.toMatchObject({ status, error: message })
})
it.each(['%ZZ', 'image%2Finvalid', 'image%5Cinvalid'])(
'rejects malformed media identifiers (%s) before lookup',
async (identifier) => {
const provider = vi.fn(async () => ({ buffer: Buffer.from('image'), mimeType: 'image/png' }))
const handle = await startFixtureServer(() => VALID_TOKEN, provider)
const response = await fetch(`${baseUrl(handle)}/api/v1/media/${identifier}`, {
headers: { Authorization: `Bearer ${VALID_TOKEN}` }
})
expect(response.status).toBe(422)
await expect(response.json()).resolves.toMatchObject({ status: 422 })
expect(provider).not.toHaveBeenCalled()
}
)
it.each(['Basic xxx', 'Bearer', 'bearer xxx', 'Bearer xxx', 'xxx'])(
'rejects the invalid Authorization format %s',
async (authorization) => {
+20 -2
View File
@@ -17,6 +17,7 @@ import {
const IMAGE_A = 'a'.repeat(32)
const IMAGE_B = 'b'.repeat(32)
/** Create a synthetic image row with an intentionally reusable local message ID. */
function image(md5: string, overrides: Partial<WechatMessage> = {}): WechatMessage {
return {
mesLocalID: '56',
@@ -29,6 +30,7 @@ function image(md5: string, overrides: Partial<WechatMessage> = {}): WechatMessa
}
}
/** Attach an in-memory fixture database without reading a real account. */
function connect(messages: Record<string, WechatMessage[]>): void {
const client = { getUsernameByMd5: (md5: string) => `wxid_${md5}` }
setChatDb({
@@ -39,6 +41,7 @@ function connect(messages: Record<string, WechatMessage[]>): void {
} as unknown as WechatDb)
}
/** Extract the opaque handle after checking the formatted image metadata. */
function mediaId(message: FormattedMessage): string {
expect(message.media).toMatchObject({ type: 'image', available: true })
return decodeURIComponent(message.media!.url.slice('/api/v1/media/'.length))
@@ -102,11 +105,26 @@ describe('chat service image media handles', () => {
it('normalizes native server ids without losing integer precision', () => {
const message = image(IMAGE_A, { serverId: 9007199254740993123n })
connect({ first: [message] })
const firstId = mediaId(listMessages('first')[0])
const first = listMessages('first')[0]
const firstId = mediaId(first)
expect(first.serverId).toBe('9007199254740993123')
expect(JSON.parse(JSON.stringify(first)).serverId).toBe('9007199254740993123')
message.serverId = '9007199254740993123'
expect(mediaId(listMessages('first')[0])).toBe(firstId)
const reread = listMessages('first')[0]
expect(reread.serverId).toBe('9007199254740993123')
expect(mediaId(reread)).toBe(firstId)
})
it.each([9007199254740992, null, undefined, false])(
'omits unsupported server ID values (%s)',
(serverId) => {
connect({ first: [image(IMAGE_A, { serverId })] })
const message = listMessages('first')[0]
expect(message.serverId).toBeUndefined()
expect(JSON.parse(JSON.stringify(message))).not.toHaveProperty('serverId')
}
)
it('scopes recovered images and supports images identified only by dat name', () => {
connect({
first: [image(IMAGE_A, { _wxe_recovered: true })],