48 lines
2.5 KiB
TypeScript
48 lines
2.5 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
||
import {
|
||
getUserFacingImageGenerationErrorMessage,
|
||
getUserFacingRequestErrorMessage,
|
||
isProviderBalanceError,
|
||
isRateLimitError,
|
||
} from "@/lib/request-error";
|
||
import { isImageFallbackModelBalanceCoolingDown } from "@/lib/ai/runtime/image-fallback-balance-cooldown";
|
||
|
||
test("a bare HTTP 429 is rate limiting, not insufficient balance", () => {
|
||
const error = Object.assign(new Error("Too many requests (HTTP 429)"), { status: 429 });
|
||
expect(isProviderBalanceError(error)).toBe(false);
|
||
expect(isRateLimitError(error)).toBe(true);
|
||
expect(getUserFacingImageGenerationErrorMessage(error)).toContain("请求过于频繁");
|
||
expect(getUserFacingRequestErrorMessage(error)).toContain("请求过于频繁");
|
||
});
|
||
|
||
test("an explicit provider balance error takes precedence over HTTP 429", () => {
|
||
const error = Object.assign(new Error("余额不足,请点击充值页面续费(HTTP 429)"), { status: 429 });
|
||
expect(isProviderBalanceError(error)).toBe(true);
|
||
expect(isRateLimitError(error)).toBe(false);
|
||
expect(getUserFacingImageGenerationErrorMessage(error)).toContain("服务商线路余额");
|
||
expect(getUserFacingImageGenerationErrorMessage(error)).toContain("与您的积分余额无关");
|
||
expect(getUserFacingImageGenerationErrorMessage(error)).not.toMatch(/^积分不足/);
|
||
});
|
||
|
||
test("nested billing details and generic quota limits are kept distinct", () => {
|
||
const nested = { status: 429, message: "request failed", data: { error: { message: "insufficient_balance" } } };
|
||
expect(isProviderBalanceError(nested)).toBe(true);
|
||
expect(isRateLimitError(nested)).toBe(false);
|
||
const usageLimit = new Error("Rate quota exceeded (HTTP 429)");
|
||
expect(isProviderBalanceError(usageLimit)).toBe(false);
|
||
expect(isRateLimitError(usageLimit)).toBe(true);
|
||
expect(isProviderBalanceError(new Error("该模型额度不足(HTTP 429)"))).toBe(true);
|
||
});
|
||
|
||
test("a recent confirmed provider balance failure skips only fallback selection", () => {
|
||
const now = Date.now();
|
||
const model = {
|
||
healthLastEventType: "quota",
|
||
healthLastError: "余额不足(HTTP 429)",
|
||
healthLastFailureAt: new Date(now - 30_000),
|
||
};
|
||
expect(isImageFallbackModelBalanceCoolingDown(model, now)).toBe(true);
|
||
expect(isImageFallbackModelBalanceCoolingDown({ ...model, healthLastFailureAt: new Date(now - 3 * 60 * 60 * 1000) }, now)).toBe(false);
|
||
expect(isImageFallbackModelBalanceCoolingDown({ ...model, healthLastEventType: "success" }, now)).toBe(false);
|
||
});
|