import { test } from 'node:test' import assert from 'node:assert/strict' import { http } from '../src/shared/api/http.ts' import { assertApiCall, isApiModule } from '../tests/helpers/api-snapshot.ts' function setupWindow() { ;(globalThis as Record).window = { localStorage: { getItem: () => '42' }, location: { origin: 'http://localhost' }, } } function mockRequest( t: Parameters[1] extends (t: infer T) => unknown ? T : never, impl: (config: { url?: string; method?: string; params?: Record; data?: unknown }) => Promise, ) { t.mock.method(http, 'request', impl as never) } const okResponse = (data: unknown) => Promise.resolve({ data: { success: true, message: 'ok', data } }) const withdrawCalls: Array<{ url?: string; method?: string; params?: Record; data?: unknown }> = [] test('test_snapshot_helper_url', async (t) => { setupWindow() mockRequest(t, (config) => { withdrawCalls.push(config) return okResponse({ items: [] }) }) const { listWithdrawCandidates } = await import('../src/shared/api/types/modules/withdraw.ts') await listWithdrawCandidates() assertApiCall(withdrawCalls, 0, { url: '/newApi/api/withdraw/candidates' }) }) test('test_snapshot_helper_method', async (t) => { setupWindow() mockRequest(t, (config) => { withdrawCalls.push(config) return okResponse({}) }) const { clearWithdrawCandidates } = await import('../src/shared/api/types/modules/withdraw.ts') await clearWithdrawCandidates(['店铺A']) assertApiCall(withdrawCalls, 1, { url: '/newApi/api/withdraw/candidates/clear', method: 'POST' }) }) test('test_snapshot_helper_params', async (t) => { setupWindow() mockRequest(t, (config) => { withdrawCalls.push(config) return okResponse({ items: [] }) }) const { getWithdrawHistory } = await import('../src/shared/api/types/modules/withdraw.ts') await getWithdrawHistory() assertApiCall(withdrawCalls, 2, { url: '/newApi/api/withdraw/history', params: { user_id: 42 }, }) }) test('test_snapshot_helper_body', async (t) => { setupWindow() mockRequest(t, (config) => { withdrawCalls.push(config) return okResponse({}) }) const { addWithdrawCandidate } = await import('../src/shared/api/types/modules/withdraw.ts') await addWithdrawCandidate('店铺A') assertApiCall(withdrawCalls, 3, { url: '/newApi/api/withdraw/candidates', method: 'POST', data: { user_id: 42, shop_name: '店铺A' }, }) }) test('test_snapshot_all_modules_covered', () => { setupWindow() const modules = [ 'dedupe', 'split', 'convert', 'publish', 'similar-asin', 'appearance-patent', 'delete-brand', 'price-track', 'shop-match', 'query-asin', 'withdraw', 'collect-data', 'image-video', 'brand', 'permission', 'digital-human', ] for (const name of modules) { assert.ok(isApiModule(name), `${name} 应注册为 API 快照模块`) } }) test('test_snapshot_fail_message', async () => { const calls = [ { url: '/newApi/api/withdraw/candidates/9', method: 'DELETE', params: { user_id: 42 } }, ] try { assertApiCall(calls, 0, { url: '/newApi/api/withdraw/candidates' }) assert.fail('应抛出断言错误') } catch (err) { const message = (err as Error).message assert.ok(message.includes('/newApi/api/withdraw/candidates/9'), '失败信息应含实际 URL') assert.ok(message.includes('/newApi/api/withdraw/candidates'), '失败信息应含期望 URL') } }) test('test_snapshot_allowlist', async () => { const calls = [ { url: '/newApi/api/withdraw/history', method: 'GET', params: { user_id: 42, page: 1 } }, ] try { assertApiCall(calls, 0, { url: '/newApi/api/withdraw/history', params: { user_id: 42 } }) assert.fail('应抛出断言错误') } catch (err) { assert.ok((err as Error).message.includes('page'), '应报告白名单外字段 page') } }) test('test_snapshot_regression_guard', async () => { const calls = [ { url: '/newApi/api/withdraw/candidates', method: 'POST', data: { user_id: 42, shop_name: 'X', owner: 'y' } }, ] try { assertApiCall(calls, 0, { url: '/newApi/api/withdraw/candidates', method: 'POST', data: { user_id: 42, shop_name: 'X' }, }) assert.fail('应抛出断言错误') } catch (err) { assert.ok((err as Error).message.includes('owner'), '快照变更应报告超集字段') } })