|
1 | | -import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; |
2 | 1 | import fs from "node:fs"; |
3 | | -import path from "node:path"; |
4 | 2 | import os from "node:os"; |
| 3 | +import path from "node:path"; |
| 4 | +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; |
5 | 5 | import { |
6 | | - mask, |
7 | | - normalizeProvider, |
8 | | - sortModels, |
9 | 6 | isLikelyPortkeyApiKeyPermissionError, |
10 | 7 | jsonRead, |
11 | | - settingsSetEnv, |
| 8 | + mask, |
| 9 | + normalizeProvider, |
| 10 | + readExistingConfig, |
| 11 | + removeShellRcBlock, |
| 12 | + settingsReadMcp, |
12 | 13 | settingsRemoveKeys, |
13 | | - settingsSetMcp, |
14 | 14 | settingsRemoveMcp, |
15 | | - settingsReadMcp, |
16 | | - writeShellRc, |
17 | | - removeShellRcBlock, |
18 | | - writeFileSecure, |
| 15 | + settingsSetEnv, |
| 16 | + settingsSetMcp, |
| 17 | + sortModels, |
19 | 18 | warnIfFileGroupOrWorldReadable, |
| 19 | + writeFileSecure, |
| 20 | + writeShellRc, |
20 | 21 | } from "../src/utils.js"; |
21 | 22 |
|
22 | 23 | function mode(file) { |
@@ -146,7 +147,6 @@ describe("jsonRead", () => { |
146 | 147 | }); |
147 | 148 | }); |
148 | 149 |
|
149 | | -// ── writeFileSecure (0600 for secret-bearing files) ─────────────────────────── |
150 | 150 |
|
151 | 151 | describe("writeFileSecure", () => { |
152 | 152 | let dir; |
@@ -371,3 +371,47 @@ describe("writeShellRc + removeShellRcBlock", () => { |
371 | 371 | expect(removeShellRcBlock(file)).toBe(false); |
372 | 372 | }); |
373 | 373 | }); |
| 374 | + |
| 375 | +// ── readExistingConfig (key discovery + placeholder guard) ──────────────────── |
| 376 | + |
| 377 | +describe("readExistingConfig", () => { |
| 378 | + let dir, cwd; |
| 379 | + beforeEach(() => { |
| 380 | + dir = tmpDir(); |
| 381 | + fs.mkdirSync(path.join(dir, ".git"), { recursive: true }); // mark project root |
| 382 | + fs.mkdirSync(path.join(dir, ".claude"), { recursive: true }); |
| 383 | + cwd = process.cwd(); |
| 384 | + process.chdir(dir); |
| 385 | + }); |
| 386 | + afterEach(() => { |
| 387 | + process.chdir(cwd); |
| 388 | + fs.rmSync(dir, { recursive: true, force: true }); |
| 389 | + }); |
| 390 | + |
| 391 | + function writeSharedSettings(env) { |
| 392 | + fs.writeFileSync( |
| 393 | + path.join(dir, ".claude", "settings.json"), |
| 394 | + JSON.stringify({ env }, null, 2) |
| 395 | + ); |
| 396 | + } |
| 397 | + |
| 398 | + it("recovers a literal key from an existing (pre-fix) config", () => { |
| 399 | + writeSharedSettings({ |
| 400 | + ANTHROPIC_BASE_URL: "https://api.portkey.ai/v1", |
| 401 | + ANTHROPIC_AUTH_TOKEN: "pk-live-REAL-KEY", |
| 402 | + }); |
| 403 | + const cfg = readExistingConfig(); |
| 404 | + expect(cfg.found).toBe(true); |
| 405 | + expect(cfg.portkeyKey).toBe("pk-live-REAL-KEY"); |
| 406 | + }); |
| 407 | + |
| 408 | + it("does NOT return the ${PORTKEY_API_KEY} placeholder as a usable key", () => { |
| 409 | + writeSharedSettings({ |
| 410 | + ANTHROPIC_BASE_URL: "https://api.portkey.ai/v1", |
| 411 | + ANTHROPIC_AUTH_TOKEN: "${PORTKEY_API_KEY}", |
| 412 | + }); |
| 413 | + const cfg = readExistingConfig(); |
| 414 | + expect(cfg.found).toBe(true); |
| 415 | + expect(cfg.portkeyKey).toBe(""); |
| 416 | + }); |
| 417 | +}); |
0 commit comments