Files
OmniRoute/tests/unit/token-refresh-cas-guard.test.ts
MumuTW 533f8051a4 chore(token-refresh): decompose services/tokenRefresh.ts into tokenRefresh/* leaves (999 → 724) (#8547)
* chore(token-refresh): extract rotation/cas/circuit-breaker refresh logic into tokenRefresh/* leaves

* test(oauth): follow isUnrecoverableRefreshError to tokenRefresh/shared.ts

cad2c7285 moved isUnrecoverableRefreshError out of tokenRefresh.ts into
tokenRefresh/shared.ts. This suite asserts on source *text* (it regex-matches
the function body to prove the unrecoverable sentinel is returned), so the
move made it fail to find the definition — the only red test across the 23
tokenRefresh-related suites.

Repoint the read() at the file that now defines the body. The public surface
is unchanged: tokenRefresh.ts still re-exports the symbol, verified by import.

* docs(changelog): add fragment for this PR

* docs(auth): correct the #7338 attribution wording in the tokenRefresh header

The header claimed credit for KooshaPari's #7338 was "preserved via co-authorship on the
extraction commits", but none of the commits carries a Co-authored-by trailer -- and adding
one would be inaccurate, since this is an independent implementation against the current
tip rather than a reuse of that diff. The by-name credit for proposing the split stays;
only the false claim about the mechanism is removed.
2026-07-26 03:52:27 -03:00

125 lines
4.8 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
// Unit tests for the CAS guard leaf extracted from tokenRefresh.ts (#4038).
// The CAS guard re-reads the row's refresh_token right before persisting and
// SKIPS the write when a concurrent writer already rotated it past the token
// the caller presented — preventing a revert that would invalidate the token
// family on rotating-token providers (Auth0/Anthropic).
const {
runWithCasGuard,
getActiveCasGuard,
getCasGuardStats,
_resetCasGuardStats,
casGuardShouldSkipPersist,
} = await import("../../open-sse/services/tokenRefresh/casGuard.ts");
const silentLog = { info() {}, warn() {}, error() {} };
test.beforeEach(() => {
_resetCasGuardStats();
});
test("getActiveCasGuard returns undefined outside a guard context", () => {
assert.equal(getActiveCasGuard(), undefined);
});
test("runWithCasGuard exposes the guard via getActiveCasGuard inside the closure", async () => {
const guard = { expectedRefreshToken: "R0", reread: async () => "R0" };
await runWithCasGuard(guard, async () => {
assert.equal(getActiveCasGuard(), guard);
});
assert.equal(getActiveCasGuard(), undefined, "guard is cleared after the closure resolves");
});
test("runWithCasGuard with a null/undefined guard runs the function unchanged", async () => {
let ran = false;
await runWithCasGuard(null, async () => {
ran = true;
});
assert.equal(ran, true);
assert.equal(getActiveCasGuard(), undefined);
});
test("casGuardShouldSkipPersist returns false when no guard is active", async () => {
assert.equal(await casGuardShouldSkipPersist(silentLog), false);
assert.equal(getCasGuardStats().skipped, 0);
assert.equal(getCasGuardStats().persisted, 0);
});
test("casGuardShouldSkipPersist returns false when the guard has no expectedRefreshToken", async () => {
const guard = { expectedRefreshToken: null, reread: async () => "R0" };
await runWithCasGuard(guard, async () => {
assert.equal(await casGuardShouldSkipPersist(silentLog), false);
});
assert.equal(getCasGuardStats().skipped, 0);
assert.equal(getCasGuardStats().persisted, 0);
});
test("casGuardShouldSkipPersist SKIPS when the row rotated past the presented token", async () => {
const guard = { expectedRefreshToken: "R0", reread: async () => "R_CONCURRENT" };
await runWithCasGuard(guard, async () => {
assert.equal(await casGuardShouldSkipPersist(silentLog), true);
});
assert.equal(getCasGuardStats().skipped, 1);
assert.equal(getCasGuardStats().persisted, 0);
});
test("casGuardShouldSkipPersist PERSISTS when the row still holds the presented token", async () => {
const guard = { expectedRefreshToken: "R0", reread: async () => "R0" };
await runWithCasGuard(guard, async () => {
assert.equal(await casGuardShouldSkipPersist(silentLog), false);
});
assert.equal(getCasGuardStats().skipped, 0);
assert.equal(getCasGuardStats().persisted, 1);
});
test("casGuardShouldSkipPersist falls through to persist when reread throws (best-effort)", async () => {
const guard = {
expectedRefreshToken: "R0",
reread: async () => {
throw new Error("db unavailable");
},
};
await runWithCasGuard(guard, async () => {
assert.equal(await casGuardShouldSkipPersist(silentLog), false);
});
// reread failure returns false (do not skip persist) WITHOUT touching the
// persisted counter — the counter only advances on a successful reread that
// confirms the row is unchanged. The key guarantee is skipped stays 0.
assert.equal(getCasGuardStats().skipped, 0, "reread failure must never block recovery");
assert.equal(getCasGuardStats().persisted, 0);
});
test("casGuardShouldSkipPersist treats an empty reread as not-rotated", async () => {
const guard = { expectedRefreshToken: "R0", reread: async () => null };
await runWithCasGuard(guard, async () => {
assert.equal(await casGuardShouldSkipPersist(silentLog), false);
});
assert.equal(getCasGuardStats().persisted, 1);
});
test("getCasGuardStats returns a snapshot copy (not the live counters)", () => {
const guard = { expectedRefreshToken: "R0", reread: async () => "R0" };
return runWithCasGuard(guard, async () => {
await casGuardShouldSkipPersist(silentLog);
const snap = getCasGuardStats();
assert.equal(snap.persisted, 1);
// Mutating the snapshot must not affect future stats.
snap.persisted = 999;
assert.equal(getCasGuardStats().persisted, 1);
});
});
test("_resetCasGuardStats zeroes both counters", async () => {
const guard = { expectedRefreshToken: "R0", reread: async () => "R_CONCURRENT" };
await runWithCasGuard(guard, async () => {
await casGuardShouldSkipPersist(silentLog);
});
assert.equal(getCasGuardStats().skipped, 1);
_resetCasGuardStats();
assert.equal(getCasGuardStats().skipped, 0);
assert.equal(getCasGuardStats().persisted, 0);
});