227 lines
8.7 KiB
JavaScript
227 lines
8.7 KiB
JavaScript
import { afterEach, expect, test, vi } from "vitest";
|
|
import { setAccessToken } from "@/shared/api/request";
|
|
import {
|
|
approveFinanceWithdrawalApplication,
|
|
createFinanceCoinSellerRechargeOrder,
|
|
fetchFinanceOptions,
|
|
fetchFinanceSession,
|
|
getFinanceCoinSellerRechargeExchangeRate,
|
|
grantFinanceCoinSellerRechargeOrder,
|
|
listFinanceCoinSellerRechargeOrders,
|
|
listFinanceRechargeDetails,
|
|
listFinanceWithdrawalApplications,
|
|
rejectFinanceWithdrawalApplication,
|
|
quoteFinanceCoinSellerRechargeOrder,
|
|
replaceFinanceCoinSellerRechargeExchangeRate,
|
|
verifyFinanceCoinSellerRechargeReceipt,
|
|
verifyFinanceCoinSellerRechargeOrder
|
|
} from "./api.js";
|
|
|
|
afterEach(() => {
|
|
setAccessToken("");
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
test("finance session no longer treats removed application permissions as withdrawal access", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async () =>
|
|
new Response(
|
|
JSON.stringify({
|
|
code: 0,
|
|
data: {
|
|
permissions: ["finance-application:audit"],
|
|
user: { account: "legacy-finance" }
|
|
}
|
|
})
|
|
)
|
|
)
|
|
);
|
|
|
|
const session = await fetchFinanceSession();
|
|
|
|
expect(session.canAccessFinance).toBe(false);
|
|
expect(session.canAuditWithdrawalApplications).toBe(false);
|
|
expect(session.canViewWithdrawalApplications).toBe(false);
|
|
expect(session).not.toHaveProperty("canAuditApplication");
|
|
expect(session).not.toHaveProperty("canCreateApplication");
|
|
});
|
|
|
|
test("finance options and withdrawal APIs use generated endpoint paths", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async (url) => {
|
|
if (String(url).includes("/admin/apps")) {
|
|
return new Response(JSON.stringify({
|
|
code: 0,
|
|
data: {
|
|
items: [{ appCode: "lalu", appName: "Lalu", logoUrl: "https://media.example.com/lalu.png" }],
|
|
total: 1
|
|
}
|
|
}));
|
|
}
|
|
if (String(url).includes("/withdrawal-applications?")) {
|
|
return new Response(JSON.stringify({ code: 0, data: { items: [], page: 1, pageSize: 50, total: 0 } }));
|
|
}
|
|
return new Response(JSON.stringify({ code: 0, data: { id: 12 } }));
|
|
})
|
|
);
|
|
|
|
const options = await fetchFinanceOptions();
|
|
await listFinanceWithdrawalApplications({ page: 1, page_size: 50 });
|
|
await approveFinanceWithdrawalApplication(14, { auditImageUrl: "https://cdn.example.com/pay.png", auditRemark: "ok" });
|
|
await rejectFinanceWithdrawalApplication(15, { auditRemark: "bad" });
|
|
|
|
const calls = vi.mocked(fetch).mock.calls;
|
|
expect(options.apps).toEqual([
|
|
{ appCode: "lalu", appName: "Lalu", logoUrl: "https://media.example.com/lalu.png" }
|
|
]);
|
|
expect(String(calls[0][0])).toContain("/api/v1/admin/apps");
|
|
expect(calls[0][1]?.method).toBe("GET");
|
|
expect(String(calls[1][0])).toContain("/api/v1/admin/finance/withdrawal-applications?");
|
|
expect(calls[1][1]?.method).toBe("GET");
|
|
expect(String(calls[2][0])).toContain("/api/v1/admin/finance/withdrawal-applications/14/approve");
|
|
expect(calls[2][1]?.method).toBe("POST");
|
|
expect(JSON.parse(String(calls[2][1]?.body))).toEqual({ auditImageUrl: "https://cdn.example.com/pay.png", auditRemark: "ok" });
|
|
expect(String(calls[3][0])).toContain("/api/v1/admin/finance/withdrawal-applications/15/reject");
|
|
expect(calls[3][1]?.method).toBe("POST");
|
|
expect(JSON.parse(String(calls[3][1]?.body))).toEqual({ auditRemark: "bad" });
|
|
});
|
|
|
|
test("finance coin seller recharge order APIs use generated endpoint paths", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async (url) => {
|
|
if (String(url).includes("/coin-seller-recharges?")) {
|
|
return new Response(JSON.stringify({ code: 0, data: { items: [], page: 1, pageSize: 50, total: 0 } }));
|
|
}
|
|
return new Response(JSON.stringify({ code: 0, data: { id: "order-1", targetUserId: "10001" } }));
|
|
})
|
|
);
|
|
|
|
await listFinanceCoinSellerRechargeOrders({ page: 1, page_size: 50, provider_code: "mifapay" });
|
|
await createFinanceCoinSellerRechargeOrder({
|
|
appCode: "lalu",
|
|
externalOrderNo: "MIFA-001",
|
|
providerAmountMinor: 125050,
|
|
providerCode: "mifapay",
|
|
providerCurrencyCode: "PHP",
|
|
targetUserId: "10001",
|
|
usdAmount: 10
|
|
});
|
|
await verifyFinanceCoinSellerRechargeReceipt({
|
|
appCode: "lalu",
|
|
externalOrderNo: "MIFA-001",
|
|
providerAmountMinor: 125050,
|
|
providerCode: "mifapay",
|
|
providerCurrencyCode: "PHP",
|
|
usdAmount: 10
|
|
});
|
|
await verifyFinanceCoinSellerRechargeOrder("order-1");
|
|
await grantFinanceCoinSellerRechargeOrder("order-1");
|
|
|
|
const calls = vi.mocked(fetch).mock.calls;
|
|
expect(String(calls[0][0])).toContain("/api/v1/admin/finance/orders/coin-seller-recharges?");
|
|
expect(String(calls[0][0])).toContain("provider_code=mifapay");
|
|
expect(calls[0][1]?.method).toBe("GET");
|
|
expect(String(calls[1][0])).toContain("/api/v1/admin/finance/orders/coin-seller-recharges");
|
|
expect(calls[1][1]?.method).toBe("POST");
|
|
expect(JSON.parse(String(calls[1][1]?.body))).toEqual({
|
|
appCode: "lalu",
|
|
externalOrderNo: "MIFA-001",
|
|
providerAmountMinor: 125050,
|
|
providerCode: "mifapay",
|
|
providerCurrencyCode: "PHP",
|
|
targetUserId: "10001",
|
|
usdAmount: 10
|
|
});
|
|
expect(String(calls[2][0])).toContain("/api/v1/admin/finance/orders/coin-seller-recharges/receipt-verifications");
|
|
expect(calls[2][1]?.method).toBe("POST");
|
|
expect(JSON.parse(String(calls[2][1]?.body))).toEqual({
|
|
appCode: "lalu",
|
|
externalOrderNo: "MIFA-001",
|
|
providerAmountMinor: 125050,
|
|
providerCode: "mifapay",
|
|
providerCurrencyCode: "PHP",
|
|
usdAmount: 10
|
|
});
|
|
expect(String(calls[3][0])).toContain("/api/v1/admin/finance/orders/coin-seller-recharges/order-1/verify");
|
|
expect(calls[3][1]?.method).toBe("POST");
|
|
expect(String(calls[4][0])).toContain("/api/v1/admin/finance/orders/coin-seller-recharges/order-1/grant");
|
|
expect(calls[4][1]?.method).toBe("POST");
|
|
});
|
|
|
|
test("finance coin seller quote and exchange rate APIs use generated endpoint paths", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async () => new Response(JSON.stringify({ code: 0, data: { appCode: "lalu", tiers: [], whitelist: [] } })))
|
|
);
|
|
|
|
await quoteFinanceCoinSellerRechargeOrder({ appCode: "lalu", targetUserId: "123456", usdAmount: 150 });
|
|
await getFinanceCoinSellerRechargeExchangeRate("lalu");
|
|
await replaceFinanceCoinSellerRechargeExchangeRate("lalu", {
|
|
tiers: [{ minUsdAmount: 100, maxUsdAmount: 200, coinsPerUsd: 80000 }],
|
|
whitelist: [{ userId: "123456", coinsPerUsd: 100000 }]
|
|
});
|
|
|
|
const calls = vi.mocked(fetch).mock.calls;
|
|
expect(String(calls[0][0])).toContain("/api/v1/admin/finance/orders/coin-seller-recharges/quotes");
|
|
expect(calls[0][1]?.method).toBe("POST");
|
|
expect(JSON.parse(String(calls[0][1]?.body))).toEqual({ appCode: "lalu", targetUserId: "123456", usdAmount: 150 });
|
|
expect(String(calls[1][0])).toContain("/api/v1/admin/finance/coin-seller-recharge-exchange-rates/lalu");
|
|
expect(calls[1][1]?.method).toBe("GET");
|
|
expect(String(calls[2][0])).toContain("/api/v1/admin/finance/coin-seller-recharge-exchange-rates/lalu");
|
|
expect(calls[2][1]?.method).toBe("PUT");
|
|
});
|
|
|
|
test("finance recharge details query every app with app header and merge by recharge time", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async (url, init) => {
|
|
const appCode = init?.headers?.["X-App-Code"];
|
|
return new Response(
|
|
JSON.stringify({
|
|
code: 0,
|
|
data: {
|
|
items: [
|
|
{
|
|
appCode,
|
|
coinAmount: appCode === "aslan" ? 2000 : 1000,
|
|
createdAtMs: appCode === "aslan" ? 1760003600000 : 1760000000000,
|
|
exchangeCoinAmount: appCode === "aslan" ? 2000 : 1000,
|
|
exchangeUsdMinorAmount: appCode === "aslan" ? 200 : 100,
|
|
rechargeType: "google_play_recharge",
|
|
status: "succeeded",
|
|
transactionId: `${appCode}-tx`,
|
|
usdMinorAmount: appCode === "aslan" ? 200 : 100,
|
|
userId: "10001"
|
|
}
|
|
],
|
|
page: 1,
|
|
pageSize: 2,
|
|
total: 1
|
|
}
|
|
})
|
|
);
|
|
})
|
|
);
|
|
|
|
const page = await listFinanceRechargeDetails(
|
|
{ page: 1, page_size: 2, start_at_ms: 1760000000000, status: "succeeded" },
|
|
[
|
|
{ appCode: "lalu", appName: "lalu" },
|
|
{ appCode: "aslan", appName: "aslan" }
|
|
]
|
|
);
|
|
|
|
const calls = vi.mocked(fetch).mock.calls;
|
|
expect(page.total).toBe(2);
|
|
expect(page.items.map((item) => item.transactionId)).toEqual(["aslan-tx", "lalu-tx"]);
|
|
expect(calls).toHaveLength(2);
|
|
expect(calls.map(([, init]) => init?.headers?.["X-App-Code"])).toEqual(["lalu", "aslan"]);
|
|
expect(String(calls[0][0])).toContain("/api/v1/admin/payment/recharge-bills?");
|
|
expect(String(calls[0][0])).toContain("page_size=2");
|
|
expect(String(calls[0][0])).toContain("status=succeeded");
|
|
expect(String(calls[0][0])).toContain("start_at_ms=1760000000000");
|
|
});
|