Files
9router/tests/unit/request-scoped-fallback.test.js
luulam f0adfb205a feat(dashboard): per-key model restrictions, pin header routing, combo side-panel picker
- Endpoint: per-API-key model allowlist (schema v3) enforced on chat (403)
  and /v1/models; Full-access toggle + multi-select picker in Keys UI.
- Providers: honor x-connection-id in /v1/chat/completions — pinned requests
  no longer rotate to another account on failure.
- Providers: strategy saves merge into stored enabled:false override;
  Test All groups match grid sections; 1-by-1 skips disabled connections.
- Dashboard: provider-card toggle syncs from server on failure; grid toggles
  always visible; connection rows get clear-✕ for stale error banners.
- Combo editor: on desktop (xl+) the Add-Model picker opens as a floating
  side panel beside the untouched combo popup instead of stacking on top;
  mobile keeps the full-screen overlay.
- Long API-key overflow fixed in key rows + provider model sections.
2026-08-27 09:26:17 +07:00

42 lines
1.7 KiB
JavaScript

import { describe, it, expect } from "vitest";
import { checkFallbackError, getQuotaCooldown } from "../../open-sse/services/accountFallback.js";
describe("checkFallbackError — request-scoped errors (P2)", () => {
it("classifies context-overflow messages as request-scoped: no fallback, no cooldown", () => {
const msgs = [
"This model's maximum context length is 16385 tokens",
"prompt is too long: 250000 tokens > 200000 maximum",
"input is too long for requested model",
"Invalid parameter: max_tokens exceed model limit",
"context_length_exceeded",
"Please reduce the length of the messages",
"Your request is too large for the context window",
];
for (const message of msgs) {
const r = checkFallbackError(400, message);
expect(r.shouldFallback, message).toBe(false);
expect(r.requestScoped, message).toBe(true);
expect(r.cooldownMs).toBe(0);
}
});
it("does NOT classify generic 4xx as request-scoped (legacy transient path preserved)", () => {
const r = checkFallbackError(400, "Invalid value for 'temperature'");
expect(r.requestScoped).toBeUndefined();
expect(r.shouldFallback).toBe(true);
expect(r.cooldownMs).toBeGreaterThan(0);
});
it("rate-limit text still backs off with fallback", () => {
const r = checkFallbackError(429, "rate limit exceeded", 0);
expect(r.shouldFallback).toBe(true);
expect(r.cooldownMs).toBe(getQuotaCooldown(1));
});
it("no credentials still falls back with long cooldown", () => {
const r = checkFallbackError(403, "no credentials found for account");
expect(r.shouldFallback).toBe(true);
expect(r.cooldownMs).toBe(2 * 60 * 1000);
});
});