task-8: callJava AbortController 透传契约测试(发送前/进行中中止、signal 透传、多调用独立)

This commit is contained in:
2026-08-31 19:21:56 +08:00
parent 35de13f7f1
commit 980e6e91e0
+115
View File
@@ -0,0 +1,115 @@
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { http } from '../src/shared/api/http.ts'
import { callJava } from '../src/shared/api/call.ts'
function mockRequest(t: Parameters<typeof test>[1] extends (t: infer T) => unknown ? T : never, impl: (config: { signal?: AbortSignal }) => Promise<unknown>) {
t.mock.method(http, 'request', impl as never)
}
function abortedError() {
const error = new Error('canceled')
error.name = 'CanceledError'
return error
}
test('test_signal_abort_before_send', async (t) => {
const controller = new AbortController()
controller.abort()
mockRequest(t, (config) => {
if (config.signal?.aborted) {
throw abortedError()
}
return Promise.resolve({ data: { success: true, message: 'ok', data: 1 } })
})
await assert.rejects(
callJava({ url: '/newApi/api/x', method: 'GET', signal: controller.signal }),
(error: Error) => /canceled|Abort/i.test(error.message),
'发送前已中止应立即拒绝',
)
})
test('test_signal_abort_mid_request', async (t) => {
const controller = new AbortController()
mockRequest(t, (config) =>
new Promise((_resolve, reject) => {
config.signal?.addEventListener('abort', () => reject(abortedError()))
}),
)
const promise = callJava({ url: '/newApi/api/x', method: 'GET', signal: controller.signal })
setTimeout(() => controller.abort(), 5)
await assert.rejects(promise, /canceled/, '进行中中止应拒绝')
})
test('test_signal_passthrough', async (t) => {
const controller = new AbortController()
let captured: unknown
mockRequest(t, (config) => {
captured = config.signal
return Promise.resolve({ data: { success: true, message: 'ok', data: 1 } })
})
await callJava({ url: '/newApi/api/x', method: 'GET', signal: controller.signal })
assert.equal(captured, controller.signal, 'signal 应透传到 axios 配置')
})
test('test_signal_abort_error_message', async (t) => {
const controller = new AbortController()
controller.abort()
mockRequest(t, (config) => {
if (config.signal?.aborted) {
throw abortedError()
}
return Promise.resolve({ data: { success: true, message: 'ok', data: 1 } })
})
await assert.rejects(
callJava({ url: '/newApi/api/x', method: 'GET', signal: controller.signal }),
(error: Error) => /canceled|Abort/i.test(error.message) || error.name === 'CanceledError',
'错误消息应含 AbortError 特征',
)
})
test('test_signal_not_provided', async (t) => {
mockRequest(t, () => Promise.resolve({ data: { success: true, message: 'ok', data: 'ok' } }))
const result = await callJava<string>({ url: '/newApi/api/x', method: 'GET' })
assert.equal(result, 'ok')
})
test('test_abort_after_resolve', async (t) => {
const controller = new AbortController()
mockRequest(t, () => Promise.resolve({ data: { success: true, message: 'ok', data: 1 } }))
const result = await callJava({ url: '/newApi/api/x', method: 'GET', signal: controller.signal })
controller.abort()
assert.equal(result, 1, '完成后 abort 不应有副作用')
})
test('test_abort_rejects_promise', async (t) => {
const controller = new AbortController()
mockRequest(t, (config) =>
new Promise((_resolve, reject) => {
config.signal?.addEventListener('abort', () => reject(abortedError()))
}),
)
const promise = callJava({ url: '/newApi/api/x', method: 'GET', signal: controller.signal })
controller.abort()
await assert.rejects(promise, /canceled/, '中止应拒绝 Promise')
})
test('test_signal_multiple_calls_independent', async (t) => {
const first = new AbortController()
const second = new AbortController()
let calls = 0
mockRequest(t, (config) => {
calls += 1
if (config.signal?.aborted) {
throw abortedError()
}
return Promise.resolve({ data: { success: true, message: 'ok', data: calls } })
})
second.abort()
const [a, b] = await Promise.allSettled([
callJava({ url: '/newApi/api/x', method: 'GET', signal: first.signal }),
callJava({ url: '/newApi/api/y', method: 'GET', signal: second.signal }),
])
assert.equal(a.status, 'fulfilled', '未中止的调用应成功')
assert.equal(b.status, 'rejected', '已中止的调用应失败')
})