Files
OmniRoute/tests/unit/proxy-health-refusal-memory.test.ts
Dizzle 29d66cbf8c feat(proxies): stop re-serving a proxy that just failed (#13578)
Behind the new `PROXY_SKIP_RECENTLY_FAILED` flag (default off), pool rotation and the opencode account rotation remember a proxy that just failed (refused probe or 429) and skip it for a doubling cooldown instead of re-serving it immediately.

Maintainer rework before merge (kept the idea, no default behavior change):
- The original was on by default and re-queried the DB on every request while a member was set aside; selection now caches a refusal sequence number and re-runs the cascade once per set-aside event.
- `src/lib/db` no longer imports the heavy dispatcher for key normalization (a parity test guarantees the same key as `proxyConfigToUrl()`); `.env.example` and `ENVIRONMENT.md` document the default as false.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 18:09:02 -03:00

65 lines
2.6 KiB
TypeScript

import test, { mock } from "node:test";
import assert from "node:assert/strict";
// The TCP reachability probe already runs for every proxied request. With the opt-in
// PROXY_SKIP_RECENTLY_FAILED flag on, its verdict feeds proxy selection: a refused probe sets
// the proxy aside, a successful one takes it back. With the flag off nothing is written.
const health = await import("../../src/lib/proxyHealth.ts");
const memory = await import("../../open-sse/utils/proxyRefusalMemory.ts");
const PROXY_URL = "http://10.7.0.1:8080";
const KEY = memory.proxyEgressKey(PROXY_URL);
test.beforeEach(() => {
memory.__resetProxyRefusalMemoryForTesting();
health.invalidateProxyHealth(PROXY_URL);
process.env.PROXY_SKIP_RECENTLY_FAILED = "true";
});
test.afterEach(() => {
mock.timers.reset();
health.__setProxyHealthTcpCheckForTesting(null);
delete process.env.PROXY_SKIP_RECENTLY_FAILED;
});
test("a refused probe sets the proxy aside for 60 s, then 120 s on a repeat", async () => {
mock.timers.enable({ apis: ["Date"], now: 1_800_000_000_000 });
health.__setProxyHealthTcpCheckForTesting(async () => false);
assert.equal(await health.isProxyReachable(PROXY_URL), false);
const first = Date.now();
assert.equal(memory.isProxyAvoided(KEY, first + 59_999), true);
assert.equal(memory.isProxyAvoided(KEY, first + 60_000), false);
mock.timers.tick(61_000);
health.invalidateProxyHealth(PROXY_URL);
assert.equal(await health.isProxyReachable(PROXY_URL), false);
const second = Date.now();
assert.equal(memory.isProxyAvoided(KEY, second + 119_999), true);
assert.equal(memory.isProxyAvoided(KEY, second + 120_000), false);
});
test("a probe that answers again ends the period", async () => {
health.__setProxyHealthTcpCheckForTesting(async () => false);
await health.isProxyReachable(PROXY_URL);
assert.equal(memory.isProxyAvoided(KEY), true);
health.invalidateProxyHealth(PROXY_URL);
health.__setProxyHealthTcpCheckForTesting(async () => true);
assert.equal(await health.isProxyReachable(PROXY_URL), true);
assert.equal(memory.isProxyAvoided(KEY), false);
});
test("with the flag at its default (off) a refused probe writes nothing", async () => {
delete process.env.PROXY_SKIP_RECENTLY_FAILED;
health.__setProxyHealthTcpCheckForTesting(async () => false);
assert.equal(await health.isProxyReachable(PROXY_URL), false);
assert.equal(memory.__proxyRefusalMemorySizeForTesting(), 0);
});
test("a malformed proxy URL writes nothing", async () => {
assert.equal(await health.isProxyReachable("not a url"), false);
assert.equal(memory.__proxyRefusalMemorySizeForTesting(), 0);
});