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[1] extends (t: infer T) => unknown ? T : never, impl: (config: { signal?: AbortSignal }) => Promise) { 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({ 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', '已中止的调用应失败') })