import { expect, test } from "bun:test"; import { renderToStaticMarkup } from "react-dom/server"; import { ChannelModelCostSummary } from "../src/pages/admin/components/channel-model-cost-summary"; import type { ChannelModel, ChannelModelPriceTier } from "../src/services/api/wallet"; const tier: ChannelModelPriceTier = { id: "tier", channelModelId: "model", selector: {}, selectorKey: "{}", resolution: "*", videoSeconds: 0, providerModelKey: "", billingMode: "fixed_request", unitPriceMicrocredits: 99000000, inputTokenPriceMicrocredits: 88000000, outputTokenPriceMicrocredits: 77000000, cachedTokenPriceMicrocredits: 66000000, priceConfigured: true, enabled: true, priceVersion: 1, createdAt: "", updatedAt: "", costPricing: { configured: true, unitPriceMicrocredits: 1250000, inputTokenPriceMicrocredits: 2000000, outputTokenPriceMicrocredits: 3000000, cachedTokenPriceMicrocredits: 500000 }, }; const item: ChannelModel = { id: "model", channelId: "channel", modelKey: "model", displayName: "model", providerModelKey: "", icon: "", capability: "image", billingMode: "fixed_request", unitPriceMicrocredits: 99000000, inputTokenPriceMicrocredits: 88000000, outputTokenPriceMicrocredits: 77000000, cachedTokenPriceMicrocredits: 66000000, priceConfigured: true, enabled: true, priceVersion: 1, priceTiers: [tier], createdAt: "", updatedAt: "", }; const render = (tiers: ChannelModelPriceTier[], capability: ChannelModel["capability"] = "image") => renderToStaticMarkup(); test("fixed and per-second specifications show cost, sale and margin", () => { const fixed = render([tier]); expect(fixed).toContain("默认规格"); expect(fixed).toContain("1.25 / 99"); expect(fixed).toContain("积分 / 次 · 利润率 98.74%"); expect(render([{ ...tier, billingMode: "per_second" }])).toContain("积分 / 秒"); }); test("text token costs include input, output and cached tokens", () => { const html = render([{ ...tier, billingMode: "token" }], "text"); expect(html).toContain("输入 2 / 88"); expect(html).toContain("输出 3 / 77"); expect(html).toContain("缓存 0.5 / 66"); expect(html).toContain("积分 / 百万 Token"); }); test("video token costs use only output video tokens", () => { const html = render([{ ...tier, billingMode: "token" }], "video"); expect(html).toContain("视频 3 / 77"); expect(html).toContain("积分 / 百万视频 Token"); expect(html).not.toContain("输入"); }); test("missing and unconfigured costs never fall back to sale prices or zero", () => { for (const costPricing of [undefined, { ...tier.costPricing!, configured: false }]) { const html = render([{ ...tier, costPricing }]); expect(html).toContain("未配置成本"); expect(html).toContain("未配置成本 / 99"); expect(html).toContain("利润率 —"); } }); test("explicit free costs remain zero even if sale pricing is unconfigured", () => { const html = renderToStaticMarkup(); expect(html).toContain("0 / 未配置售价"); expect(html).toContain("利润率 —"); expect(html).not.toContain("未配置成本"); }); test("invalid or missing numeric costs are not silently displayed as free", () => { for (const unitPriceMicrocredits of [NaN, Infinity, -1, undefined]) { const html = render([{ ...tier, costPricing: { ...tier.costPricing!, unitPriceMicrocredits: unitPriceMicrocredits as number } }]); expect(html).toContain("— / 99"); expect(html).toContain("利润率 —"); } }); test("specification labels prefer canonical selectors and additional tiers remain expandable", () => { const html = render(Array.from({ length: 4 }, (_, i) => ({ ...tier, id: String(i), selector: { operation: "image_to_video", vquality: "1080p", videoSeconds: "10" }, resolution: "720p", videoSeconds: 5 }))); expect(html).toContain("图生视频 / 1080P / 10 秒"); expect(html).not.toContain("720P"); expect(html).toContain(" { expect(render([{ ...tier, enabled: false }])).toContain("无已启用规格"); expect(render([])).toContain("无已启用规格"); }); test("model list uses the cost component without gating it on user prices", async () => { const source = await Bun.file(new URL("../src/pages/admin/components/channel-model-manager.tsx", import.meta.url)).text(); expect(source).toContain('title: "规格成本价 / 销售价 / 利润率"'); expect(source).toContain(""); expect(source).not.toContain("billingSummary(item)"); }); test("zero revenue is undefined, free costs have 100% margin and losses stay negative", () => { expect(render([{ ...tier, unitPriceMicrocredits: 0 }])).toContain("利润率 —"); expect(render([{ ...tier, unitPriceMicrocredits: 1_000_000 }])).toContain("利润率 -25%"); expect(render([{ ...tier, costPricing: { ...tier.costPricing!, unitPriceMicrocredits: 0 } }])).toContain("利润率 100%"); });