import { test } from 'node:test' import assert from 'node:assert/strict' import { http } from '../src/shared/api/http.ts' import { listWithdrawCandidates, addWithdrawCandidate, deleteWithdrawCandidate, clearWithdrawCandidates, matchWithdrawShops, getWithdrawDashboard, getWithdrawHistory, getWithdrawTaskProgressBatch, createWithdrawTask, submitWithdrawTaskResult, getWithdrawResultDownloadUrl, deleteWithdrawTask, deleteWithdrawHistory, listPatrolDeleteCandidates, addPatrolDeleteCandidate, deletePatrolDeleteCandidate, listPatrolDeleteConditions, addPatrolDeleteCondition, deletePatrolDeleteCondition, matchPatrolDeleteShops, getPatrolDeleteDashboard, getPatrolDeleteHistory, getPatrolDeleteTaskProgressBatch, createPatrolDeleteTask, submitPatrolDeleteTaskResult, getPatrolDeleteResultDownloadUrl, deletePatrolDeleteTask, deletePatrolDeleteHistory, } from '../src/shared/api/types/modules/withdraw.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; timeout?: number }) => Promise, ) { t.mock.method(http, 'request', impl as never) } const okResponse = (data: unknown) => Promise.resolve({ data: { success: true, message: 'ok', data } }) test('test_withdraw_candidates', async (t) => { setupWindow() const calls: { url?: string; method?: string; params?: Record; data?: unknown }[] = [] mockRequest(t, (config) => { calls.push(config) return okResponse({ id: 1, shop_name: 's1' }) }) await listWithdrawCandidates() await addWithdrawCandidate('店铺A') await deleteWithdrawCandidate(3) await clearWithdrawCandidates(['店铺A', '店铺B']) assert.equal(calls[0].url, '/newApi/api/withdraw/candidates') assert.deepEqual(calls[0].params, { user_id: 42 }) assert.equal(calls[1].url, '/newApi/api/withdraw/candidates') assert.equal(calls[1].method, 'POST') assert.deepEqual(calls[1].data, { user_id: 42, shop_name: '店铺A' }) assert.equal(calls[2].url, '/newApi/api/withdraw/candidates/3') assert.equal(calls[2].method, 'DELETE') assert.equal(calls[3].url, '/newApi/api/withdraw/candidates/clear') assert.equal(calls[3].method, 'POST') assert.deepEqual(calls[3].data, { user_id: 42, shop_names: ['店铺A', '店铺B'] }) }) test('test_withdraw_create', async (t) => { setupWindow() const calls: { url?: string; method?: string; data?: unknown }[] = [] mockRequest(t, (config) => { calls.push(config) return okResponse({ taskId: 1, items: [] }) }) await createWithdrawTask([{ shopName: '店铺A', matched: true }], 100) assert.equal(calls[0].url, '/newApi/api/withdraw/tasks') assert.equal(calls[0].method, 'POST') assert.deepEqual(calls[0].data, { user_id: 42, reserved_amount: 100, items: [{ shopName: '店铺A', matched: true }] }) }) test('test_withdraw_submit_result', async (t) => { setupWindow() const calls: { url?: string; method?: string; data?: unknown }[] = [] mockRequest(t, (config) => { calls.push(config) return okResponse(null) }) const payload = { shops: [{ shopName: '店铺A', rows: [{ country: 'US', withdrawAmount: 10, status: 'SUCCESS' }], shopDone: true }], } await submitWithdrawTaskResult(7, payload) assert.equal(calls[0].url, '/newApi/api/withdraw/tasks/7/result') assert.equal(calls[0].method, 'POST') assert.deepEqual(calls[0].data, payload) }) test('test_withdraw_progress_dashboard_history', async (t) => { setupWindow() const calls: { url?: string; method?: string; data?: unknown; timeout?: number; params?: Record }[] = [] mockRequest(t, (config) => { calls.push(config) return okResponse({ items: [], missingTaskIds: [] }) }) await getWithdrawTaskProgressBatch([5, 3]) await getWithdrawDashboard() await getWithdrawHistory() await deleteWithdrawTask(7) await deleteWithdrawHistory(9) assert.equal(calls[0].url, '/newApi/api/withdraw/tasks/progress/batch') assert.equal(calls[0].method, 'POST') assert.deepEqual(calls[0].data, { taskIds: [3, 5], userId: 42 }) assert.equal(calls[0].timeout, 10000) assert.equal(calls[1].url, '/newApi/api/withdraw/dashboard') assert.deepEqual(calls[1].params, { user_id: 42 }) assert.equal(calls[2].url, '/newApi/api/withdraw/history') assert.equal(calls[3].url, '/newApi/api/withdraw/tasks/7') assert.equal(calls[3].method, 'DELETE') assert.equal(calls[4].url, '/newApi/api/withdraw/history/9') assert.equal(calls[4].method, 'DELETE') assert.equal( getWithdrawResultDownloadUrl(7), 'http://localhost/newApi/api/withdraw/results/7/download?user_id=42', ) }) test('test_withdraw_match', async (t) => { setupWindow() const calls: { url?: string; data?: unknown }[] = [] mockRequest(t, (config) => { calls.push(config) return okResponse({ items: [] }) }) await matchWithdrawShops(['店铺A']) assert.equal(calls[0].url, '/newApi/api/withdraw/match-shops') assert.deepEqual(calls[0].data, { user_id: 42, shop_names: ['店铺A'] }) }) test('test_patrol_delete_candidates_conditions', async (t) => { setupWindow() const calls: { url?: string; method?: string; params?: Record; data?: unknown }[] = [] mockRequest(t, (config) => { calls.push(config) return okResponse({ id: 1, shop_name: 's1' }) }) await listPatrolDeleteCandidates() await addPatrolDeleteCandidate('店铺A') await deletePatrolDeleteCandidate(3) await listPatrolDeleteConditions() await addPatrolDeleteCondition('条件X') await deletePatrolDeleteCondition(5) assert.equal(calls[0].url, '/newApi/api/patrol-delete/candidates') assert.deepEqual(calls[0].params, { user_id: 42 }) assert.equal(calls[1].url, '/newApi/api/patrol-delete/candidates') assert.equal(calls[1].method, 'POST') assert.deepEqual(calls[1].data, { user_id: 42, shop_name: '店铺A' }) assert.equal(calls[2].url, '/newApi/api/patrol-delete/candidates/3') assert.equal(calls[2].method, 'DELETE') assert.equal(calls[3].url, '/newApi/api/patrol-delete/conditions') assert.deepEqual(calls[3].params, { user_id: 42 }) assert.equal(calls[4].url, '/newApi/api/patrol-delete/conditions') assert.equal(calls[4].method, 'POST') assert.deepEqual(calls[4].data, { user_id: 42, conditionText: '条件X' }) assert.equal(calls[5].url, '/newApi/api/patrol-delete/conditions/5') assert.equal(calls[5].method, 'DELETE') }) test('test_patrol_delete_create_submit', async (t) => { setupWindow() const calls: { url?: string; method?: string; data?: unknown }[] = [] mockRequest(t, (config) => { calls.push(config) return okResponse({ taskId: 1, items: [] }) }) const item = { shopName: '店铺A', matched: true, countrySections: [{ country: 'US', rows: [{ status: 'ok', quantity: '1', deleteQuantity: '0', processStatus: 'done' }] }], cartRatios: [{ country: 'US', ratio: '0.5' }], } await createPatrolDeleteTask([item]) assert.equal(calls[0].url, '/newApi/api/patrol-delete/tasks') assert.equal(calls[0].method, 'POST') assert.deepEqual(calls[0].data, { user_id: 42, items: [item], delete_conditions: [] }) const payload = { shops: [{ shopName: '店铺A', countrySections: item.countrySections, cartRatios: item.cartRatios, shopDone: true, chunkIndex: 0, chunkTotal: 1 }] } await submitPatrolDeleteTaskResult(7, payload) assert.equal(calls[1].url, '/newApi/api/patrol-delete/tasks/7/result') assert.deepEqual(calls[1].data, payload) }) test('test_patrol_delete_progress_dashboard_download', async (t) => { setupWindow() const calls: { url?: string; method?: string; data?: unknown; timeout?: number; params?: Record }[] = [] mockRequest(t, (config) => { calls.push(config) return okResponse({ items: [], missingTaskIds: [] }) }) await getPatrolDeleteTaskProgressBatch([5, 3]) await getPatrolDeleteDashboard() await getPatrolDeleteHistory() await matchPatrolDeleteShops(['店铺A']) await deletePatrolDeleteTask(7) await deletePatrolDeleteHistory(9) assert.equal(calls[0].url, '/newApi/api/patrol-delete/tasks/progress/batch') assert.equal(calls[0].method, 'POST') assert.deepEqual(calls[0].data, { taskIds: [3, 5], userId: 42 }) assert.equal(calls[0].timeout, 10000) assert.equal(calls[1].url, '/newApi/api/patrol-delete/dashboard') assert.deepEqual(calls[1].params, { user_id: 42 }) assert.equal(calls[2].url, '/newApi/api/patrol-delete/history') assert.equal(calls[3].url, '/newApi/api/patrol-delete/match-shops') assert.deepEqual(calls[3].data, { user_id: 42, shop_names: ['店铺A'] }) assert.equal(calls[4].url, '/newApi/api/patrol-delete/tasks/7') assert.equal(calls[4].method, 'DELETE') assert.equal(calls[5].url, '/newApi/api/patrol-delete/history/9') assert.equal(calls[5].method, 'DELETE') assert.equal( getPatrolDeleteResultDownloadUrl(7), 'http://localhost/newApi/api/patrol-delete/results/7/download?user_id=42', ) }) test('test_withdraw_patrol_export_compat', async (t) => { setupWindow() const fromJavaModules = await import('../src/shared/api/java-modules.ts') const fromWithdraw = await import('../src/shared/api/types/modules/withdraw.ts') const names = [ 'listWithdrawCandidates', 'addWithdrawCandidate', 'deleteWithdrawCandidate', 'clearWithdrawCandidates', 'matchWithdrawShops', 'getWithdrawDashboard', 'getWithdrawHistory', 'getWithdrawTaskProgressBatch', 'createWithdrawTask', 'submitWithdrawTaskResult', 'getWithdrawResultDownloadUrl', 'deleteWithdrawTask', 'deleteWithdrawHistory', 'listPatrolDeleteCandidates', 'addPatrolDeleteCandidate', 'deletePatrolDeleteCandidate', 'listPatrolDeleteConditions', 'addPatrolDeleteCondition', 'deletePatrolDeleteCondition', 'matchPatrolDeleteShops', 'getPatrolDeleteDashboard', 'getPatrolDeleteHistory', 'getPatrolDeleteTaskProgressBatch', 'createPatrolDeleteTask', 'submitPatrolDeleteTaskResult', 'getPatrolDeleteResultDownloadUrl', 'deletePatrolDeleteTask', 'deletePatrolDeleteHistory', ] for (const name of names) { assert.equal(fromJavaModules[name], fromWithdraw[name], `${name} 应为同一引用`) } const row = { country: 'US', withdrawAmount: 10, status: 'SUCCESS' } assert.equal(row.status, 'SUCCESS') }) test('test_withdraw_unwrap_and_error', async (t) => { setupWindow() mockRequest(t, () => okResponse({ taskId: 1, items: [] })) const result = await createWithdrawTask([], null) assert.equal(result.taskId, 1) mockRequest(t, () => Promise.resolve({ data: { success: false, message: '余额不足' } })) await assert.rejects(addWithdrawCandidate('x'), /余额不足/) })