import { afterEach, expect, test, vi } from 'vitest'; import { createAdminAccount, executeAdminRechargeRefund, getAdminUserDetail, listAdminRechargeOrders, resolveAdminRechargeRefundManualReview, updateAdminAccount, } from './adminApiClient'; afterEach(() => { vi.unstubAllGlobals(); }); test('后台账号创建和更新携带 owner 会话与 Tab 权限', async () => { const fetchMock = vi.fn().mockImplementation(() => Promise.resolve( new Response(JSON.stringify({account: {accountId: 'member-1'}}), { status: 200, }), ), ); vi.stubGlobal('fetch', fetchMock); await createAdminAccount('owner-token', { username: 'operator', displayName: '运营', password: 'secret123', tabPermissions: ['dashboard', 'tracking'], enabled: true, }); await updateAdminAccount('owner-token', 'member/1', { displayName: '运营二组', tabPermissions: ['tracking'], enabled: false, }); expect(fetchMock.mock.calls[0]?.[0]).toBe('/admin/api/accounts'); expect(fetchMock.mock.calls[0]?.[1]).toEqual( expect.objectContaining({ method: 'POST', headers: expect.objectContaining({Authorization: 'Bearer owner-token'}), }), ); expect(fetchMock.mock.calls[1]?.[0]).toBe('/admin/api/accounts/member%2F1'); expect(fetchMock.mock.calls[1]?.[1]).toEqual( expect.objectContaining({ method: 'PUT', body: JSON.stringify({ displayName: '运营二组', tabPermissions: ['tracking'], enabled: false, }), }), ); }); test('充值订单查询按后台契约序列化筛选参数', async () => { const fetchMock = vi.fn().mockResolvedValue( new Response(JSON.stringify({ entries: [] }), { status: 200, headers: { 'content-type': 'application/json' }, }), ); vi.stubGlobal('fetch', fetchMock); await listAdminRechargeOrders('token-1', { orderId: 'order 1', userId: 'user-1', providerTransactionId: 'wx-1', paymentChannel: 'wechat_native', status: 'paid', createdAfter: '2026-07-01T00:00:00.000Z', createdBefore: '2026-07-13T23:59:00.000Z', limit: 50, }); const requestUrl = String(fetchMock.mock.calls[0]?.[0]); const parsed = new URL(requestUrl, 'http://admin.local'); expect(parsed.pathname).toBe('/admin/api/profile/recharge-orders'); expect(Object.fromEntries(parsed.searchParams)).toEqual({ orderId: 'order 1', providerTransactionId: 'wx-1', userId: 'user-1', paymentChannel: 'wechat_native', status: 'paid', createdAfter: '2026-07-01T00:00:00.000Z', createdBefore: '2026-07-13T23:59:00.000Z', limit: '50', }); }); test('用户详情只发送实际提供的用户定位字段', async () => { const fetchMock = vi .fn() .mockResolvedValue( new Response(JSON.stringify({ userId: 'user-1' }), { status: 200 }), ); vi.stubGlobal('fetch', fetchMock); await getAdminUserDetail('token-1', { publicUserCode: 'TN1001' }); const requestUrl = String(fetchMock.mock.calls[0]?.[0]); const parsed = new URL(requestUrl, 'http://admin.local'); expect(parsed.pathname).toBe('/admin/api/profile/users/detail'); expect(Object.fromEntries(parsed.searchParams)).toEqual({ publicUserCode: 'TN1001', }); }); test('退款执行使用独立 execute 管理员路由', async () => { const fetchMock = vi .fn() .mockResolvedValue( new Response(JSON.stringify({ outRefundNo: 'refund-1' }), { status: 200, }), ); vi.stubGlobal('fetch', fetchMock); await executeAdminRechargeRefund('token-1', { orderId: 'order-1', refundAmountCents: 300, requestId: 'request-1', reason: '用户申请', }); expect(String(fetchMock.mock.calls[0]?.[0])).toBe( '/admin/api/profile/recharge-refunds/execute', ); expect(fetchMock.mock.calls[0]?.[1]).toEqual( expect.objectContaining({ method: 'POST', body: JSON.stringify({ orderId: 'order-1', refundAmountCents: 300, requestId: 'request-1', reason: '用户申请', }), }), ); }); test('退款人工复核使用独立 resolve 管理员路由', async () => { const fetchMock = vi .fn() .mockResolvedValue( new Response(JSON.stringify({ outRefundNo: 'refund-1' }), { status: 200, }), ); vi.stubGlobal('fetch', fetchMock); await resolveAdminRechargeRefundManualReview('token-1', { outRefundNo: 'refund-1', reason: '已核对微信商户平台原始账单', expectedErrorCode: 'provider_transaction_id_mismatch', }); expect(String(fetchMock.mock.calls[0]?.[0])).toBe( '/admin/api/profile/recharge-refunds/manual-review/resolve', ); expect(fetchMock.mock.calls[0]?.[1]).toEqual( expect.objectContaining({ method: 'POST', body: JSON.stringify({ outRefundNo: 'refund-1', reason: '已核对微信商户平台原始账单', expectedErrorCode: 'provider_transaction_id_mismatch', }), }), ); });