Files
Kilo-Org_kilocode/packages/opencode/test/kilocode/model-cache-org.test.ts
T
Imanol Maiztegui ad00b1461d refactor(kilocode): decouple provider-auth lifecycle, add cloud-fork CLI option, and migrate indexing config to Effect Schema
Extract `disposeAllInstancesAfterProviderAuthCallback` into a dedicated
module and invoke it from the provider OAuth callback handler instead of
coupling it to the ProviderAuth layer. This removes the InstanceStore
dependency from `ProviderAuth.layer`.

Additional changes:
- Add `--cloud-fork` CLI option to the run command for fetching sessions
  from cloud and continuing locally
- Replace the Zod-bridged `Schema.Any` indexing config with a native
  Effect `IndexingSchema` definition in kilo-indexing, propagating typed
  config through the SDK OpenAPI spec
- Use `optionalOmitUndefined` for `recommendedIndex` to emit clean JSON
- Fix `request.source.signal` access by guarding with `instanceof Request`
- Replace `Effect.catchAllCause` with `Effect.catchCause` in telemetry
- Migrate tests from `BashTool` to `ShellTool`, extract `TestConfig`
  fixture, and convert stats-subagent-cost tests to use `testEffect`
- Reorder SDK event union members to match updated OpenAPI schema
- Export `aggregateSessionStats` for Kilo regression test coverage
- Widen `setViewedSessions` input to accept readonly arrays
2026-05-19 13:09:03 +02:00

161 lines
4.7 KiB
TypeScript

// 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"
import * as Log from "@opencode-ai/core/util/log"
Log.init({ print: false })
// Capture the options passed to fetchKiloModels
let captured: any = undefined
mock.module("@kilocode/kilo-gateway", () => ({
fetchKiloModels: async (options: any) => {
captured = options
return {
models: {
"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 default plugins to prevent actual installations during tests
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 { WithInstance } from "../../src/project/with-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",
}),
)
},
})
// 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",
})
await WithInstance.provide({
directory: tmp.path,
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",
}),
)
},
})
// 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,
})
await WithInstance.provide({
directory: tmp.path,
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()
},
})
})
test("ModelCache.clear removes cached entry so next fetch hits the network", 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 Auth.set("kilo", {
type: "oauth",
access: "token-clear-test",
refresh: "refresh-clear",
expires: Date.now() + 3600000,
accountId: "org-clear",
})
await WithInstance.provide({
directory: tmp.path,
fn: async () => {
// Populate cache
captured = undefined
ModelCache.clear("kilo")
await ModelCache.fetch("kilo")
expect(captured).toBeDefined()
// Verify cache is populated — second fetch should NOT call fetchKiloModels
captured = undefined
await ModelCache.fetch("kilo")
expect(captured).toBeUndefined()
expect(ModelCache.get("kilo")).toBeDefined()
// Clear the cache
ModelCache.clear("kilo")
// get() should return undefined after clear
expect(ModelCache.get("kilo")).toBeUndefined()
// Next fetch should call fetchKiloModels again
captured = undefined
await ModelCache.fetch("kilo")
expect(captured).toBeDefined()
},
})
})