64 lines
4.6 KiB
TypeScript
64 lines
4.6 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import { agentImageProviderHarness as harness } from "../helpers/agent-image-provider-harness";
|
|
import { agentImageFailurePolicy } from "@/lib/ai/runtime/agent-image-failover";
|
|
|
|
|
|
test("automatic image generation switches within the same provider and skips a paused model on the next task", async () => {
|
|
const h = harness({ generate: id => { if (id === "primary") throw Object.assign(new Error("channel unavailable"), { status: 503 }); return ""; } });
|
|
const result = await h.generate(h.request);
|
|
expect(result.modelRef).toBe("backup"); expect(h.calls.map(c => c.id)).toEqual(["primary", "backup"]);
|
|
expect([...h.paused]).toEqual(["primary"]);
|
|
expect(h.calls[1].imageParams).toMatchObject({ imageSize: "2K", aspectRatio: "3:4", negativePrompt: "no text", seed: 0, watermark: false });
|
|
await h.generate(h.request);
|
|
expect(h.calls.map(c => c.id)).toEqual(["primary", "backup", "backup"]);
|
|
});
|
|
|
|
test("a pinned model failure never calls any backup or silently substitutes a missing model", async () => {
|
|
const h = harness({ generate: () => { throw Object.assign(new Error("invalid authentication"), { status: 401 }); } });
|
|
await expect(h.generate({ ...h.request, agentImageMode: "pinned", allowFailover: true })).rejects.toThrow();
|
|
expect(h.calls.map(c => c.id)).toEqual(["primary"]);
|
|
await expect(h.generate({ ...h.request, agentImageMode: "pinned", model: "missing" })).rejects.toThrow("指定的图片模型不可用");
|
|
expect(h.calls).toHaveLength(1);
|
|
});
|
|
|
|
test("a partly successful batch keeps its delivered images and only generates the missing count", async () => {
|
|
const h = harness({ generate: (id, _n, call) => { if (id === "primary" && call === 2) throw new Error("VIP98K task failed"); return ``; } });
|
|
const result = await h.generate({ ...h.request, imageCount: 3 });
|
|
expect(h.calls.map(c => c.id)).toEqual(["primary", "primary", "backup", "backup"]);
|
|
expect(result.images).toHaveLength(3);
|
|
expect(result.images.map((i: any) => i.modelRef)).toEqual(["primary", "backup", "backup"]);
|
|
expect(result.images[0].originalUrl).toContain("primary-1");
|
|
});
|
|
|
|
test("if every backup fails, previously delivered images are retained", async () => {
|
|
const h = harness({ generate: (_id, _n, call) => { if (call > 1) throw new Error("VIP98K task failed"); return ""; } });
|
|
const result = await h.generate({ ...h.request, imageCount: 3 });
|
|
expect(result.images).toHaveLength(1); expect(result.images[0].originalUrl).toContain("keep.png");
|
|
});
|
|
|
|
test("a submitted task with an unknown outcome is not submitted again through a backup", async () => {
|
|
const h = harness({ generate: () => { throw new Error("VIP98K task timed out; it may still be running"); } });
|
|
await expect(h.generate(h.request)).rejects.toThrow();
|
|
expect(h.calls.map(c => c.id)).toEqual(["primary"]); expect(h.paused.size).toBe(0);
|
|
expect(agentImageFailurePolicy(Object.assign(new Error("timeout"), { providerOutcomeUnknown: true })).retry).toBe(false);
|
|
expect(agentImageFailurePolicy(new Error("VIP98K task failed: timeout")).retry).toBe(true);
|
|
});
|
|
|
|
test("content restrictions and user input problems cannot be bypassed by switching services", () => {
|
|
for (const error of [new Error("content policy rejection"), Object.assign(new Error("bad input"), { status: 400 }), new Error("用户积分不足")]) expect(agentImageFailurePolicy(error)).toEqual({ retry: false, pause: null });
|
|
});
|
|
|
|
|
|
test("automatic fallback respects requested dimensions, disabled candidates and the user's available credit", async () => {
|
|
const models = [
|
|
{ id: "primary", creditCost: 1, supportedImageSizes: ["2K"] },
|
|
{ id: "wrong-size", creditCost: 1, supportedImageSizes: ["1K"] },
|
|
{ id: "too-expensive", creditCost: 10, supportedImageSizes: ["2K"] },
|
|
{ id: "disabled-health", creditCost: 1, supportedImageSizes: ["2K"], healthStatus: "auto_disabled" },
|
|
{ id: "backup", creditCost: 2, supportedImageSizes: ["2K"] },
|
|
].map(({ id, healthStatus, ...config }) => ({ model: { id, modelId: id, displayName: id, isEnabled: true, healthStatus, supportsImageGen: true, supportsVision: true, imageGenConfig: config }, provider: { type: "openai-image", isEnabled: true } }));
|
|
const h = harness({ models, generate: id => { if (id === "primary") throw new Error("VIP98K task failed"); return ""; } });
|
|
const result = await h.generate({ ...h.request, agentImageMaxCreditCost: 3 });
|
|
expect(result.modelRef).toBe("backup"); expect(h.calls.map(c => c.id)).toEqual(["primary", "backup"]);
|
|
});
|