Files
Profeto/frontend/src/lib/http.test.ts
T
shangfangjian f563d5cc99 fix(P0-00): HTTP client 必须传递 method/body,仅 body 存在时设 Content-Type
request() 此前忽略 options.method(默认 GET)与 options.body,导致 POST/PUT/DELETE
全部以 GET 空 body 发出;且无条件设置 Content-Type 误污染无 body 请求。

修复: 显式传递 method(默认 GET) 与 body; 仅当 body 非空时加 Content-Type。

测试 frontend/src/lib/http.test.ts(5/5):GET/POST/PUT/DELETE 的 method/body/headers。
运行: node --experimental-transform-types frontend/src/lib/http.test.ts
2026-09-22 02:24:50 +08:00

66 lines
2.5 KiB
TypeScript

/**
* P0-00: HTTP client method/body/headers 可信度测试。
* 运行: node --experimental-strip-types frontend/src/lib/http.test.ts
*
* 最小环境 polyfill:Node 22 自带 fetch/AbortController,本测试不触发 401 路径,
* 故 window.dispatchEvent 不会被调用,无需完整 DOM。
*/
import { test } from 'node:test'
import assert from 'node:assert/strict'
// 最小浏览器环境 polyfill(仅覆盖 http.ts 在 happy path 用到的全局)
const store: Record<string, string> = {}
// @ts-expect-error 测试用最小 window stub
globalThis.window = {
dispatchEvent: () => false,
localStorage: {
getItem: (k: string) => store[k] ?? null,
setItem: (k: string, v: string) => { store[k] = v },
removeItem: (k: string) => { delete store[k] },
},
}
// 捕获每次 fetch 的入参供断言
let lastInit: RequestInit | undefined
globalThis.fetch = async (_url: string, init?: RequestInit) => {
lastInit = init
return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'content-type': 'application/json' } })
}
const { http } = await import('./http.ts')
test('GET: method=GET, 无 body, 无 Content-Type', async () => {
await http.get('/api/v1/matches')
assert.equal(lastInit?.method, 'GET')
assert.equal(lastInit?.body, undefined)
assert.equal((lastInit?.headers as Record<string, string>)?.['Content-Type'], undefined)
})
test('POST: method=POST, 序列化 body, 有 Content-Type', async () => {
await http.post('/api/v1/matches', { a: 1 })
assert.equal(lastInit?.method, 'POST')
assert.equal(lastInit?.body, JSON.stringify({ a: 1 }))
assert.equal((lastInit?.headers as Record<string, string>)?.['Content-Type'], 'application/json')
})
test('POST 空 body: 不设 Content-Type', async () => {
await http.post('/api/v1/matches', undefined)
assert.equal(lastInit?.method, 'POST')
assert.equal(lastInit?.body, undefined)
assert.equal((lastInit?.headers as Record<string, string>)?.['Content-Type'], undefined)
})
test('PUT: method=PUT, 有 body 与 Content-Type', async () => {
await http.put('/api/v1/x', { b: 2 })
assert.equal(lastInit?.method, 'PUT')
assert.equal(lastInit?.body, JSON.stringify({ b: 2 }))
assert.equal((lastInit?.headers as Record<string, string>)?.['Content-Type'], 'application/json')
})
test('DELETE: method=DELETE, 无 body, 无 Content-Type', async () => {
await http.delete('/api/v1/x/1')
assert.equal(lastInit?.method, 'DELETE')
assert.equal(lastInit?.body, undefined)
assert.equal((lastInit?.headers as Record<string, string>)?.['Content-Type'], undefined)
})