import { describe, expect, it } from "bun:test"; import { CredentialRefreshCoordinator, runWithCredentialRefresh, bindSessionCredential, credentialRefreshCoordinator, refreshSessionCredential, releaseSessionCredential, requestWithSessionCredential } from "../../src/product/credential-refresh.js"; describe("credential renewal", () => { it("rejects stale replies and stops repeated requests when the renewed credential is still rejected", async () => { const session = "rejected-credential-session"; bindSessionCredential(session, { authorization: "Bearer OLD", projectId: "p" }); let calls = 0; let failures = 0; const unsubscribe = credentialRefreshCoordinator.subscribe(session, (event) => { if (event.type === "credential_refresh_required") { expect(refreshSessionCredential(session, { authorization: "Bearer NEW", projectId: "other" }, event.requestId)).toBeFalse(); expect(refreshSessionCredential(session, { authorization: "Bearer NEW", projectId: "p" }, "stale")).toBeFalse(); refreshSessionCredential(session, { authorization: "Bearer NEW", projectId: "p" }, event.requestId); } else if (event.type === "credential_refresh_failed") failures += 1; }); try { expect((await requestWithSessionCredential(session, async () => { calls += 1; return new Response(null, { status: 401 }); })).status).toBe(401); await expect(requestWithSessionCredential(session, async () => { calls += 1; return new Response(null, { status: 200 }); })).rejects.toThrow("renewal failed"); expect(calls).toBe(2); expect(failures).toBe(1); } finally { unsubscribe(); releaseSessionCredential(session); } }); it("coalesces concurrent 401 responses, resumes both calls, and retries only once", async () => { const coordinator = new CredentialRefreshCoordinator(1000); const old = { authorization: "Bearer TEST_OLD", projectId: "p" }; const renewed = { authorization: "Bearer TEST_NEW", projectId: "p" }; let requests = 0; coordinator.subscribe("s", (event) => { if (event.type === "credential_refresh_required") { requests += 1; queueMicrotask(() => coordinator.resolve("s", event.requestId, renewed)); } }); const calls: string[] = []; const execute = async (credential: typeof old) => { calls.push(credential.authorization); return { status: credential.authorization === old.authorization ? 401 : 200 }; }; expect((await Promise.all([runWithCredentialRefresh(coordinator, "s", old, execute), runWithCredentialRefresh(coordinator, "s", old, execute)])).map((result) => result.status)).toEqual([200, 200]); expect(requests).toBe(1); expect(calls).toHaveLength(4); }); it("does not refresh 403 and fails when refresh is unavailable or cancelled", async () => { const coordinator = new CredentialRefreshCoordinator(1000); const credential = { authorization: "Bearer TEST", projectId: "p" }; expect((await runWithCredentialRefresh(coordinator, "s", credential, async () => ({ status: 403 }))).status).toBe(403); await expect(runWithCredentialRefresh(coordinator, "s", credential, async () => ({ status: 401 }))).rejects.toThrow("unavailable"); coordinator.enableBackground("s"); const pending = coordinator.request("s", "access_token_expired"); coordinator.cancelSession("s"); await expect(pending).rejects.toThrow("cancelled"); }); it("renews before near-expiry execution and exposes the same pending request for reconnect", async () => { const coordinator = new CredentialRefreshCoordinator(1000); const old = { authorization: "Bearer OLD", projectId: "p", expiresAt: new Date(Date.now() + 1000).toISOString() }; coordinator.subscribe("s", (event) => { if (event.type === "credential_refresh_required") { expect(coordinator.getPendingEvent("s")?.requestId).toBe(event.requestId); coordinator.resolve("s", event.requestId, { authorization: "Bearer NEW", projectId: "p" }); } }); expect(await runWithCredentialRefresh(coordinator, "s", old, async (credential) => ({ status: 200, authorization: credential.authorization }))).toMatchObject({ authorization: "Bearer NEW" }); }); });