Files
NianAIGC/tests/billing-api-contract.test.ts

148 lines
5.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it } from "vitest";
import { parseAdminBillingPayload, parseMemberBillingPayload } from "@/lib/client/billing-api";
const wallet = {
organizationId: "org-1",
balanceFen: 0,
totalRechargedFen: 0,
totalChargedFen: 0,
updatedAt: "2026-08-17T00:00:00.000Z"
};
const ledgerEntry = {
id: "ledger-1",
organizationId: "org-1",
kind: "recharge",
deltaFen: 100,
balanceAfterFen: 100,
description: "充值",
metadata: {},
createdAt: "2026-08-17T00:00:00.000Z"
};
function validAdminPayload() {
return {
billingAccount: {},
organizations: [],
members: [],
ledger: [],
priceRules: []
};
}
function validMemberPayload() {
return {
organization: { id: "org-1", name: "测试组织" },
billingAccount: {},
wallet,
ledger: [],
summary: { rechargeFen: 0, chargedFen: 0, refundedFen: 0, netConsumedFen: 0 },
personal: { rechargeFen: 0, chargedFen: 0, refundedFen: 0, netConsumedFen: 0 }
};
}
describe("billing API payload contract", () => {
it("accepts valid empty collection payloads", () => {
const admin = validAdminPayload();
const member = validMemberPayload();
expect(parseAdminBillingPayload(admin)).toBe(admin);
expect(parseMemberBillingPayload(member)).toBe(member);
});
it.each(["organizations", "members", "ledger", "priceRules"])(
"rejects a missing admin %s collection",
(field) => {
const payload: Record<string, unknown> = validAdminPayload();
delete payload[field];
expect(() => parseAdminBillingPayload(payload))
.toThrow(`超管计费数据格式错误:${field} 必须是数组。`);
}
);
it("rejects a malformed admin price-rule element", () => {
expect(() => parseAdminBillingPayload({ ...validAdminPayload(), priceRules: [null] }))
.toThrow("超管计费数据格式错误priceRules[0] 必须是对象。");
});
it("rejects malformed admin organization elements and wallets", () => {
expect(() => parseAdminBillingPayload({ ...validAdminPayload(), organizations: [null] }))
.toThrow("超管计费数据格式错误organizations[0] 必须是对象。");
expect(() => parseAdminBillingPayload({
...validAdminPayload(),
organizations: [{ id: "org-1", name: "测试组织", status: "active", wallet: { ...wallet, balanceFen: "0" } }]
})).toThrow("超管计费数据格式错误organizations[0].wallet.balanceFen 必须是有限数字。");
});
it("rejects a malformed admin member element", () => {
expect(() => parseAdminBillingPayload({ ...validAdminPayload(), members: [null] }))
.toThrow("超管计费数据格式错误members[0] 必须是对象。");
});
it("accepts a fresh super administrator without organizationId", () => {
const payload = {
...validAdminPayload(),
members: [{ id: "root", displayName: "管理员", phone: "13800000000", role: "super_admin", status: "active" }]
};
expect(parseAdminBillingPayload(payload)).toBe(payload);
});
it("rejects a malformed admin ledger element", () => {
expect(() => parseAdminBillingPayload({ ...validAdminPayload(), ledger: [null] }))
.toThrow("超管计费数据格式错误ledger[0] 必须是对象。");
});
it("rejects malformed member objects used by the renderer", () => {
expect(() => parseMemberBillingPayload({ ...validMemberPayload(), organization: null }))
.toThrow("成员计费数据格式错误organization 必须是对象。");
expect(() => parseMemberBillingPayload({ ...validMemberPayload(), wallet: null }))
.toThrow("成员计费数据格式错误wallet 必须是对象。");
expect(() => parseMemberBillingPayload({ ...validMemberPayload(), summary: null }))
.toThrow("成员计费数据格式错误summary 必须是对象。");
expect(() => parseMemberBillingPayload({ ...validMemberPayload(), personal: null }))
.toThrow("成员计费数据格式错误personal 必须是对象。");
});
it("rejects a malformed member ledger element", () => {
expect(() => parseMemberBillingPayload({ ...validMemberPayload(), ledger: [null] }))
.toThrow("成员计费数据格式错误ledger[0] 必须是对象。");
});
it("accepts fully populated collection elements", () => {
const admin = {
...validAdminPayload(),
organizations: [{ id: "org-1", name: "测试组织", status: "active", wallet }],
members: [{ id: "member-1", displayName: "成员", phone: "13800000000", role: "user", organizationId: "org-1", status: "active" }],
ledger: [ledgerEntry],
priceRules: [{
id: "rule-1",
provider: "evolink",
capability: "image.generate",
unit: "image",
standardUnitPriceFen: 100,
markupMultiplier: 1.2,
enabled: true,
parameterDimensions: [{
key: "quality",
label: "质量",
baselineValue: "standard",
tiers: [{ value: "standard", label: "标准", standardFactor: 1, markupMultiplier: 1.2, enabled: true }]
}]
}]
};
const member = { ...validMemberPayload(), ledger: [ledgerEntry] };
expect(parseAdminBillingPayload(admin)).toBe(admin);
expect(parseMemberBillingPayload(member)).toBe(member);
});
it("rejects non-object responses", () => {
expect(() => parseAdminBillingPayload("unexpected"))
.toThrow("超管计费数据格式错误:响应必须是对象。");
expect(() => parseMemberBillingPayload(null))
.toThrow("成员计费数据格式错误:响应必须是对象。");
});
});