72 lines
3.9 KiB
TypeScript
72 lines
3.9 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
|
|
const root = new URL("../", import.meta.url);
|
|
const route = readFileSync(new URL("src/app/api/admin/payments/route.ts", root), "utf8");
|
|
const packagesRoute = readFileSync(new URL("src/app/api/admin/packages/route.ts", root), "utf8");
|
|
const page = readFileSync(new URL("src/app/admin/payments/page.tsx", root), "utf8");
|
|
const packagesPage = readFileSync(new URL("src/app/admin/packages/page.tsx", root), "utf8");
|
|
const management = readFileSync(new URL("src/components/admin/payment-management.tsx", root), "utf8");
|
|
const migration = readFileSync(new URL("supabase/migrations/20260727030000_payment_admin_stats.sql", root), "utf8");
|
|
|
|
test("支付后台接口只允许管理员并查询平台订单", () => {
|
|
assert.match(route, /requireAdminSession\("read"\)/);
|
|
assert.doesNotMatch(route, /isAdminUser\(user\)/);
|
|
assert.match(route, /from\("payment_orders"\)/);
|
|
assert.match(route, /auth\.admin\.getUserById/);
|
|
assert.match(route, /payment_packages\(name\)/);
|
|
assert.match(route, /order_no|orderNo/);
|
|
assert.doesNotMatch(route, /SUPABASE_SERVICE_ROLE_KEY/);
|
|
assert.doesNotMatch(route, /raw_notify_payload/);
|
|
});
|
|
|
|
test("支付接口包含筛选、统计和分页契约", () => {
|
|
for (const field of ["status", "from", "to", "limit", "offset"]) assert.match(route, new RegExp(field));
|
|
for (const field of ["totalOrders", "paidOrders", "pendingOrders", "failedExpiredOrders", "paidAmountCents", "grantedCredits"]) assert.match(route, new RegExp(field));
|
|
assert.match(route, /max\(100\)/);
|
|
assert.match(route, /count: "exact"/);
|
|
assert.match(route, /hasMore/);
|
|
assert.match(migration, /get_payment_order_stats/);
|
|
assert.match(migration, /status = 'paid'/);
|
|
});
|
|
|
|
test("统一支付管理页面使用 Ant Design 与 Refine 并保留支付数据合同", () => {
|
|
assert.match(page, /PaymentManagement/);
|
|
for (const component of ["List", "Card", "Table", "Form", "Tag", "Alert", "Statistic"]) {
|
|
assert.match(management, new RegExp(`\\b${component}\\b`));
|
|
}
|
|
assert.match(management, /\/api\/admin\/payments/);
|
|
assert.match(management, /\/api\/admin\/packages/);
|
|
for (const field of ["orderNo", "userEmail", "packageName", "moneyCents", "credits", "status", "epayTradeNo", "createdAt", "paidAt"]) {
|
|
assert.match(management, new RegExp(field));
|
|
}
|
|
assert.match(management, /paymentLoading/);
|
|
assert.match(management, /paymentError/);
|
|
assert.match(management, /pagination=\{\{/);
|
|
assert.doesNotMatch(management, /standalone-page|admin-header|admin-section/);
|
|
});
|
|
|
|
test("套餐列表、Modal 与新增编辑停用操作收敛到支付管理页", () => {
|
|
assert.match(management, /<Card title="套餐列表" extra=\{<Button[\s\S]*?添加套餐<\/Button>\}>/);
|
|
assert.doesNotMatch(management, /<List[\s\S]{0,120}headerButtons/);
|
|
assert.match(management, /<Modal/);
|
|
assert.match(management, /title=\{editingPackage \? "编辑套餐" : "添加套餐"\}/);
|
|
assert.match(management, /价格(元)/);
|
|
assert.match(management, /priceCents: Math\.round\(values\.priceYuan \* 100\)/);
|
|
for (const method of ["POST", "PATCH", "DELETE"]) assert.match(management, new RegExp(`method: .*${method}`));
|
|
assert.match(management, /message\.success/);
|
|
assert.match(management, /message\.error/);
|
|
assert.match(management, /setPackagesLoading/);
|
|
assert.match(management, /packagesError/);
|
|
assert.match(management, /packageForm\.resetFields\(\)/);
|
|
assert.match(packagesRoute, /requireAdminSession\("read"\)/);
|
|
assert.match(packagesRoute, /requireAdminSession\("write"\)/);
|
|
});
|
|
|
|
test("旧套餐路由服务端重定向到统一支付管理页", () => {
|
|
assert.match(packagesPage, /import \{ redirect \} from "next\/navigation"/);
|
|
assert.match(packagesPage, /redirect\("\/admin\/payments"\)/);
|
|
assert.doesNotMatch(packagesPage, /use client|<form|\/api\/admin\/packages/);
|
|
});
|