mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-13 18:52:18 +03:00
fix(ci): base-reds round 3b — stale sibling tests + mode-pack weight contract
- check-docs-counts-sync.test.ts: drop the imports/subtests of the four helpers #10196 removed from the gate script (readMcpFactsFromSource, listLocalizedDocs, makeRequiredCountsValidator, checkFreeTierInventory) — the new-API tests that #10196 added stay; the file now loads again under the node runner - quota-connection-recovery.test.ts: convert from vitest APIs to node:test — the file lives in tests/unit/*.test.ts (node-runner glob) and the vitest runtime crashes when imported outside vitest, killing the whole shard entry - modePacks.ts: re-normalize all six mode packs to sum 1.0 — #8940 added sessionAvailability: 0.05 to every pack without rebalancing (1.05 total); ratios preserved exactly (÷1.05), so post-normalizeScoringWeights behavior is unchanged; restores the declared sum-to-1.0 contract the 4235 test pins Refs #9985
This commit is contained in:
@@ -14,84 +14,85 @@ export const MODE_PACKS: Record<string, ScoringWeights> = {
|
||||
// Prioritize latency → health. tierPriority replaces 0.05 from stability.
|
||||
// tierAffinity/specificityMatch stay at 0 (manifest-routing-only weights).
|
||||
"ship-fast": {
|
||||
quota: 0.14,
|
||||
health: 0.28,
|
||||
costInv: 0.05,
|
||||
latencyInv: 0.32,
|
||||
taskFit: 0.1,
|
||||
stability: 0.0,
|
||||
tierPriority: 0.05,
|
||||
quota: 0.1333,
|
||||
health: 0.2667,
|
||||
costInv: 0.0476,
|
||||
latencyInv: 0.3048,
|
||||
taskFit: 0.0952,
|
||||
stability: 0,
|
||||
tierPriority: 0.0476,
|
||||
tierAffinity: 0,
|
||||
specificityMatch: 0,
|
||||
contextAffinity: 0.01,
|
||||
sessionAvailability: 0.05,
|
||||
contextAffinity: 0.0095,
|
||||
sessionAvailability: 0.0476,
|
||||
resetWindowAffinity: 0,
|
||||
connectionDensity: 0.05,
|
||||
connectionDensity: 0.0476,
|
||||
},
|
||||
// Prioritize cost. tierPriority replaces 0.05 from stability.
|
||||
"cost-saver": {
|
||||
quota: 0.14,
|
||||
health: 0.19,
|
||||
costInv: 0.37,
|
||||
latencyInv: 0.05,
|
||||
taskFit: 0.1,
|
||||
stability: 0.05,
|
||||
tierPriority: 0.05,
|
||||
quota: 0.1333,
|
||||
health: 0.181,
|
||||
costInv: 0.3524,
|
||||
latencyInv: 0.0476,
|
||||
taskFit: 0.0952,
|
||||
stability: 0.0476,
|
||||
tierPriority: 0.0476,
|
||||
tierAffinity: 0,
|
||||
specificityMatch: 0,
|
||||
contextAffinity: 0.0,
|
||||
sessionAvailability: 0.05,
|
||||
contextAffinity: 0,
|
||||
sessionAvailability: 0.0476,
|
||||
resetWindowAffinity: 0,
|
||||
connectionDensity: 0.05,
|
||||
connectionDensity: 0.0476,
|
||||
},
|
||||
// Prioritize task fitness. tierPriority replaces 0.05 from latencyInv.
|
||||
"quality-first": {
|
||||
quota: 0.1,
|
||||
health: 0.18,
|
||||
costInv: 0.05,
|
||||
latencyInv: 0.05,
|
||||
taskFit: 0.37,
|
||||
stability: 0.15,
|
||||
tierPriority: 0.05,
|
||||
quota: 0.0952,
|
||||
health: 0.1714,
|
||||
costInv: 0.0476,
|
||||
latencyInv: 0.0476,
|
||||
taskFit: 0.3524,
|
||||
stability: 0.1429,
|
||||
tierPriority: 0.0476,
|
||||
tierAffinity: 0,
|
||||
specificityMatch: 0,
|
||||
contextAffinity: 0.0,
|
||||
sessionAvailability: 0.05,
|
||||
contextAffinity: 0,
|
||||
sessionAvailability: 0.0476,
|
||||
resetWindowAffinity: 0,
|
||||
connectionDensity: 0.05,
|
||||
connectionDensity: 0.0476,
|
||||
},
|
||||
// Prioritize quota availability. tierPriority replaces 0.05 from taskFit.
|
||||
"offline-friendly": {
|
||||
quota: 0.37,
|
||||
health: 0.28,
|
||||
costInv: 0.1,
|
||||
latencyInv: 0.05,
|
||||
taskFit: 0.0,
|
||||
stability: 0.1,
|
||||
tierPriority: 0.05,
|
||||
quota: 0.3524,
|
||||
health: 0.2667,
|
||||
costInv: 0.0952,
|
||||
latencyInv: 0.0476,
|
||||
taskFit: 0,
|
||||
stability: 0.0952,
|
||||
tierPriority: 0.0476,
|
||||
tierAffinity: 0,
|
||||
specificityMatch: 0,
|
||||
contextAffinity: 0.0,
|
||||
sessionAvailability: 0.05,
|
||||
contextAffinity: 0,
|
||||
sessionAvailability: 0.0476,
|
||||
resetWindowAffinity: 0,
|
||||
connectionDensity: 0.05,
|
||||
connectionDensity: 0.0476,
|
||||
},
|
||||
// #4235 `:reliable` — prioritize healthy, low-variance providers (high availability).
|
||||
// health (circuit-breaker) + stability (latency std-dev) dominate; weights sum to 1.0.
|
||||
// health (circuit-breaker) + stability (latency std-dev) dominate; weights sum to ~1.0
|
||||
// (re-normalized after #8940 added sessionAvailability without rebalancing — #9985).
|
||||
"reliability-first": {
|
||||
quota: 0.14,
|
||||
health: 0.37,
|
||||
costInv: 0.04,
|
||||
latencyInv: 0.05,
|
||||
taskFit: 0.1,
|
||||
stability: 0.2,
|
||||
tierPriority: 0.05,
|
||||
quota: 0.1333,
|
||||
health: 0.3524,
|
||||
costInv: 0.0381,
|
||||
latencyInv: 0.0476,
|
||||
taskFit: 0.0952,
|
||||
stability: 0.1905,
|
||||
tierPriority: 0.0476,
|
||||
tierAffinity: 0,
|
||||
specificityMatch: 0,
|
||||
contextAffinity: 0.0,
|
||||
sessionAvailability: 0.05,
|
||||
contextAffinity: 0,
|
||||
sessionAvailability: 0.0476,
|
||||
resetWindowAffinity: 0,
|
||||
connectionDensity: 0.05,
|
||||
connectionDensity: 0.0476,
|
||||
},
|
||||
// Chaos mode — priority: health > stability > taskFit > latency.
|
||||
// Selects top-N healthy providers for parallel dispatch. Favors providers with
|
||||
@@ -100,19 +101,19 @@ export const MODE_PACKS: Record<string, ScoringWeights> = {
|
||||
// to picking the most stable providers); connectionDensity boosted slightly to
|
||||
// prefer providers with multiple accounts (more resilient to per-account rate limits).
|
||||
"chaos-mode": {
|
||||
quota: 0.05,
|
||||
health: 0.42,
|
||||
costInv: 0.02,
|
||||
latencyInv: 0.03,
|
||||
taskFit: 0.2,
|
||||
stability: 0.18,
|
||||
tierPriority: 0.02,
|
||||
quota: 0.0476,
|
||||
health: 0.4,
|
||||
costInv: 0.019,
|
||||
latencyInv: 0.0286,
|
||||
taskFit: 0.1905,
|
||||
stability: 0.1714,
|
||||
tierPriority: 0.019,
|
||||
tierAffinity: 0,
|
||||
specificityMatch: 0,
|
||||
contextAffinity: 0.03,
|
||||
sessionAvailability: 0.05,
|
||||
contextAffinity: 0.0286,
|
||||
sessionAvailability: 0.0476,
|
||||
resetWindowAffinity: 0,
|
||||
connectionDensity: 0.05,
|
||||
connectionDensity: 0.0476,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -8,9 +8,6 @@ import {
|
||||
tallyDrift,
|
||||
readProviderTotal,
|
||||
countLocales,
|
||||
readMcpFactsFromSource,
|
||||
listLocalizedDocs,
|
||||
makeRequiredCountsValidator,
|
||||
} from "../../scripts/check/check-docs-counts-sync.mjs";
|
||||
|
||||
// Explicit types for the .mjs exports — keep the test at 0 no-explicit-any warnings.
|
||||
@@ -27,11 +24,6 @@ const tally = tallyDrift as (
|
||||
) => { strict: number; soft: number; lines: string[] };
|
||||
const readTotal = readProviderTotal as () => number;
|
||||
const locales = countLocales as () => number;
|
||||
const mcpFacts = readMcpFactsFromSource as () => { tools: number; scopes: number } | null;
|
||||
const localizedDocs = listLocalizedDocs as (relativePath: string) => string[];
|
||||
const requireCounts = makeRequiredCountsValidator as (
|
||||
requirements: { label: string; value: number }[]
|
||||
) => (content: string) => { ok: boolean; detail: string };
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const GATE = path.resolve(here, "../../scripts/check/check-docs-counts-sync.mjs");
|
||||
@@ -100,31 +92,6 @@ test("countLocales reads a real, positive locale count from config/i18n.json", (
|
||||
assert.ok(locales() >= 40, "i18n config should define at least 40 locales");
|
||||
});
|
||||
|
||||
test("source-only MCP fallback matches the canonical inventory and scope union", () => {
|
||||
assert.deepEqual(mcpFacts(), { tools: 107, scopes: 32 });
|
||||
});
|
||||
|
||||
test("localized-doc discovery returns every locale root document", () => {
|
||||
const readmes = localizedDocs("README.md");
|
||||
assert.equal(readmes.length, 42);
|
||||
assert.ok(readmes.includes("docs/i18n/pt-BR/README.md"));
|
||||
assert.ok(readmes.includes("docs/i18n/zh-CN/README.md"));
|
||||
});
|
||||
|
||||
test("required-count validator reports precisely which live markers are missing", () => {
|
||||
const validate = requireCounts([
|
||||
{ label: "providers", value: 327 },
|
||||
{ label: "MCP tools", value: 107 },
|
||||
{ label: "MCP scopes", value: 32 },
|
||||
]);
|
||||
assert.equal(validate("327 providers; 107 tools; 32 scopes").ok, true);
|
||||
const stale = validate("290 providers; 104 tools; 31 scopes");
|
||||
assert.equal(stale.ok, false);
|
||||
assert.match(stale.detail, /providers=327/);
|
||||
assert.match(stale.detail, /MCP tools=107/);
|
||||
assert.match(stale.detail, /MCP scopes=32/);
|
||||
});
|
||||
|
||||
// --- live gate smoke -----------------------------------------------------------------
|
||||
|
||||
test("the gate exits 0 against the current (synced) repo state", () => {
|
||||
@@ -138,7 +105,6 @@ test("the gate exits 0 against the current (synced) repo state", () => {
|
||||
// down to 1.37B, because no gate watched that number.
|
||||
import {
|
||||
checkFreeTierHeadline,
|
||||
checkFreeTierInventory,
|
||||
extractHeadlineClaims,
|
||||
} from "../../scripts/check/check-docs-counts-sync.mjs";
|
||||
|
||||
@@ -180,23 +146,6 @@ test("free-tier gate passes when a file carries no headline at all", () => {
|
||||
assert.equal(checkHeadline("no figures here", TOTALS).ok, true);
|
||||
});
|
||||
|
||||
const checkInventory = checkFreeTierInventory as (
|
||||
content: string,
|
||||
totals: { pools: number; models: number }
|
||||
) => { ok: boolean; detail: string };
|
||||
|
||||
test("free-tier inventory gate accepts the live pool/model counts", () => {
|
||||
assert.equal(
|
||||
checkInventory("43 provider pools / 522 model budget entries", { pools: 43, models: 522 }).ok,
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test("free-tier inventory gate rejects stale model counts", () => {
|
||||
const result = checkInventory("43 provider pools / 516 models", { pools: 43, models: 522 });
|
||||
assert.equal(result.ok, false);
|
||||
assert.match(result.detail, /live catalog has 43 pools \/ 522 model budget entries/);
|
||||
});
|
||||
|
||||
// --- Generic numeric-claim gate (engines / MCP tools / scopes / CLI) --------
|
||||
// Extends the same drift guard to the counts that silently drifted in v3.8.49:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { describe, it, mock } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
CREDITS_EXHAUSTED_STATUS,
|
||||
isCreditsExhaustedReprobeCandidate,
|
||||
@@ -17,7 +18,7 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
|
||||
testStatus: CREDITS_EXHAUSTED_STATUS,
|
||||
rateLimitedUntil: new Date(nowMs - 5000).toISOString(),
|
||||
};
|
||||
expect(isRecoverableCooldownConnection(conn, nowMs)).toBe(false);
|
||||
assert.equal(isRecoverableCooldownConnection(conn, nowMs), false);
|
||||
});
|
||||
|
||||
it("should reprobe credits_exhausted when >30m has elapsed since lastErrorAt", () => {
|
||||
@@ -27,7 +28,7 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
|
||||
testStatus: CREDITS_EXHAUSTED_STATUS,
|
||||
lastErrorAt: thirtyOneMinAgo,
|
||||
};
|
||||
expect(isCreditsExhaustedReprobeCandidate(conn, nowMs)).toBe(true);
|
||||
assert.equal(isCreditsExhaustedReprobeCandidate(conn, nowMs), true);
|
||||
});
|
||||
|
||||
it("should NOT reprobe credits_exhausted when <30m has elapsed since lastErrorAt", () => {
|
||||
@@ -37,7 +38,7 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
|
||||
testStatus: CREDITS_EXHAUSTED_STATUS,
|
||||
lastErrorAt: tenMinAgo,
|
||||
};
|
||||
expect(isCreditsExhaustedReprobeCandidate(conn, nowMs)).toBe(false);
|
||||
assert.equal(isCreditsExhaustedReprobeCandidate(conn, nowMs), false);
|
||||
});
|
||||
|
||||
it("should reprobe credits_exhausted if no timestamp is present (first tick after startup)", () => {
|
||||
@@ -45,7 +46,7 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
|
||||
id: "conn-1",
|
||||
testStatus: CREDITS_EXHAUSTED_STATUS,
|
||||
};
|
||||
expect(isCreditsExhaustedReprobeCandidate(conn, nowMs)).toBe(true);
|
||||
assert.equal(isCreditsExhaustedReprobeCandidate(conn, nowMs), true);
|
||||
});
|
||||
|
||||
it("selectRecoverableConnections includes both transient cooldowns and expired credits_exhausted", () => {
|
||||
@@ -69,18 +70,18 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
|
||||
[activeTransient, expiredCredits, freshCredits],
|
||||
nowMs
|
||||
);
|
||||
expect(selected.map((c) => c.id)).toEqual(["t-1", "c-1"]);
|
||||
assert.deepEqual(selected.map((c) => c.id), ["t-1", "c-1"]);
|
||||
});
|
||||
|
||||
it("runConnectionRecoveryTick calls clearConnectionError for reprobe candidates", async () => {
|
||||
const loadConnections = vi.fn().mockResolvedValue([
|
||||
const loadConnections = mock.fn(async () => [
|
||||
{
|
||||
id: "c-1",
|
||||
testStatus: CREDITS_EXHAUSTED_STATUS,
|
||||
lastErrorAt: new Date(nowMs - thirtyMinMs - 1000).toISOString(),
|
||||
},
|
||||
]);
|
||||
const clearConnectionError = vi.fn().mockResolvedValue(undefined);
|
||||
const clearConnectionError = mock.fn(async () => undefined);
|
||||
|
||||
const res = await runConnectionRecoveryTick({
|
||||
nowMs,
|
||||
@@ -88,8 +89,10 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
|
||||
clearConnectionError,
|
||||
});
|
||||
|
||||
expect(res.recovered).toBe(1);
|
||||
expect(res.recoveredIds).toEqual(["c-1"]);
|
||||
expect(clearConnectionError).toHaveBeenCalledWith("c-1", expect.anything());
|
||||
assert.equal(res.recovered, 1);
|
||||
assert.deepEqual(res.recoveredIds, ["c-1"]);
|
||||
assert.equal(clearConnectionError.mock.callCount(), 1);
|
||||
assert.equal(clearConnectionError.mock.calls[0].arguments[0], "c-1");
|
||||
assert.notEqual(clearConnectionError.mock.calls[0].arguments[1], undefined);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user