import { expect, test } from "@playwright/test"; import { isImageGenerationModelDescriptor, isStillImageGenerationModelDescriptor, isVideoModelDescriptor, } from "@/lib/ai/model-classification"; import { getStyleProModelAvailability } from "@/lib/style-pro/model-availability"; import type { AiProvider } from "@/lib/db/schema"; function styleProEntry(overrides?: { providerType?: AiProvider["type"]; modelId?: string; supportsVision?: boolean; healthStatus?: "healthy" | "degraded" | "auto_disabled"; healthAutoDisabledAt?: Date | null; healthObservationStartedAt?: Date | null; }) { return { model: { displayName: "Test Image Model", modelId: overrides?.modelId || "test-image-model", supportsVision: overrides?.supportsVision ?? true, capabilities: null, defaultParams: null, isEnabled: true, healthStatus: overrides?.healthStatus || "healthy", healthAutoDisabledAt: overrides?.healthAutoDisabledAt || null, healthObservationStartedAt: overrides?.healthObservationStartedAt || null, }, provider: { type: overrides?.providerType || "openai-image", }, }; } test.describe("model classification", () => { test("keeps multi-reference BananaImage models classified as image models", () => { const descriptor = { providerType: "bananaimage", modelId: "gemini-3-pro-image-preview", capabilities: { supportedModes: [ "text-to-image", "image-to-image", "reference-images", ], }, supportsImageGen: true, }; expect(isVideoModelDescriptor(descriptor)).toBe(false); expect(isImageGenerationModelDescriptor(descriptor)).toBe(true); }); test("still recognizes explicit video generation modes", () => { expect( isVideoModelDescriptor({ providerType: "custom", modelId: "custom-video-model", capabilities: { supportedModes: ["text-to-video"] }, }) ).toBe(true); }); test("uses one image-only rule for canvas and Style Pro model pools", () => { expect( isStillImageGenerationModelDescriptor({ providerType: "ruxa-image", modelId: "google/nano-banana-2", supportsImageGen: true, }) ).toBe(true); expect( isStillImageGenerationModelDescriptor({ providerType: "ruxa-video", modelId: "kling-video", supportsImageGen: true, }) ).toBe(false); }); test("prevents reference-incompatible models from being selected in Style Pro", () => { const availability = getStyleProModelAvailability( styleProEntry({ supportsVision: false }) ); expect(availability.selectable).toBe(false); expect(availability.availabilityLabel).toBe("仅文生图"); }); test("temporarily disables degraded models in Style Pro", () => { const availability = getStyleProModelAvailability( styleProEntry({ healthStatus: "degraded", healthObservationStartedAt: new Date(), }) ); expect(availability.selectable).toBe(false); expect(availability.availabilityLabel).toBe("暂不可用"); }); test("keeps known reference-capable BFL models selectable", () => { const availability = getStyleProModelAvailability( styleProEntry({ providerType: "bfl-image", modelId: "flux-2-klein-4b", supportsVision: false, }) ); expect(availability.selectable).toBe(true); }); });