mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-21 06:12:17 +03:00
Correct and well-traced: `rate_limited_until` is TEXT but one write path stores a bare epoch that SQLite coerces to `"1781696905131.0"`, which `new Date()` alone reads as `NaN` — so a still-cooling connection looked available and combo fed it traffic that could only come back 429. Routing both readers through the existing tolerant normalizer is the minimal fix, and keeping unreadable values fail-open is the right default. The #3954/#3995 lineage explains exactly why this function never inherited the normalization. This PR also carries the batch's file-size rebaseline, since it merges first and the ceiling has to cover every intermediate state. --- Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the rest of this batch — zero conflicts between them. - `typecheck:core` clean; `check:changelog-integrity` OK - complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline - 86 focused assertions green across the batch's 10 unit test files, plus 16/16 on the v1 plugin option schema and 16/16 on the v2 option tests - `check-file-size` rebaselined for this batch's real growth (annotation `_rebaseline_2026_09_11_mergebatch_v3851_maxmad_opencode`, landed on #13141). `open-sse/utils/stream.ts` was deliberately left frozen: it is already 3115 > 3098 on the pure tip with zero contribution from this batch. ⚠️ base-red inherited: #12732 — `Docs Gates`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and the `stream.ts` freeze above). None of them touch these diffs. Thanks @maxmad64bis.
64 lines
2.5 KiB
TypeScript
64 lines
2.5 KiB
TypeScript
/**
|
|
* Regression: `hasFutureRateLimitUntil` parses with `new Date(String(value))`
|
|
* alone, so a numeric-epoch string from the TEXT `rate_limited_until` column
|
|
* (e.g. a `${Date.now()}.0`-shaped value, cf. #3954) yields NaN and the
|
|
* still-cooling connection is never skipped (fail-open → guaranteed upstream
|
|
* 429). `formatRetryAfter` has the same blind spot and renders
|
|
* "reset after NaNs".
|
|
*/
|
|
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
const { hasFutureRateLimitUntil } =
|
|
await import("../../open-sse/services/combo/comboPredicates.ts");
|
|
const { formatRetryAfter } = await import("../../open-sse/services/accountFallback.ts");
|
|
|
|
const HOUR = 3_600_000;
|
|
|
|
test("hasFutureRateLimitUntil: future numeric-epoch string is future", () => {
|
|
assert.equal(hasFutureRateLimitUntil(`${Date.now() + HOUR}.0`), true);
|
|
});
|
|
|
|
test("hasFutureRateLimitUntil: future numeric epoch number is future", () => {
|
|
assert.equal(hasFutureRateLimitUntil(Date.now() + HOUR), true);
|
|
});
|
|
|
|
test("hasFutureRateLimitUntil: past numeric-epoch string is not future", () => {
|
|
assert.equal(hasFutureRateLimitUntil(String(Date.now() - HOUR)), false);
|
|
});
|
|
|
|
test("hasFutureRateLimitUntil: future ISO string is future (unchanged)", () => {
|
|
assert.equal(hasFutureRateLimitUntil(new Date(Date.now() + HOUR).toISOString()), true);
|
|
});
|
|
|
|
test("hasFutureRateLimitUntil: empty/null/undefined/blank is not future (unchanged)", () => {
|
|
assert.equal(hasFutureRateLimitUntil(""), false);
|
|
assert.equal(hasFutureRateLimitUntil(null), false);
|
|
assert.equal(hasFutureRateLimitUntil(undefined), false);
|
|
assert.equal(hasFutureRateLimitUntil(" "), false);
|
|
});
|
|
|
|
test("hasFutureRateLimitUntil: garbage is not future (unchanged)", () => {
|
|
assert.equal(hasFutureRateLimitUntil("abc"), false);
|
|
});
|
|
|
|
test("hasFutureRateLimitUntil: non-string values never throw (narrowing)", () => {
|
|
assert.equal(hasFutureRateLimitUntil(true), false);
|
|
assert.equal(hasFutureRateLimitUntil({}), false);
|
|
assert.equal(hasFutureRateLimitUntil([]), false);
|
|
});
|
|
|
|
test("formatRetryAfter: future numeric-epoch string renders a duration", () => {
|
|
const rendered = formatRetryAfter(`${Date.now() + HOUR}.0`);
|
|
assert.match(rendered, /^reset after \d/);
|
|
assert.doesNotMatch(rendered, /NaN/);
|
|
});
|
|
|
|
test("formatRetryAfter: past numeric-epoch string renders reset after 0s", () => {
|
|
assert.equal(formatRetryAfter(String(Date.now() - HOUR)), "reset after 0s");
|
|
});
|
|
|
|
test("formatRetryAfter: garbage renders empty (unknown, not expired)", () => {
|
|
assert.equal(formatRetryAfter("abc"), "");
|
|
});
|