Skip to content

Commit 75c0ace

Browse files
committed
test(auth): add tests for multi-profile auth
- Auth profiles: set/get with profiles, list profiles, hasDefault, setDefault swap, remove with profile - parseKey/buildKey/validateProfileName utilities - parseModel with provider:profile/model format
1 parent fd9c1ca commit 75c0ace

2 files changed

Lines changed: 129 additions & 0 deletions

File tree

packages/opencode/test/auth/auth.test.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,116 @@ describe("Auth", () => {
7373
}),
7474
)
7575
})
76+
77+
describe("Auth Profiles", () => {
78+
it.instance("set and get with profile", () =>
79+
Effect.gen(function* () {
80+
const auth = yield* Auth.Service
81+
yield* auth.set("openrouter", { type: "api", key: "sk-default" })
82+
yield* auth.set("openrouter", { type: "api", key: "sk-work" }, "work")
83+
yield* auth.set("openrouter", { type: "api", key: "sk-personal" }, "personal")
84+
85+
const def = yield* auth.get("openrouter")
86+
expect(def?.type).toBe("api")
87+
if (def?.type === "api") expect(def.key).toBe("sk-default")
88+
89+
const work = yield* auth.get("openrouter", "work")
90+
expect(work?.type).toBe("api")
91+
if (work?.type === "api") expect(work.key).toBe("sk-work")
92+
93+
const personal = yield* auth.get("openrouter", "personal")
94+
expect(personal?.type).toBe("api")
95+
if (personal?.type === "api") expect(personal.key).toBe("sk-personal")
96+
}),
97+
)
98+
99+
it.instance("profiles lists all profiles for a provider", () =>
100+
Effect.gen(function* () {
101+
const auth = yield* Auth.Service
102+
yield* auth.set("openrouter", { type: "api", key: "sk-default" })
103+
yield* auth.set("openrouter", { type: "api", key: "sk-work" }, "work")
104+
105+
const profiles = yield* auth.profiles("openrouter")
106+
expect(profiles.length).toBe(2)
107+
const names = profiles.map((p) => p.profile).sort()
108+
expect(names).toEqual([undefined, "work"])
109+
}),
110+
)
111+
112+
it.instance("hasDefault returns true when default exists", () =>
113+
Effect.gen(function* () {
114+
const auth = yield* Auth.Service
115+
yield* auth.set("openrouter", { type: "api", key: "sk-default" })
116+
117+
expect(yield* auth.hasDefault("openrouter")).toBe(true)
118+
expect(yield* auth.hasDefault("anthropic")).toBe(false)
119+
}),
120+
)
121+
122+
it.instance("setDefault swaps named profile to default", () =>
123+
Effect.gen(function* () {
124+
const auth = yield* Auth.Service
125+
yield* auth.set("openrouter", { type: "api", key: "sk-old-default" })
126+
yield* auth.set("openrouter", { type: "api", key: "sk-work" }, "work")
127+
128+
yield* auth.setDefault("openrouter", "work")
129+
130+
const def = yield* auth.get("openrouter")
131+
expect(def?.type).toBe("api")
132+
if (def?.type === "api") expect(def.key).toBe("sk-work")
133+
134+
const old = yield* auth.get("openrouter", "work")
135+
expect(old?.type).toBe("api")
136+
if (old?.type === "api") expect(old.key).toBe("sk-old-default")
137+
}),
138+
)
139+
140+
it.instance("remove with profile only removes that profile", () =>
141+
Effect.gen(function* () {
142+
const auth = yield* Auth.Service
143+
yield* auth.set("openrouter", { type: "api", key: "sk-default" })
144+
yield* auth.set("openrouter", { type: "api", key: "sk-work" }, "work")
145+
146+
yield* auth.remove("openrouter", "work")
147+
148+
const def = yield* auth.get("openrouter")
149+
expect(def?.type).toBe("api")
150+
151+
const work = yield* auth.get("openrouter", "work")
152+
expect(work).toBeUndefined()
153+
}),
154+
)
155+
})
156+
157+
describe("Auth parseKey / buildKey", () => {
158+
it("parseKey returns providerID only for simple keys", () => {
159+
expect(Auth.parseKey("openrouter")).toEqual({ providerID: "openrouter" })
160+
expect(Auth.parseKey("anthropic")).toEqual({ providerID: "anthropic" })
161+
})
162+
163+
it("parseKey splits providerID and profile", () => {
164+
expect(Auth.parseKey("openrouter:work")).toEqual({ providerID: "openrouter", profile: "work" })
165+
expect(Auth.parseKey("anthropic:personal")).toEqual({ providerID: "anthropic", profile: "personal" })
166+
})
167+
168+
it("buildKey creates simple key when no profile", () => {
169+
expect(Auth.buildKey("openrouter")).toBe("openrouter")
170+
expect(Auth.buildKey("openrouter", undefined)).toBe("openrouter")
171+
})
172+
173+
it("buildKey creates composite key with profile", () => {
174+
expect(Auth.buildKey("openrouter", "work")).toBe("openrouter:work")
175+
})
176+
177+
it("validateProfileName accepts valid names", () => {
178+
expect(Auth.validateProfileName("work")).toBe(true)
179+
expect(Auth.validateProfileName("my-profile")).toBe(true)
180+
expect(Auth.validateProfileName("profile_1")).toBe(true)
181+
})
182+
183+
it("validateProfileName rejects invalid names", () => {
184+
expect(Auth.validateProfileName("")).toBe(false)
185+
expect(Auth.validateProfileName("my profile")).toBe(false)
186+
expect(Auth.validateProfileName("my@profile")).toBe(false)
187+
})
188+
})

packages/opencode/test/provider/provider.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,12 +335,28 @@ test("parseModel correctly parses provider/model string", () => {
335335
const result = Provider.parseModel("anthropic/claude-sonnet-4")
336336
expect(String(result.providerID)).toBe("anthropic")
337337
expect(String(result.modelID)).toBe("claude-sonnet-4")
338+
expect(result.profile).toBeUndefined()
338339
})
339340

340341
test("parseModel handles model IDs with slashes", () => {
341342
const result = Provider.parseModel("openrouter/anthropic/claude-3-opus")
342343
expect(String(result.providerID)).toBe("openrouter")
343344
expect(String(result.modelID)).toBe("anthropic/claude-3-opus")
345+
expect(result.profile).toBeUndefined()
346+
})
347+
348+
test("parseModel extracts profile from provider:profile/model format", () => {
349+
const result = Provider.parseModel("openrouter:work/claude-sonnet-4")
350+
expect(String(result.providerID)).toBe("openrouter")
351+
expect(result.profile).toBe("work")
352+
expect(String(result.modelID)).toBe("claude-sonnet-4")
353+
})
354+
355+
test("parseModel handles profile with model IDs containing slashes", () => {
356+
const result = Provider.parseModel("openrouter:personal/anthropic/claude-3-opus")
357+
expect(String(result.providerID)).toBe("openrouter")
358+
expect(result.profile).toBe("personal")
359+
expect(String(result.modelID)).toBe("anthropic/claude-3-opus")
344360
})
345361

346362
it.instance("defaultModel returns first available model when no config set", () =>

0 commit comments

Comments
 (0)