-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathprofiles.test.ts
More file actions
128 lines (113 loc) · 5.06 KB
/
Copy pathprofiles.test.ts
File metadata and controls
128 lines (113 loc) · 5.06 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { expect, test } from "bun:test";
import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import {
loadProfile,
projectProfilePath,
profilesDir,
resolveProfile,
} from "./config/profiles.js";
function makeTmp(): string {
return join(tmpdir(), `interchange-profiles-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
}
test("profilesDir returns ~/.corbits/profiles", () => {
const result = profilesDir("/home/user");
expect(result).toBe("/home/user/.corbits/profiles");
});
test("projectProfilePath returns <cwd>/.corbits/profile.json", () => {
const result = projectProfilePath("/my/project");
expect(result).toBe("/my/project/.corbits/profile.json");
});
test("loadProfile returns null for missing file", async () => {
const result = await loadProfile("/no/such/file/profile.json");
expect(result).toBeNull();
});
test("loadProfile parses valid profile", async () => {
const dir = makeTmp();
await mkdir(dir, { recursive: true });
const path = join(dir, "profile.json");
await writeFile(path, JSON.stringify({ model: "claude-opus-4-8", maxTurns: 50 }));
const result = await loadProfile(path);
expect(result).toEqual({ model: "claude-opus-4-8", maxTurns: 50 });
});
test("loadProfile parses systemPromptExtensions", async () => {
const dir = makeTmp();
await mkdir(dir, { recursive: true });
const path = join(dir, "profile.json");
await writeFile(path, JSON.stringify({ systemPromptExtensions: ["no-destructive-migrations"] }));
const result = await loadProfile(path);
expect(result).toEqual({ systemPromptExtensions: ["no-destructive-migrations"] });
});
test("loadProfile rejects unknown keys", async () => {
const dir = makeTmp();
await mkdir(dir, { recursive: true });
const path = join(dir, "profile.json");
await writeFile(path, JSON.stringify({ model: "x", unknownKey: true }));
await expect(loadProfile(path)).rejects.toThrow(/unknownKey must be removed/);
});
test("loadProfile rejects invalid maxTurns", async () => {
const dir = makeTmp();
await mkdir(dir, { recursive: true });
const path = join(dir, "profile.json");
await writeFile(path, JSON.stringify({ maxTurns: -1 }));
await expect(loadProfile(path)).rejects.toThrow(/maxTurns/);
});
test("loadProfile rejects non-array systemPromptExtensions", async () => {
const dir = makeTmp();
await mkdir(dir, { recursive: true });
const path = join(dir, "profile.json");
await writeFile(path, JSON.stringify({ systemPromptExtensions: "bad" }));
await expect(loadProfile(path)).rejects.toThrow(/systemPromptExtensions/);
});
test("loadProfile rejects invalid JSON", async () => {
const dir = makeTmp();
await mkdir(dir, { recursive: true });
const path = join(dir, "profile.json");
await writeFile(path, "not json");
await expect(loadProfile(path)).rejects.toThrow(/Invalid JSON/);
});
test("resolveProfile returns empty object when no profile files exist", async () => {
const cwd = makeTmp();
await mkdir(cwd, { recursive: true });
const result = await resolveProfile(cwd);
expect(result).toEqual({});
});
test("resolveProfile applies project profile fields", async () => {
const cwd = makeTmp();
const dir = join(cwd, ".corbits");
await mkdir(dir, { recursive: true });
await writeFile(join(dir, "profile.json"), JSON.stringify({ model: "claude-sonnet", maxTurns: 30 }));
const result = await resolveProfile(cwd);
expect(result.model).toBe("claude-sonnet");
expect(result.maxTurns).toBe(30);
});
test("resolveProfile surfaces profile name when set", async () => {
const cwd = makeTmp();
const dir = join(cwd, ".corbits");
await mkdir(dir, { recursive: true });
await writeFile(join(dir, "profile.json"), JSON.stringify({ profile: "work" }));
const result = await resolveProfile(cwd);
expect(result.profile).toBe("work");
});
test("resolveProfile: project profile fields override named profile fields", async () => {
const home = makeTmp();
const cwd = makeTmp();
const namedDir = join(home, ".corbits", "profiles");
await mkdir(namedDir, { recursive: true });
await writeFile(join(namedDir, "work.json"), JSON.stringify({ model: "base-model", maxTurns: 10 }));
const localDir = join(cwd, ".corbits");
await mkdir(localDir, { recursive: true });
await writeFile(join(localDir, "profile.json"), JSON.stringify({ profile: "work", model: "override-model" }));
// We can't easily inject profilesDir home in resolveProfile without additional plumbing,
// so test the merge logic directly via the exported functions.
// Project profile model should win over named profile model.
const projectProfile = await loadProfile(join(localDir, "profile.json"));
const namedProfile = await loadProfile(join(namedDir, "work.json"));
const merged = { ...namedProfile };
if (projectProfile?.model !== undefined) merged.model = projectProfile.model;
if (projectProfile?.maxTurns !== undefined) merged.maxTurns = projectProfile.maxTurns;
expect(merged.model).toBe("override-model");
// maxTurns not in project profile so named profile value survives
expect(merged.maxTurns).toBe(10);
});