import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { DEFAULT_BILLING_PRICE_RULES, ensureDefaultBillingPriceRules } from "@/lib/server/billing-catalog"; import { quoteGenerationCharge } from "@/lib/server/billing-service"; import { createBillingPriceRule, getBillingPriceRule, updateBillingPriceTierMultiplier } from "@/lib/server/billing-store"; let runtimeDir = ""; describe("default billing catalog refresh", () => { beforeEach(async () => { runtimeDir = await mkdtemp(join(tmpdir(), "zhinian-billing-seed-")); vi.stubEnv("ZHINIAN_RUNTIME_DIR", runtimeDir); vi.stubEnv("ZHINIAN_DATA_BACKEND", "local"); }); afterEach(async () => { vi.unstubAllEnvs(); await rm(runtimeDir, { recursive: true, force: true }); }); it("keeps a saved tier multiplier across catalog refresh and quotation", async () => { await ensureDefaultBillingPriceRules(); await updateBillingPriceTierMultiplier({ ruleId: "base-seedream-5-0-pro", dimensionKey: "size", tierValue: "1.5K", markupMultiplier: 1.3 }); const quote = await quoteGenerationCharge({ provider: "seedream", capability: "image.generate", reqKey: "doubao-seedream-5-0-pro-260628", requestPayload: { settings: { size: "1.5K" } }, usageContext: { source: "platform", accountId: "user-1", displayName: "测试用户", organizationId: "org-1" } }); expect(quote).toMatchObject({ standardUnitPriceFen: 30, markupMultiplier: 1.3, amountFen: 39 }); const saved = await getBillingPriceRule("base-seedream-5-0-pro"); const tiers = saved?.parameterDimensions?.find((dimension) => dimension.key === "size")?.tiers; expect(tiers?.find((tier) => tier.value === "1.5K")?.markupMultiplier).toBe(1.3); expect(tiers?.find((tier) => tier.value === "2K")?.markupMultiplier).toBe(1.2); }); it("takes current default definitions while retaining valid matching tier markups", async () => { const candidate = DEFAULT_BILLING_PRICE_RULES.find((rule) => rule.id === "base-seedream-5-0-pro")!; const size = candidate.parameterDimensions!.find((dimension) => dimension.key === "size")!; await createBillingPriceRule({ ...candidate, standardUnitPriceFen: 5, markupMultiplier: 1.7, parameterDimensions: [{ ...size, label: "旧分辨率", tiers: [ { ...size.tiers[0], standardFactor: 99, markupMultiplier: 0 }, { ...size.tiers[1], standardFactor: 99, markupMultiplier: 1.4 }, { value: "3K", label: "旧档位", standardFactor: 9, markupMultiplier: 3, enabled: true } ] }] }); await ensureDefaultBillingPriceRules(); const saved = await getBillingPriceRule(candidate.id!); expect(saved?.standardUnitPriceFen).toBe(30); expect(saved?.markupMultiplier).toBe(1.7); const dimension = saved?.parameterDimensions?.find((item) => item.key === "size"); expect(dimension?.label).toBe("分辨率"); expect(dimension?.tiers.map((tier) => tier.value)).toEqual(["1K", "1.5K", "2K"]); expect(dimension?.tiers.map((tier) => [tier.standardFactor, tier.markupMultiplier])).toEqual([ [1, 1.2], [1, 1.4], [2, 1.2] ]); }); });