Files

125 lines
3.8 KiB
TypeScript

// kilocode_change - new file
// Regression test: OAuth accountId must flow into model fetch as kilocodeOrganizationId
// When a user logs in via OAuth and selects an enterprise organization, the model fetch
// should use the organization-specific endpoint, not the personal endpoint.
import { test, expect, mock } from "bun:test"
import path from "path"
// Capture the options passed to fetchKiloModels
let captured: any = undefined
mock.module("@kilocode/kilo-gateway", () => ({
fetchKiloModels: async (options: any) => {
captured = options
return {
"test-model": {
id: "test-model",
name: "Test Model",
cost: { input: 0.001, output: 0.002 },
limit: { context: 128000, output: 4096 },
},
}
},
KILO_OPENROUTER_BASE: "https://api.kilo.ai/api/openrouter",
}))
// Mock BunProc and default plugins to prevent actual installations during tests
mock.module("../../src/bun/index", () => ({
BunProc: {
install: async (pkg: string) => {
const lastAtIndex = pkg.lastIndexOf("@")
return lastAtIndex > 0 ? pkg.substring(0, lastAtIndex) : pkg
},
run: async () => {
throw new Error("BunProc.run should not be called in tests")
},
which: () => process.execPath,
InstallFailedError: class extends Error {},
},
}))
const mockPlugin = () => ({})
mock.module("opencode-copilot-auth", () => ({ default: mockPlugin }))
mock.module("opencode-anthropic-auth", () => ({ default: mockPlugin }))
mock.module("@gitlab/opencode-gitlab-auth", () => ({ default: mockPlugin }))
import { tmpdir } from "../fixture/fixture"
import { Instance } from "../../src/project/instance"
import { Auth } from "../../src/auth"
import { ModelCache } from "../../src/provider/model-cache"
test("model fetch uses accountId from OAuth auth as kilocodeOrganizationId", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://app.kilo.ai/config.json",
}),
)
},
})
await Instance.provide({
directory: tmp.path,
init: async () => {
// Simulate an OAuth login where user selected an enterprise organization
await Auth.set("kilo", {
type: "oauth",
access: "test-oauth-token",
refresh: "test-refresh-token",
expires: Date.now() + 3600000,
accountId: "org-enterprise-123",
})
},
fn: async () => {
// Reset captured and cache
captured = undefined
ModelCache.clear("kilo")
// Trigger model fetch through the cache
await ModelCache.fetch("kilo")
// The fetchKiloModels call should have received the organization ID
expect(captured).toBeDefined()
expect(captured.kilocodeToken).toBe("test-oauth-token")
expect(captured.kilocodeOrganizationId).toBe("org-enterprise-123")
},
})
})
test("model fetch without OAuth accountId does not set kilocodeOrganizationId", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://app.kilo.ai/config.json",
}),
)
},
})
await Instance.provide({
directory: tmp.path,
init: async () => {
// Simulate an OAuth login for a personal account (no accountId)
await Auth.set("kilo", {
type: "oauth",
access: "test-personal-token",
refresh: "test-refresh-token",
expires: Date.now() + 3600000,
})
},
fn: async () => {
captured = undefined
ModelCache.clear("kilo")
await ModelCache.fetch("kilo")
expect(captured).toBeDefined()
expect(captured.kilocodeToken).toBe("test-personal-token")
expect(captured.kilocodeOrganizationId).toBeUndefined()
},
})
})