chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Unit tests for the Codex bulk-import normalizer.
|
||||
*
|
||||
* Pure helpers — no I/O, no network. Synthetic id_tokens are crafted by
|
||||
* base64url-encoding a header/payload/signature triple (only the payload is
|
||||
* decoded; we never verify the signature).
|
||||
*
|
||||
* Ported from decolua/9router#1257 (beaaan).
|
||||
*/
|
||||
|
||||
import test, { describe } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
normalizeCodexImportRecord,
|
||||
flattenCodexImportPayload,
|
||||
} from "../../src/lib/oauth/services/codexImport.ts";
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
function b64url(obj: unknown): string {
|
||||
return Buffer.from(JSON.stringify(obj))
|
||||
.toString("base64")
|
||||
.replace(/=+$/, "")
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_");
|
||||
}
|
||||
|
||||
function makeIdToken(payload: Record<string, unknown>): string {
|
||||
const header = b64url({ alg: "RS256", typ: "JWT" });
|
||||
const body = b64url(payload);
|
||||
return `${header}.${body}.signature`;
|
||||
}
|
||||
|
||||
const FULL_RECORD = {
|
||||
id_token: makeIdToken({
|
||||
email: "test-jwt@example.com",
|
||||
"https://api.openai.com/auth": {
|
||||
chatgpt_account_id: "acct-from-jwt",
|
||||
chatgpt_plan_type: "plus",
|
||||
},
|
||||
}),
|
||||
access_token: "access-xyz",
|
||||
refresh_token: "refresh-xyz",
|
||||
account_id: "acct-from-record",
|
||||
email: "test-record@example.com",
|
||||
type: "codex",
|
||||
expired: "2026-05-22T10:34:04+07:00",
|
||||
};
|
||||
|
||||
// ── normalizeCodexImportRecord ────────────────────────────────────────────────
|
||||
|
||||
describe("normalizeCodexImportRecord", () => {
|
||||
test("normalizes a full Codex export record", () => {
|
||||
const result = normalizeCodexImportRecord(FULL_RECORD);
|
||||
assert.equal(result.ok, true);
|
||||
if (!result.ok) return;
|
||||
const { payload } = result;
|
||||
assert.equal(payload.provider, "codex");
|
||||
assert.equal(payload.authType, "oauth");
|
||||
assert.equal(payload.accessToken, "access-xyz");
|
||||
assert.equal(payload.refreshToken, "refresh-xyz");
|
||||
assert.equal(payload.idToken, FULL_RECORD.id_token);
|
||||
assert.equal(payload.testStatus, "active");
|
||||
// JWT email wins over the record-level email.
|
||||
assert.equal(payload.email, "test-jwt@example.com");
|
||||
assert.deepEqual(payload.providerSpecificData, {
|
||||
chatgptAccountId: "acct-from-jwt",
|
||||
chatgptPlanType: "plus",
|
||||
});
|
||||
// ISO-formatted, parses back to the right instant.
|
||||
const expiresMs = Date.parse(payload.expiresAt);
|
||||
assert.ok(Number.isFinite(expiresMs));
|
||||
assert.equal(expiresMs, Date.parse(FULL_RECORD.expired));
|
||||
});
|
||||
|
||||
test("falls back to record email when id_token has none", () => {
|
||||
const idToken = makeIdToken({
|
||||
"https://api.openai.com/auth": { chatgpt_account_id: "x" },
|
||||
});
|
||||
const result = normalizeCodexImportRecord({
|
||||
access_token: "a",
|
||||
refresh_token: "r",
|
||||
email: "fallback@example.com",
|
||||
id_token: idToken,
|
||||
});
|
||||
assert.equal(result.ok, true);
|
||||
if (!result.ok) return;
|
||||
assert.equal(result.payload.email, "fallback@example.com");
|
||||
assert.equal(result.payload.providerSpecificData?.chatgptAccountId, "x");
|
||||
});
|
||||
|
||||
test("falls back to account_id when id_token is missing", () => {
|
||||
const result = normalizeCodexImportRecord({
|
||||
access_token: "a",
|
||||
refresh_token: "r",
|
||||
email: "no-jwt@example.com",
|
||||
account_id: "acct-top-level",
|
||||
});
|
||||
assert.equal(result.ok, true);
|
||||
if (!result.ok) return;
|
||||
assert.equal(result.payload.providerSpecificData?.chatgptAccountId, "acct-top-level");
|
||||
assert.equal(result.payload.idToken, undefined);
|
||||
});
|
||||
|
||||
test("synthesizes expiresAt when `expired` is missing", () => {
|
||||
const before = Date.now();
|
||||
const result = normalizeCodexImportRecord({
|
||||
access_token: "a",
|
||||
refresh_token: "r",
|
||||
email: "x@example.com",
|
||||
});
|
||||
const after = Date.now();
|
||||
assert.equal(result.ok, true);
|
||||
if (!result.ok) return;
|
||||
const expiresMs = Date.parse(result.payload.expiresAt);
|
||||
assert.ok(expiresMs > before, "expiresAt should be in the future");
|
||||
// Default lifetime is 10 days; allow a generous upper bound.
|
||||
assert.ok(
|
||||
expiresMs <= after + 11 * 24 * 60 * 60 * 1000,
|
||||
"expiresAt should be within ~11 days",
|
||||
);
|
||||
});
|
||||
|
||||
test("rejects invalid `expired` strings by falling back to default", () => {
|
||||
const result = normalizeCodexImportRecord({
|
||||
access_token: "a",
|
||||
refresh_token: "r",
|
||||
email: "x@example.com",
|
||||
expired: "not-a-date",
|
||||
});
|
||||
assert.equal(result.ok, true);
|
||||
if (!result.ok) return;
|
||||
assert.ok(Number.isFinite(Date.parse(result.payload.expiresAt)));
|
||||
});
|
||||
|
||||
test("rejects records missing required fields", () => {
|
||||
assert.equal(normalizeCodexImportRecord({}).ok, false);
|
||||
assert.equal(
|
||||
normalizeCodexImportRecord({ access_token: "a", email: "x@y.z" }).ok,
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
normalizeCodexImportRecord({ access_token: "a", refresh_token: "r" }).ok,
|
||||
false, // no email anywhere
|
||||
);
|
||||
});
|
||||
|
||||
test("rejects records with non-codex `type`", () => {
|
||||
const result = normalizeCodexImportRecord({
|
||||
access_token: "a",
|
||||
refresh_token: "r",
|
||||
email: "x@example.com",
|
||||
type: "claude",
|
||||
});
|
||||
assert.equal(result.ok, false);
|
||||
if (result.ok) return;
|
||||
assert.match(result.error, /Unsupported type/);
|
||||
});
|
||||
|
||||
test("rejects non-objects", () => {
|
||||
assert.equal(normalizeCodexImportRecord(null).ok, false);
|
||||
assert.equal(normalizeCodexImportRecord("foo").ok, false);
|
||||
assert.equal(normalizeCodexImportRecord([]).ok, false);
|
||||
});
|
||||
|
||||
test("omits providerSpecificData when no account info is present", () => {
|
||||
const result = normalizeCodexImportRecord({
|
||||
access_token: "a",
|
||||
refresh_token: "r",
|
||||
email: "x@example.com",
|
||||
});
|
||||
assert.equal(result.ok, true);
|
||||
if (!result.ok) return;
|
||||
assert.equal(result.payload.providerSpecificData, undefined);
|
||||
});
|
||||
|
||||
test("unwraps the Codex CLI auth.json shape", () => {
|
||||
const idToken = makeIdToken({
|
||||
email: "cli@example.com",
|
||||
"https://api.openai.com/auth": {
|
||||
chatgpt_account_id: "acct-cli",
|
||||
chatgpt_plan_type: "free",
|
||||
},
|
||||
});
|
||||
// Access token's `exp` is the only expiry signal in auth.json.
|
||||
const expEpoch = Math.floor(Date.now() / 1000) + 3600;
|
||||
const accessToken = makeIdToken({ exp: expEpoch });
|
||||
const authJson = {
|
||||
auth_mode: "chatgpt",
|
||||
OPENAI_API_KEY: null,
|
||||
tokens: {
|
||||
id_token: idToken,
|
||||
access_token: accessToken,
|
||||
refresh_token: "rt-cli",
|
||||
account_id: "acct-cli",
|
||||
},
|
||||
last_refresh: "2026-05-16T11:35:58.322795500Z",
|
||||
};
|
||||
const result = normalizeCodexImportRecord(authJson);
|
||||
assert.equal(result.ok, true);
|
||||
if (!result.ok) return;
|
||||
assert.equal(result.payload.accessToken, accessToken);
|
||||
assert.equal(result.payload.refreshToken, "rt-cli");
|
||||
assert.equal(result.payload.idToken, idToken);
|
||||
assert.equal(result.payload.email, "cli@example.com");
|
||||
assert.deepEqual(result.payload.providerSpecificData, {
|
||||
chatgptAccountId: "acct-cli",
|
||||
chatgptPlanType: "free",
|
||||
});
|
||||
// Falls back to the access_token's `exp` claim when no `expired` field exists.
|
||||
assert.equal(Date.parse(result.payload.expiresAt), expEpoch * 1000);
|
||||
});
|
||||
|
||||
test("rejects auth.json when nested tokens are incomplete", () => {
|
||||
const result = normalizeCodexImportRecord({
|
||||
auth_mode: "chatgpt",
|
||||
tokens: { id_token: "x" }, // missing access_token + refresh_token
|
||||
});
|
||||
assert.equal(result.ok, false);
|
||||
});
|
||||
});
|
||||
|
||||
// ── flattenCodexImportPayload ─────────────────────────────────────────────────
|
||||
|
||||
describe("flattenCodexImportPayload", () => {
|
||||
test("wraps a single object", () => {
|
||||
const result = flattenCodexImportPayload({ a: 1 });
|
||||
assert.equal(result.ok, true);
|
||||
if (!result.ok) return;
|
||||
assert.deepEqual(result.records, [{ a: 1 }]);
|
||||
});
|
||||
|
||||
test("passes arrays through", () => {
|
||||
const result = flattenCodexImportPayload([{ a: 1 }, { b: 2 }]);
|
||||
assert.equal(result.ok, true);
|
||||
if (!result.ok) return;
|
||||
assert.deepEqual(result.records, [{ a: 1 }, { b: 2 }]);
|
||||
});
|
||||
|
||||
test("rejects scalars", () => {
|
||||
assert.equal(flattenCodexImportPayload("nope").ok, false);
|
||||
assert.equal(flattenCodexImportPayload(42).ok, false);
|
||||
assert.equal(flattenCodexImportPayload(null).ok, false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user