Skip to content

Commit 4086526

Browse files
lidge-junjun
andauthored
test(auth): seed the pool quota after the clock is pinned (#3147)
* test(auth): seed the pool quota and credential after the clock is pinned The websocket refresh test still failed on loaded CI runners after #3139, on both macOS and Linux, and dev's own HEAD fails it too — so it was not something any open branch introduced. Two writes stamp real time when they run before the clock is pinned: updateAccountQuota sets updatedAt: Date.now(), and saveCodexAccountCredential sets replacedAt. Everything after the pin reads the pinned 2027 value, so the gap is about 136 days against a 6-hour freshness window (QUOTA_DISK_MAX_AGE_MS, src/codex/quota.ts:491). The seeded state reads as stale no matter how fast the runner is, the startup pool-quota prime refreshes the credential before the first turn is served, and seenAuth[0] is already the new token — which is why the failure diff was always the first element. #3139 pinned the clock and the fetch stub before startServer, closing the window for the prime's own reads. It could not close a window for timestamps written before either was in place. Both seeds now run after the pin. Timing-dependent by nature: the mismatch does not reproduce locally either before or after, so the evidence is the mechanism rather than a local red-to-green. A 136-day gap against a 6-hour window is arithmetic, not a race. Twelve consecutive local runs are clean. * test(auth): restore the affinity test's quota seed after the pin The previous commit removed `updateAccountQuota("pool-a", 10, 5)` from the `expired thread affinity` test along with the websocket test's own seeds. That seed belongs to the affinity test, and its comment kept pointing at a call that was no longer there. Restore it on the correct side of the clock pin. Note what the comment now claims and what it does not: seeding after the pin is what keeps the startup pool-quota prime quiet, because `primeCodexPoolQuotas` treats a missing entry as stale exactly like an expired one (src/codex/auth-api.ts:1334). It is not a race fix for `expect(upstreamRequests).toBe(3)` — `redirectCanonicalCodexTo` only rewrites `/backend-api/codex`, while the prime's WHAM call goes to `/backend-api/wham/usage` and never reaches the counted upstream. Verified with `bun test tests/server-auth.test.ts`: 91 pass, 0 fail. --------- Co-authored-by: jun <jun@lidge.dev>
1 parent 15b0f70 commit 4086526

1 file changed

Lines changed: 23 additions & 14 deletions

File tree

tests/server-auth.test.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2125,17 +2125,17 @@ describe("server local API auth", () => {
21252125
expiresAt: now + CODEX_THREAD_AFFINITY_IDLE_TTL_MS + 10 * 60_000,
21262126
chatgptAccountId: "acct-pool-a",
21272127
});
2128-
updateAccountQuota("pool-a", 10, 5);
2129-
21302128
const originalNow = Date.now;
21312129
// Pin the clock BEFORE startServer, not after. `startServer` returns synchronously but
21322130
// arms an async pool-quota prime (src/server/index.ts:2054-2064) that outlives its
21332131
// return, and that prime decides staleness with `Date.now() - quota.updatedAt >=
2134-
// POOL_CACHE_TTL` (src/codex/auth-api.ts:1334-1337). `updateAccountQuota` above stamped
2135-
// `updatedAt` with the REAL clock, so a prime that lands after a 2027 fake clock is
2136-
// installed sees months of cache age, fetches, and rotates the credential out from under
2137-
// the assertions. Installing the clock first closes the window entirely.
2132+
// POOL_CACHE_TTL` (src/codex/auth-api.ts:1334-1337), where a MISSING entry is stale too.
2133+
// Seeding the quota after the pin is what actually keeps the prime quiet: a seed written
2134+
// before the pin stamps `updatedAt` with the real clock, which reads as months of cache
2135+
// age against this 2027 `now` and sends the prime off to fetch and rotate the credential
2136+
// out from under the assertions.
21382137
Date.now = () => now;
2138+
updateAccountQuota("pool-a", 10, 5);
21392139
const server = startServer(0);
21402140
try {
21412141
for (const threadId of ["expired-http", "expired-compact", "expired-ws"]) {
@@ -2240,14 +2240,6 @@ describe("server local API auth", () => {
22402240
codexAccountNamespaces: { "ws-refresh": "pool-a" },
22412241
activeCodexAccountId: "pool-a",
22422242
} as OcxConfig);
2243-
saveCodexAccountCredential("pool-a", {
2244-
accessToken: "old-access-token",
2245-
refreshToken: "old-refresh-token",
2246-
expiresAt: now + 120_000,
2247-
chatgptAccountId: "acct-pool-a",
2248-
});
2249-
updateAccountQuota("pool-a", 10, 5);
2250-
22512243
const originalNow = Date.now;
22522244
const originalFetch = globalThis.fetch;
22532245
// Both the clock and the fetch stub go up before `startServer`. The async pool-quota
@@ -2258,6 +2250,23 @@ describe("server local API auth", () => {
22582250
// credential before the first turn was served — so `seenAuth[0]` was already the new
22592251
// token. The failure diff was always the first element, never the second.
22602252
Date.now = () => now;
2253+
// Seed the credential and quota AFTER the clock is pinned.
2254+
//
2255+
// Both writes stamp real time when they run before the pin: `updateAccountQuota` sets
2256+
// `updatedAt: Date.now()`, and `saveCodexAccountCredential` sets `replacedAt`. The
2257+
// startup pool-quota prime then compares those stamps
2258+
// against this 2027 `now` and judges stale — so it refreshes the credential before the
2259+
// first turn is served and `seenAuth[0]` is already the new token. Pinning the clock
2260+
// and the fetch stub first (#3139) closed the window for the prime's own reads, but not
2261+
// for a timestamp written before either was in place, which is why this kept flaking on
2262+
// loaded runners after that fix.
2263+
saveCodexAccountCredential("pool-a", {
2264+
accessToken: "old-access-token",
2265+
refreshToken: "old-refresh-token",
2266+
expiresAt: now + 120_000,
2267+
chatgptAccountId: "acct-pool-a",
2268+
});
2269+
updateAccountQuota("pool-a", 10, 5);
22612270
globalThis.fetch = (async (input, init) => {
22622271
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
22632272
if (url === "https://auth.openai.com/oauth/token") {

0 commit comments

Comments
 (0)