mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 19:02:17 +03:00
* fix: repair five base-red failures on release/v3.8.49 Every PR cut from this branch fails CI on the branch's own breakage. Five distinct causes, none introduced by the PRs that trip over them: 1. dast-smoke / Turbopack build — src/sse/handlers/chat.ts imported PROVIDER_BREAKER_FAILURE_STATUSES twice in one statement. A duplicate import specifier is an ECMAScript syntax error, so the production build never compiled. Introduced by #8258, whose export fix landed on top of an import that already existed. 2. Unit Tests — the #8393 verified-cooldown bypass was renamed exactCooldownVerified -> exactCooldownIsUpstreamReset during the #8254 conflict resolution, which also dropped the flag at the markAccountUnavailable call site entirely. The rename left the test passing the old key (so the flag was silently ignored and a verified upstream reset got clamped back to maxCooldownMs), and the dropped call site meant no real caller set it at all. Align the test on the surviving name, restore the call site, and restore the doc comment explaining #6863 vs #7940. 3. Unit Tests — #8526 added four common.* keys to en.json only, breaking the strict key-parity tests for pt-BR and vi. Translated into all 42 locales. 4. Unit Tests — vi carried 17 __MISSING__ placeholders from #8354 and #8463, and vi is the one locale with a no-placeholder test. Translated. 5. No new ESLint warnings — four suppressed `any`s in tests/unit/combo-routing-engine.test.ts no longer exist, and ESLint exits 2 on stale suppressions. Pruned (271 -> 267); no other entry moved. Also regenerates skills/cli-backup-sync/SKILL.md, which still documented the `backup status` flags #8512 removed — the merge-integrity gate compares the generated output against the tree. Not fixed here: the env/docs contract (NEXT_PUBLIC_OMNIROUTE_BASE_PATH and OMNIROUTE_BACKUP_SCHEDULE_JOB_INTERVAL_MS missing from .env.example), which #8690 already covers, and the quality baselines, which #8686 covers. * fix(quality): keep prettier off the generated SKILL.md files check:agent-skills-sync diffs the generator's output against the tree byte for byte, but lint-staged runs prettier over any staged *.md — and prettier inserts a blank line after the frontmatter that the generator does not emit. Committing a regenerated skill therefore made the gate fail again on the very file that was just brought back in sync. The 44 untouched skills only escape this because they never pass through lint-staged. The generator is the formatter of record for these files, so ignore them. * fix(i18n,quality): drop the stale zh-TW key; raise the auth.ts frozen cap #8463 renamed `oauthModal.googleOAuthWarning` away but left the old key behind in zh-TW, so the "the stale googleOAuthWarning key is GONE from every locale" guard fails on the branch. Removed it. The auth.ts frozen line cap goes 2486 -> 2492. Restoring the dropped exactCooldownIsUpstreamReset call site costs 7 lines, and staging the file makes lint-staged reformat four pre-existing over-100-column lines to prettier's rule — unavoidable without bypassing the hook, which hard rule #10 forbids. The file still sits 12 lines below where the cap was set relative to its actual size.
141 lines
4.4 KiB
TypeScript
141 lines
4.4 KiB
TypeScript
import { describe, it, before } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
describe("recordModelLockoutFailure — exactCooldownMs cap against maxCooldownMs", () => {
|
|
let accountFallback: typeof import("../../open-sse/services/accountFallback.ts");
|
|
|
|
before(async () => {
|
|
accountFallback = await import("../../open-sse/services/accountFallback.ts");
|
|
});
|
|
|
|
it("caps exactCooldownMs against maxCooldownMs when exact exceeds max", () => {
|
|
accountFallback.clearAllModelLockouts();
|
|
|
|
// Use exactCooldownMs=600000 (10min) but maxCooldownMs=300000 (5min)
|
|
const result = accountFallback.recordModelLockoutFailure(
|
|
"openai",
|
|
"conn-1",
|
|
"gpt-4",
|
|
"quota_exhausted",
|
|
429,
|
|
120_000,
|
|
null,
|
|
{ exactCooldownMs: 600_000, maxCooldownMs: 300_000 }
|
|
);
|
|
|
|
assert.ok(result.cooldownMs <= 300_000, `cooldownMs=${result.cooldownMs} should be <= 300000`);
|
|
});
|
|
|
|
it("keeps exactCooldownMs unchanged when it is below maxCooldownMs", () => {
|
|
accountFallback.clearAllModelLockouts();
|
|
|
|
const result = accountFallback.recordModelLockoutFailure(
|
|
"openai",
|
|
"conn-2",
|
|
"gpt-4",
|
|
"quota_exhausted",
|
|
429,
|
|
120_000,
|
|
null,
|
|
{ exactCooldownMs: 30_000, maxCooldownMs: 300_000 }
|
|
);
|
|
|
|
assert.strictEqual(result.cooldownMs, 30_000);
|
|
});
|
|
|
|
it("caps exactCooldownMs for quota_exhausted with default midnight cooldown", () => {
|
|
accountFallback.clearAllModelLockouts();
|
|
|
|
// When exactCooldownMs is not set and reason is quota_exhausted,
|
|
// it uses getMsUntilTomorrow() which could be very large.
|
|
// With maxCooldownMs=300000 it should be capped.
|
|
const result = accountFallback.recordModelLockoutFailure(
|
|
"openai",
|
|
"conn-3",
|
|
"gpt-4",
|
|
"quota_exhausted",
|
|
429,
|
|
120_000,
|
|
null,
|
|
{ maxCooldownMs: 300_000 }
|
|
);
|
|
|
|
assert.ok(result.cooldownMs <= 300_000, `cooldownMs=${result.cooldownMs} should be <= 300000`);
|
|
});
|
|
|
|
it("uses BACKOFF_CONFIG.max as fallback when maxCooldownMs is not provided", () => {
|
|
accountFallback.clearAllModelLockouts();
|
|
|
|
const result = accountFallback.recordModelLockoutFailure(
|
|
"openai",
|
|
"conn-4",
|
|
"gpt-4",
|
|
"rate_limit_exceeded",
|
|
429,
|
|
120_000,
|
|
null,
|
|
{ exactCooldownMs: 300_000 }
|
|
);
|
|
|
|
// When maxCooldownMs is not passed, exact cooldowns are not capped
|
|
// so exactCooldownMs=300000 should be preserved as-is
|
|
assert.strictEqual(result.cooldownMs, 300_000);
|
|
});
|
|
|
|
// #6863 vs #7940 boundary: the same magnitude (~92.5h, the Antigravity reset
|
|
// from #6863) against the same maxCooldownMs (~30min) must resolve two
|
|
// different ways depending on provenance — a SYNTHETIC estimate stays capped
|
|
// (#7940's contract), a caller-VERIFIED upstream reset passes through exactly
|
|
// (#6863's contract). #7980 regressed this by capping both indiscriminately.
|
|
const RESET_6863_MS = 332_848_000; // "Resets in 92h27m28s"
|
|
const CAP_7940_MS = 1_800_000; // 30min operator-configured max
|
|
|
|
it("still caps a SYNTHETIC exactCooldownMs even when it dwarfs maxCooldownMs (#7940)", () => {
|
|
accountFallback.clearAllModelLockouts();
|
|
|
|
const result = accountFallback.recordModelLockoutFailure(
|
|
"antigravity",
|
|
"conn-5",
|
|
"claude-sonnet-4-6",
|
|
"rate_limit",
|
|
429,
|
|
120_000,
|
|
null,
|
|
// No exactCooldownIsUpstreamReset flag — mirrors an un-provenanced/estimated exact
|
|
// cooldown, which must still respect the operator's maxCooldownMs cap.
|
|
{ exactCooldownMs: RESET_6863_MS, maxCooldownMs: CAP_7940_MS }
|
|
);
|
|
|
|
assert.strictEqual(
|
|
result.cooldownMs,
|
|
CAP_7940_MS,
|
|
`synthetic exactCooldownMs must stay capped at maxCooldownMs; got ${result.cooldownMs}`
|
|
);
|
|
});
|
|
|
|
it("passes a VERIFIED exactCooldownMs through uncapped past maxCooldownMs (#6863)", () => {
|
|
accountFallback.clearAllModelLockouts();
|
|
|
|
const result = accountFallback.recordModelLockoutFailure(
|
|
"antigravity",
|
|
"conn-6",
|
|
"claude-sonnet-4-6",
|
|
"rate_limit",
|
|
429,
|
|
120_000,
|
|
null,
|
|
{
|
|
exactCooldownMs: RESET_6863_MS,
|
|
maxCooldownMs: CAP_7940_MS,
|
|
exactCooldownIsUpstreamReset: true,
|
|
}
|
|
);
|
|
|
|
assert.strictEqual(
|
|
result.cooldownMs,
|
|
RESET_6863_MS,
|
|
`verified upstream exactCooldownMs must bypass maxCooldownMs entirely; got ${result.cooldownMs}`
|
|
);
|
|
});
|
|
});
|