|
| 1 | +import { describe, it, expect, beforeEach, afterEach } from "vitest"; |
| 2 | +import { mkdtempSync, mkdirSync, rmSync } from "node:fs"; |
| 3 | +import { tmpdir } from "node:os"; |
| 4 | +import { join, resolve } from "node:path"; |
| 5 | +import { windowsClaudeConfigPath } from "./setup.js"; |
| 6 | + |
| 7 | +/** |
| 8 | + * 윈도우 Claude Desktop 은 MSIX 패키지라 앱 안에서 본 %APPDATA% 가 패키지 전용 위치로 |
| 9 | + * 리다이렉트된다. setup 이 밖에서 보이는 %APPDATA%\Claude 에 써 두면 설치는 성공했다고 |
| 10 | + * 나오는데 앱은 그 파일을 읽지 않아 서버가 나타나지 않는다. |
| 11 | + * |
| 12 | + * 실제 경로 선택은 환경변수와 디렉터리 존재 여부로만 갈리므로, 임시 디렉터리로 두 배치를 |
| 13 | + * 만들어 어느 쪽을 고르는지 본다(플랫폼 무관). |
| 14 | + */ |
| 15 | +describe("windowsClaudeConfigPath", () => { |
| 16 | + let root: string; |
| 17 | + let home: string; |
| 18 | + const saved = { appData: process.env["APPDATA"], localAppData: process.env["LOCALAPPDATA"] }; |
| 19 | + |
| 20 | + beforeEach(() => { |
| 21 | + root = mkdtempSync(join(tmpdir(), "setup-path-test-")); |
| 22 | + home = join(root, "home"); |
| 23 | + process.env["APPDATA"] = join(root, "Roaming"); |
| 24 | + process.env["LOCALAPPDATA"] = join(root, "Local"); |
| 25 | + mkdirSync(process.env["APPDATA"], { recursive: true }); |
| 26 | + mkdirSync(process.env["LOCALAPPDATA"], { recursive: true }); |
| 27 | + }); |
| 28 | + |
| 29 | + afterEach(() => { |
| 30 | + if (saved.appData === undefined) delete process.env["APPDATA"]; |
| 31 | + else process.env["APPDATA"] = saved.appData; |
| 32 | + if (saved.localAppData === undefined) delete process.env["LOCALAPPDATA"]; |
| 33 | + else process.env["LOCALAPPDATA"] = saved.localAppData; |
| 34 | + rmSync(root, { recursive: true, force: true }); |
| 35 | + }); |
| 36 | + |
| 37 | + const roamingFallback = () => |
| 38 | + resolve(process.env["APPDATA"]!, "Claude/claude_desktop_config.json"); |
| 39 | + |
| 40 | + it("MSIX 패키지 폴더가 있으면 앱이 실제로 읽는 경로를 고른다", () => { |
| 41 | + const pkgConfigDir = join( |
| 42 | + process.env["LOCALAPPDATA"]!, |
| 43 | + "Packages", |
| 44 | + "Claude_pzs8sxrjxfjjc", |
| 45 | + "LocalCache", |
| 46 | + "Roaming", |
| 47 | + "Claude", |
| 48 | + ); |
| 49 | + mkdirSync(pkgConfigDir, { recursive: true }); |
| 50 | + |
| 51 | + expect(windowsClaudeConfigPath(home)).toBe( |
| 52 | + resolve(pkgConfigDir, "claude_desktop_config.json"), |
| 53 | + ); |
| 54 | + }); |
| 55 | + |
| 56 | + it("Packages 폴더 자체가 없으면 %APPDATA% 를 쓴다", () => { |
| 57 | + expect(windowsClaudeConfigPath(home)).toBe(roamingFallback()); |
| 58 | + }); |
| 59 | + |
| 60 | + it("Claude 패키지가 없으면 %APPDATA% 를 쓴다", () => { |
| 61 | + mkdirSync(join(process.env["LOCALAPPDATA"]!, "Packages", "SomeOther_abc123"), { |
| 62 | + recursive: true, |
| 63 | + }); |
| 64 | + |
| 65 | + expect(windowsClaudeConfigPath(home)).toBe(roamingFallback()); |
| 66 | + }); |
| 67 | + |
| 68 | + it("최초 설치라 설정 디렉터리가 아직 없어도 그 패키지 자리를 쓴다", () => { |
| 69 | + // setup 을 처음 돌리는 사람이 정확히 이 상태다. 여기서 %APPDATA% 로 떨어지면 |
| 70 | + // 이 수정이 고치려는 바로 그 증상이 그대로 남는다. |
| 71 | + const pkg = join(process.env["LOCALAPPDATA"]!, "Packages", "Claude_pzs8sxrjxfjjc"); |
| 72 | + mkdirSync(pkg, { recursive: true }); |
| 73 | + |
| 74 | + expect(windowsClaudeConfigPath(home)).toBe( |
| 75 | + resolve(pkg, "LocalCache/Roaming/Claude/claude_desktop_config.json"), |
| 76 | + ); |
| 77 | + }); |
| 78 | + |
| 79 | + it("후보가 여럿이면 설정 디렉터리가 있는 쪽을 고른다", () => { |
| 80 | + const packages = join(process.env["LOCALAPPDATA"]!, "Packages"); |
| 81 | + mkdirSync(join(packages, "Claude_aaaaaaaaaaaaa", "LocalCache", "Roaming"), { recursive: true }); |
| 82 | + const live = join(packages, "Claude_zzzzzzzzzzzzz", "LocalCache", "Roaming", "Claude"); |
| 83 | + mkdirSync(live, { recursive: true }); |
| 84 | + |
| 85 | + expect(windowsClaudeConfigPath(home)).toBe(resolve(live, "claude_desktop_config.json")); |
| 86 | + }); |
| 87 | + |
| 88 | + it("후보가 여럿인데 가릴 단서가 없으면 %APPDATA% 를 쓴다", () => { |
| 89 | + const packages = join(process.env["LOCALAPPDATA"]!, "Packages"); |
| 90 | + mkdirSync(join(packages, "Claude_aaaaaaaaaaaaa"), { recursive: true }); |
| 91 | + mkdirSync(join(packages, "Claude_zzzzzzzzzzzzz"), { recursive: true }); |
| 92 | + |
| 93 | + expect(windowsClaudeConfigPath(home)).toBe(roamingFallback()); |
| 94 | + }); |
| 95 | +}); |
0 commit comments