-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcodex-session.test.ts
More file actions
110 lines (100 loc) · 4.23 KB
/
Copy pathcodex-session.test.ts
File metadata and controls
110 lines (100 loc) · 4.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { test, expect, describe, beforeEach, afterEach } from "bun:test";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { isCodexTokenExpired, getValidCodexToken, CodexAuthError } from "../../src/auth/codex/session.js";
import { loadCodexProfile, saveCodexProfile } from "../../src/auth/codex/store.js";
describe("isCodexTokenExpired", () => {
test("not expired well before expiry", () => {
expect(isCodexTokenExpired({ access: "a", refresh: "r", expiresAt: 1_000_000 }, 500_000)).toBe(false);
});
test("expired within the refresh skew window", () => {
// 30s before expiry is inside the 60s skew, so treated as expired.
expect(isCodexTokenExpired({ access: "a", refresh: "r", expiresAt: 1_000_000 }, 1_000_000 - 30_000)).toBe(true);
});
test("expired after expiry", () => {
expect(isCodexTokenExpired({ access: "a", refresh: "r", expiresAt: 1_000_000 }, 2_000_000)).toBe(true);
});
});
describe("getValidCodexToken", () => {
const realFetch = globalThis.fetch;
beforeEach(() => {
globalThis.fetch = realFetch;
});
afterEach(() => {
globalThis.fetch = realFetch;
});
async function withHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
const home = await mkdtemp(join(tmpdir(), "codex-session-"));
try {
return await fn(home);
} finally {
await rm(home, { recursive: true, force: true });
}
}
test("returns the stored token when still valid (no network)", async () => {
await withHome(async (home) => {
globalThis.fetch = (() => {
throw new Error("should not be called");
}) as typeof fetch;
await saveCodexProfile(
{ name: "p", createdAt: 0, tokens: { access: "live", refresh: "r", expiresAt: 10_000_000 } },
home,
);
expect((await getValidCodexToken("p", 1_000, home)).access).toBe("live");
});
});
test("returns the account id alongside the access token", async () => {
await withHome(async (home) => {
globalThis.fetch = (() => {
throw new Error("should not be called");
}) as typeof fetch;
await saveCodexProfile(
{ name: "p", createdAt: 0, tokens: { access: "live", refresh: "r", expiresAt: 10_000_000, accountId: "acct-1" } },
home,
);
const access = await getValidCodexToken("p", 1_000, home);
expect(access.access).toBe("live");
expect(access.accountId).toBe("acct-1");
});
});
test("refreshes and persists when the token is expired", async () => {
await withHome(async (home) => {
await saveCodexProfile(
{ name: "p", createdAt: 0, tokens: { access: "old", refresh: "old-r", expiresAt: 1_000 } },
home,
);
globalThis.fetch = (async () =>
new Response(JSON.stringify({ access_token: "fresh", refresh_token: "new-r", expires_in: 3600 }), {
status: 200,
headers: { "content-type": "application/json" },
})) as typeof fetch;
const token = await getValidCodexToken("p", 5_000, home);
expect(token.access).toBe("fresh");
const stored = await loadCodexProfile("p", home);
expect(stored?.tokens.access).toBe("fresh");
expect(stored?.tokens.refresh).toBe("new-r");
expect(stored?.createdAt).toBe(0);
});
});
test("throws CodexAuthError(missing) for an unknown profile", async () => {
await withHome(async (home) => {
const err = await getValidCodexToken("ghost", 0, home).catch((e: unknown) => e);
expect(err).toBeInstanceOf(CodexAuthError);
expect((err as CodexAuthError).reason).toBe("missing");
expect((err as CodexAuthError).profile).toBe("ghost");
});
});
test("throws CodexAuthError(refresh-failed) when refresh is rejected", async () => {
await withHome(async (home) => {
await saveCodexProfile(
{ name: "p", createdAt: 0, tokens: { access: "old", refresh: "bad", expiresAt: 1_000 } },
home,
);
globalThis.fetch = (async () => new Response("revoked", { status: 400 })) as typeof fetch;
const err = await getValidCodexToken("p", 5_000, home).catch((e: unknown) => e);
expect(err).toBeInstanceOf(CodexAuthError);
expect((err as CodexAuthError).reason).toBe("refresh-failed");
});
});
});