Files
OmniRoute/tests/unit/explicit-inactive-probe-w2.test.ts
Bob.Hou c4eafaa26d fix(auth): probe a pinned inactive connection after quota top-up (#13017)
The one-shot framing is what makes this safe: a pin is an explicit operator act, so taking the inactive row only for that request, with siblings out of the pool and a 60s per-connection storm gate, keeps the blast radius at one request. Dashboard deactivate staying off is the right carve-out.

Reconciled against the tip after the batch landed: the `chatHelpers.ts` import block conflicted with `buildExhaustionOptions` (#12975) and both imports were kept. 41/41 across the probe suites afterwards.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded with the other 19 PRs of this batch. Two in-batch conflicts, both additive and resolved by keeping each side: the `ENVIRONMENT.md` table (#13035 + #13011) and the `chatHelpers.ts` import block (#12975 on the tip + #13017).

- `typecheck:core` clean; `check:dashboard-typecheck` OK (206 pre-existing, within baseline); `check:changelog-integrity` OK; `check:docs-counts` migrations ✓
- complexity 2816 / baseline 3218 and cognitive-complexity 1271 / baseline 1437 — both under baseline
- 531 of 532 focused assertions green across the batch's 46 test files
- `check-file-size` rebaselined for the batch's real growth (annotation `_rebaseline_2026_09_11_mergebatch_v3851_houminxi`, landed on #13038), attributed per PR

The single red is **not this batch**: `tests/unit/combo/quota-weighted-strategy.test.ts` → "A/B isolation: 7 hard-empty + 2 at 0.5% + 1 at 40%, floor=1" asserts an order between two connections of identical weight and flakes on the pure tip too — 2 failures in 4 runs at `origin/release/v3.8.51` with nothing from this batch applied.

⚠️ base-red inherited: #12732 — `Docs Gates`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` reproduce on the pure tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098, untouched here).

Thanks @HouMinXi — the live evidence on these (X500 logs, `storage.sqlite` state, real `/v1/models` probes, the 36-minute outage write-up) is what let a 20-PR batch be reviewed as a unit.
2026-09-11 20:45:21 -03:00

86 lines
2.9 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-explicit-inactive-w2-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "explicit-inactive-w2-test-secret";
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const auth = await import("../../src/sse/services/auth.ts");
const { maybeReactivateAfterExplicitProbe, resetExplicitProbeMapForTests } = await import(
"../../src/sse/services/explicitInactiveProbe.ts"
);
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
async function seedInactiveSiliconflow(testStatus: "active" | "credits_exhausted") {
const row = await providersDb.createProviderConnection({
provider: "siliconflow",
authType: "apikey",
name: "sf-inactive",
apiKey: "sf-inactive-test-key",
isActive: false,
testStatus,
});
assert.ok(typeof row?.id === "string" && row.id.length > 0);
return { id: row.id };
}
function asPinnedCreds(creds: unknown) {
assert.ok(creds);
return creds as { connectionId?: string; reactivatedFromInactive?: boolean };
}
test.beforeEach(async () => {
resetExplicitProbeMapForTests();
await resetStorage();
});
test.after(async () => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("W-2 pin + inactive returns credentials after wiring (was null)", async () => {
const row = await seedInactiveSiliconflow("active");
const creds = asPinnedCreds(
await auth.getProviderCredentials("siliconflow", null, null, "siliconflow/m", {
forcedConnectionId: row.id,
})
);
assert.equal(creds.connectionId, row.id);
assert.equal(creds.reactivatedFromInactive, true);
});
test("W-2 pin + credits_exhausted returns credentials after wiring", async () => {
const row = await seedInactiveSiliconflow("credits_exhausted");
const creds = asPinnedCreds(
await auth.getProviderCredentials("siliconflow", null, null, "siliconflow/m", {
forcedConnectionId: row.id,
})
);
assert.equal(creds.connectionId, row.id);
assert.equal(creds.reactivatedFromInactive, true);
});
test("W-1 successful probe re-enables inactive pin in SQLite", async () => {
const row = await seedInactiveSiliconflow("active");
const before = await providersDb.getProviderConnectionById(row.id);
assert.equal(before?.isActive, false);
await maybeReactivateAfterExplicitProbe({
reactivatedFromInactive: true,
connectionId: row.id,
});
const after = await providersDb.getProviderConnectionById(row.id);
assert.equal(after?.isActive, true);
});