import { describe, expect, it } from "vitest"; import { buildPopiserverPriceQuoteRequest, getQuotedPointAmount, } from "@/utils/composerPricing"; import type { SelectedModel } from "@/types"; import type { GenerationNodeConfig } from "@/utils/generationConfig"; import type { ComposerPricingContext } from "@/utils/composerPricingContext"; function model(overrides: Partial = {}): SelectedModel { return { provider: "popiserver", modelId: "popi-image", displayName: "Popi Image", capabilities: ["text-to-image"], ...overrides, }; } function draft(overrides: Partial = {}): GenerationNodeConfig { return { prompt: "prompt", selectedModel: model(), inputImages: [], imageCount: 1, referenceSubjectList: [], parameters: {}, extraTaskParams: {}, ...overrides, }; } function context(overrides: Partial & { config?: GenerationNodeConfig } = {}): ComposerPricingContext { return { config: overrides.config ?? draft(), capability: overrides.capability ?? "image", media: overrides.media ?? {}, inputVideoDurationSeconds: overrides.inputVideoDurationSeconds ?? 0, }; } describe("composerPricing", () => { it("builds PopiServer estimate payloads with provider model aliases", () => { const request = buildPopiserverPriceQuoteRequest(context({ inputVideoDurationSeconds: 12, config: draft({ selectedModel: model({ modelId: "123", displayName: "Token billed video", billingMethod: 2, aiModelCode: "video-code", aiModelCodeAlias: "video-alias", }), parameters: { duration: 8 }, }), })); expect(request).toEqual({ source: "estimate", payload: { model_name: "video-alias", estimation_type: "auto", parameters: { duration: 8, inputPrompt: "prompt", input_video_duration_seconds: 12, }, imageCount: 1, input_video_duration_seconds: 12, }, }); }); it("skips task price quote requests for llm and Seedance estimates", () => { expect(buildPopiserverPriceQuoteRequest(context({ capability: "llm", config: draft(), }))).toBeNull(); expect(buildPopiserverPriceQuoteRequest(context({ capability: "video", config: draft({ selectedModel: model({ displayName: "Seedance" }), }), }))).toMatchObject({ source: "estimate" }); }); it("uses PopiServer estimate only for billingMethod 2 or Seedance models", () => { expect(buildPopiserverPriceQuoteRequest(context({ config: draft({ selectedModel: model({ billingMethod: 2 }), }) }))).toMatchObject({ source: "estimate" }); expect(buildPopiserverPriceQuoteRequest(context({ config: draft({ selectedModel: model({ billingMethod: 1, aiModelId: 32, aiModelCode: "image-code", type: 1, subType: 101, }), }) }))).toMatchObject({ source: "task-price" }); expect(buildPopiserverPriceQuoteRequest(context({ capability: "video", config: draft({ selectedModel: model({ displayName: "Seedance Pro", billingMethod: 1, }), }), }))).toMatchObject({ source: "estimate" }); }); it("builds unified PopiServer quote requests", () => { const estimateContext = context({ config: draft({ selectedModel: model({ billingMethod: 2 }), }) }); expect(buildPopiserverPriceQuoteRequest(estimateContext)).toMatchObject({ source: "estimate", payload: { estimation_type: "auto", }, }); expect(buildPopiserverPriceQuoteRequest(context({ config: draft({ selectedModel: model({ billingMethod: 1, aiModelId: 32, }), }) }))).toMatchObject({ source: "task-price", }); expect(buildPopiserverPriceQuoteRequest(context())).toBeNull(); expect(getQuotedPointAmount({ data: { points: "36" } })).toBe(36); expect(getQuotedPointAmount({ data: { points: "bad" } })).toBeNull(); }); });