import assert from 'node:assert/strict' import test from 'node:test' import { readFileSync } from 'node:fs' import ts from 'typescript' import axios from 'axios' function compile(name, dependencies = {}) { const source = readFileSync(new URL(name, import.meta.url), 'utf8') .replace('import.meta.env.VITE_API_BASE_URL', "'/api'") const code = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, esModuleInterop: true }, }).outputText const exports = {} new Function('require', 'exports', code)((id) => { assert.ok(id in dependencies, `unexpected dependency ${id}`) return dependencies[id] }, exports) return exports } function harness(adapter) { let token = 'old-access' let refreshToken = 'old-refresh' const http = compile('./http.ts', { axios: { ...axios, create: (config) => axios.create({ ...config, adapter }) }, '@chongchong/shared': { parseApiJsonResponse: (data) => data }, '@/api/session': { getAccessToken: () => token, getRefreshToken: () => refreshToken, persistTokenPair: (tokens) => { token = tokens.accessToken; refreshToken = tokens.refreshToken }, clearSessionTokens: () => { token = ''; refreshToken = '' }, }, '@/api/errorDetails': compile('./errorDetails.ts'), '@/api/types': compile('./types.ts'), '@/api/crossTabRefresh': compile('./crossTabRefresh.ts'), }) return http } test('order and lastleg 401s share one refresh and replay the unified access token', async () => { let refreshes = 0 const calls = [] const http = harness(async (config) => { calls.push({ url: config.url, base: config.baseURL, token: config.headers.Authorization }) if (config.url === '/auth/refresh') { refreshes++ await new Promise((resolve) => setTimeout(resolve, 10)) return { config, status: 200, headers: {}, data: { code: '200', message: 'OK', data: { accessToken: 'new-access', refreshToken: 'new-refresh' } } } } if (config.headers.Authorization === 'Bearer old-access') { throw new axios.AxiosError('Expired', 'ERR_BAD_REQUEST', config, null, { status: 401, data: { code: 'A0401', message: 'Expired' } }) } return { config, status: 200, headers: {}, data: { code: '200', message: 'OK', data: config.url } } }) const urls = ['/merchant/v1/orders', '/merchant/v1/last-leg/inbound-plans'] assert.deepEqual(await Promise.all(urls.map((url) => http.apiRequest({ url }))), urls) assert.equal(refreshes, 1) assert.ok(calls.every((call) => call.base === '/api')) assert.equal(calls.find((call) => call.url === '/auth/refresh').token, undefined) assert.equal(calls.filter((call) => call.token === 'Bearer new-access').length, 2) }) test('data-less security failures preserve diagnostics and malformed success is rejected', async () => { for (const data of [{ code: 'A0403', message: 'Denied', traceId: 'trace-1', requestId: 'request-1' }, { code: '200', message: 'OK' }]) { const http = harness(async (config) => ({ config, status: 200, headers: {}, data })) await assert.rejects(http.apiRequest({ url: '/merchant/v1/last-leg/inbound-plans' }), (error) => { assert.equal(error.code, data.code === '200' ? 'INVALID_RESPONSE' : 'A0403') if (data.code !== '200') { assert.equal(error.traceId, 'trace-1'); assert.equal(error.requestId, 'request-1') } return true }) } })