import { test } from "node:test"; import assert from "node:assert/strict"; import { mkdtempSync, readFileSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { applyGrant, deliver } from "../credentials.ts"; test("applyGrant strips the refresh token a full grant on disk left behind", () => { const local = { claudeAiOauth: { accessToken: "at-old", refreshToken: "rt-must-not-survive" } }; const next = applyGrant(local, { accessToken: "at-new", expiresAt: 123 }); assert.equal(next.claudeAiOauth!.accessToken, "at-new"); assert.equal(next.claudeAiOauth!.expiresAt, 123); assert.ok(!("refreshToken" in next.claudeAiOauth!), "a node held onto a refresh token"); }); test("deliver writes the port-map shape, access-token-only, and never a refresh token", () => { const dir = mkdtempSync(join(tmpdir(), "anthropic-consumer-")); const path = join(dir, ".credentials.json"); // A prior interactive login left a full grant on disk. writeFileSync(path, JSON.stringify({ claudeAiOauth: { accessToken: "at-old", refreshToken: "rt-login" } })); deliver(path, { accessToken: "at-delivered", expiresAt: 999, subscriptionType: "max" }); const raw = readFileSync(path, "utf8"); const creds = JSON.parse(raw); assert.equal(creds.claudeAiOauth.accessToken, "at-delivered"); assert.equal(creds.claudeAiOauth.expiresAt, 999); assert.equal(creds.claudeAiOauth.subscriptionType, "max"); assert.doesNotMatch(raw, /rt-login/, "the refresh token is still on disk"); assert.ok(!("refreshToken" in creds.claudeAiOauth)); });