mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-21 22:52:19 +03:00
Merged — locally validated (15/15 focused tests, typecheck:core clean, file-size/changelog gates green) after resolving base-drift against #10654 (both landed today, same file — combined import block, no logical conflict). Thanks!
162 lines
5.9 KiB
TypeScript
162 lines
5.9 KiB
TypeScript
/**
|
|
* Guards for the shared probe-target resolution (#8411).
|
|
*
|
|
* The scheduler sweep and the "Test All" endpoint used to carry their own copy of the probe
|
|
* target and batch size. They now share src/lib/proxyHealth/probeTarget.ts, so these tests pin
|
|
* two things: that the defaults still match what both call sites hardcoded before, and that no
|
|
* environment value can produce a batch size that would hang the sweep loop.
|
|
*/
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
DEFAULT_PROBE_CONCURRENCY,
|
|
DEFAULT_PROBE_STAGGER_MS,
|
|
DEFAULT_PROBE_TARGET,
|
|
MAX_PROBE_CONCURRENCY,
|
|
MAX_PROBE_STAGGER_MS,
|
|
resolveProbeConcurrency,
|
|
resolveProbeStaggerMs,
|
|
resolveProbeTarget,
|
|
staggerDelayMs,
|
|
waitForProbeSlot,
|
|
} from "../../src/lib/proxyHealth/probeTarget.ts";
|
|
|
|
// ─── the loop-safety invariant ─────────────────────────
|
|
|
|
test("no environment value can yield a batch size below 1", () => {
|
|
// `for (i = 0; i < n; i += concurrency)` never advances at 0 and walks backwards below it,
|
|
// so this is the one property that turns a bad config into a hung sweep rather than a slow one.
|
|
const hostile = [
|
|
"0",
|
|
"-1",
|
|
"-9999",
|
|
"",
|
|
" ",
|
|
"abc",
|
|
"NaN",
|
|
"Infinity",
|
|
"-Infinity",
|
|
"1e-9",
|
|
"0.4",
|
|
"null",
|
|
undefined,
|
|
];
|
|
for (const raw of hostile) {
|
|
const resolved = resolveProbeConcurrency({ PROXY_HEALTH_TEST_CONCURRENCY: raw });
|
|
assert.ok(
|
|
Number.isInteger(resolved) && resolved >= 1,
|
|
`concurrency ${JSON.stringify(raw)} resolved to ${resolved}`
|
|
);
|
|
}
|
|
});
|
|
|
|
// ─── defaults: what both call sites hardcoded before ───
|
|
|
|
test("the defaults reproduce the previous hardcoded values", () => {
|
|
assert.equal(resolveProbeTarget({}), DEFAULT_PROBE_TARGET);
|
|
assert.equal(DEFAULT_PROBE_TARGET, "https://httpbin.org/ip");
|
|
assert.equal(resolveProbeConcurrency({}), DEFAULT_PROBE_CONCURRENCY);
|
|
assert.equal(DEFAULT_PROBE_CONCURRENCY, 10);
|
|
assert.equal(resolveProbeStaggerMs({}), DEFAULT_PROBE_STAGGER_MS);
|
|
});
|
|
|
|
// ─── target ────────────────────────────────────────────
|
|
|
|
test("an operator-supplied target wins", () => {
|
|
assert.equal(
|
|
resolveProbeTarget({ PROXY_HEALTH_TEST_URL: "https://probe.internal/ping" }),
|
|
"https://probe.internal/ping"
|
|
);
|
|
});
|
|
|
|
test("an empty target falls back, a whitespace one is left untouched", () => {
|
|
assert.equal(resolveProbeTarget({ PROXY_HEALTH_TEST_URL: "" }), DEFAULT_PROBE_TARGET);
|
|
// Whitespace is truthy, so it reached the probe before this refactor. Trimming it here would
|
|
// silently repair a broken deployment — a behavior change this change is not entitled to make.
|
|
assert.equal(resolveProbeTarget({ PROXY_HEALTH_TEST_URL: " " }), " ");
|
|
});
|
|
|
|
// ─── concurrency ───────────────────────────────────────
|
|
|
|
test("concurrency honours a sane value and is capped", () => {
|
|
assert.equal(resolveProbeConcurrency({ PROXY_HEALTH_TEST_CONCURRENCY: "3" }), 3);
|
|
assert.equal(
|
|
resolveProbeConcurrency({ PROXY_HEALTH_TEST_CONCURRENCY: "10000" }),
|
|
MAX_PROBE_CONCURRENCY
|
|
);
|
|
});
|
|
|
|
test("an unparseable concurrency falls back instead of yielding NaN", () => {
|
|
assert.equal(
|
|
resolveProbeConcurrency({ PROXY_HEALTH_TEST_CONCURRENCY: "abc" }),
|
|
DEFAULT_PROBE_CONCURRENCY
|
|
);
|
|
});
|
|
|
|
// ─── stagger ───────────────────────────────────────────
|
|
|
|
test("the stagger step is clamped to its bounds", () => {
|
|
assert.equal(resolveProbeStaggerMs({ PROXY_HEALTH_TEST_STAGGER_MS: "250" }), 250);
|
|
assert.equal(resolveProbeStaggerMs({ PROXY_HEALTH_TEST_STAGGER_MS: "0" }), 0);
|
|
assert.equal(resolveProbeStaggerMs({ PROXY_HEALTH_TEST_STAGGER_MS: "-50" }), 0);
|
|
assert.equal(
|
|
resolveProbeStaggerMs({ PROXY_HEALTH_TEST_STAGGER_MS: "999999" }),
|
|
MAX_PROBE_STAGGER_MS
|
|
);
|
|
});
|
|
|
|
test("an unparseable stagger falls back instead of yielding NaN", () => {
|
|
const resolved = resolveProbeStaggerMs({ PROXY_HEALTH_TEST_STAGGER_MS: "later" });
|
|
assert.equal(resolved, DEFAULT_PROBE_STAGGER_MS);
|
|
assert.ok(Number.isFinite(resolved));
|
|
});
|
|
|
|
// ─── delay computation ─────────────────────────────────
|
|
|
|
test("the head of a batch is never delayed", () => {
|
|
assert.equal(staggerDelayMs(0, 100), 0);
|
|
});
|
|
|
|
test("delays grow strictly with the position in the batch", () => {
|
|
const step = 100;
|
|
for (let i = 1; i < 10; i++) {
|
|
assert.equal(staggerDelayMs(i, step), i * step);
|
|
assert.ok(staggerDelayMs(i, step) > staggerDelayMs(i - 1, step));
|
|
}
|
|
});
|
|
|
|
test("a zero or negative step inserts no wait at all", () => {
|
|
for (const step of [0, -1]) {
|
|
for (let i = 0; i < 5; i++) {
|
|
assert.equal(staggerDelayMs(i, step), 0);
|
|
}
|
|
}
|
|
});
|
|
|
|
test("a negative index cannot produce a negative wait", () => {
|
|
assert.equal(staggerDelayMs(-1, 100), 0);
|
|
});
|
|
|
|
// ─── the wait itself ───────────────────────────────────
|
|
|
|
test("the head of a batch is not held back at all", async () => {
|
|
const before = Date.now();
|
|
await waitForProbeSlot(0, 100);
|
|
// No timer is armed for slot 0, so this resolves on the microtask queue. The assertion is
|
|
// deliberately loose: it proves no ~100ms timer fired, not a precise scheduler timing.
|
|
assert.ok(Date.now() - before < 50, "slot 0 must not wait");
|
|
});
|
|
|
|
test("a later slot is actually held back", async () => {
|
|
const before = Date.now();
|
|
await waitForProbeSlot(2, 30);
|
|
assert.ok(Date.now() - before >= 55, "slot 2 must wait about two steps");
|
|
});
|
|
|
|
test("a disabled step holds nobody back", async () => {
|
|
const before = Date.now();
|
|
await Promise.all([waitForProbeSlot(5, 0), waitForProbeSlot(9, 0)]);
|
|
assert.ok(Date.now() - before < 50, "a zero step must insert no wait");
|
|
});
|