Files
Kilo-Org_kilocode/packages/opencode/test/kilocode/kilo-loader-auth.test.ts
T
Imanol Maiztegui bacfa4ae62 Effect Migration for Kilo callsites (#10572)
* refactor(opencode): migrate agent promise helpers to kilocode module and adopt Effect-based tests

Remove legacy promise-based helpers (`get`, `list`, `defaultAgent`, `remove`)
from the core agent module and update callsites to use the dedicated
`@/kilocode/agent` module directly. Migrate kilocode-specific tests from
`WithInstance.provide` patterns to the `testEffect` helper with Effect
generators for cleaner, more idiomatic test code.

- Remove `makeRuntime` import and exported promise helpers from agent.ts
- Update HTTP API handlers to import from `@/kilocode/agent` instead of
  re-exported `Agent` namespace
- Rewrite agent tests to use `load()` helper or Effect service access
- Convert agent-global-config-dirs and agent-skill-permissions tests to
  `testEffect` pattern
- Reorder SDK type definitions (BackgroundProcessLogs/WorkspaceWarpError)

* chore(auth): remove legacy promise helpers and replace with direct Effect service access

Eliminate the exported promise-based convenience functions (`get`, `all`,
`set`, `remove`) from the Auth module and replace all callsites with
explicit `AppRuntime.runPromise` or `makeRuntime` invocations that access
`Auth.Service` directly through Effect's service pattern.

- Delete `makeRuntime`-backed promise helpers from auth/index.ts
- Update CLI entrypoint, kilo-sessions, indexing, and server instance to
  use `AppRuntime.runPromise(Auth.Service.use(...))` for auth access
- Inject `Auth.Service` as a dependency into the ModelsDev layer and
  consume it via Effect generator instead of promise wrapper
- Create a local `makeRuntime` instance in model-cache.ts for non-Effect
  callsites that still need promise-based auth access
- Rewrite tests to manipulate auth.json directly on disk with proper
  save/restore semantics instead of relying on removed helpers

* refactor(suggestion): convert suggest tool to Effect-native with injected Command dependency

Transform the suggestion tool from async/promise-based implementation to
idiomatic Effect generators with explicit dependency injection of the
Command service rather than importing and calling module-level helpers.

- Convert `resolvePrompt` from async function to Effect generator that
  accepts a `Command.Interface` parameter
- Refactor `SuggestTool` definition to yield `Command.Service` from the
  Effect context and thread it through to `resolvePrompt`
- Add `Command.Service` as a dependency to the tool registry layer and
  provide `Command.defaultLayer` in both production and test wiring
- Remove unused `makeRuntime` import and exported `get` helper from
  command/index.ts
- Add explicit type annotations to Auth delegate in server instance
- Rewrite suggestion tests to use `testEffect` helper with a mock
  `Command.Service` layer instead of spying on module exports

* feat(git): migrate WorktreeFamily to Effect service and wire Git.Service as dependency

Convert WorktreeFamily.list from an async function using legacy promise
helpers to an Effect generator that yields Git.Service from context,
eliminating the need for the removed `run` promise wrapper in git/index.

- Replace `WorktreeFamily.list()` async function with Effect.fn generator
  that obtains Git.Service and InstanceState from the Effect context
- Remove legacy `makeRuntime`/`run`/`runPromise` exports from git module
- Update RecallTool to thread Git.Service through to WorktreeFamily calls
  via EffectBridge
- Add Git.Service as a required dependency in tool registry and HTTP
  server route layers
- Update all test layers to provide Git.defaultLayer

* chore(mcp): replace legacy promise helpers with Effect-native AppRuntime calls

Remove exported promise-based `status`, `connect`, and `disconnect`
helpers from MCP module and convert the network recovery callsite in
SessionNetwork to use AppRuntime.runPromise with Effect.gen directly.

* refactor(auth): adopt makeRuntime helper for Auth service resolution in kilo modules

Replace AppRuntime.runPromise with locally scoped makeRuntime instances
in kilo-sessions and kilocode/indexing modules, removing the dependency
on the global AppRuntime singleton for Auth service access.

* fix(test): simplify cleanup error handling in provider test

Replace try-catch block with promise .catch() for file unlink operation
during test teardown.
2026-05-26 12:51:21 +02:00

173 lines
4.8 KiB
TypeScript

// kilocode_change - new file
//
// Tests that the kilo custom loader keeps paid models visible without authentication.
// Mocks fetchKiloModels from @kilocode/kilo-gateway to avoid real network
// calls (which fail on Windows CI).
import { test, expect, mock } from "bun:test"
import path from "path"
import { unlink } from "fs/promises"
// Bun's mock.module() is process-wide and permanent — it replaces the module
// for ALL test files in the same runner process. To avoid breaking other tests
// that import @kilocode/kilo-gateway, we spread the real exports and only
// override fetchKiloModels with a stub that returns both free and paid models.
const real = await import("@kilocode/kilo-gateway")
mock.module("@kilocode/kilo-gateway", () => ({
...real,
fetchKiloModels: async () => ({
models: {
"free-model": {
id: "free-model",
name: "Free Model",
cost: { input: 0, output: 0 },
limit: { context: 128000, output: 4096 },
},
"paid-model": {
id: "paid-model",
name: "Paid Model",
cost: { input: 1.0, output: 2.0 },
limit: { context: 128000, output: 4096 },
},
},
}),
}))
import { tmpdir } from "../fixture/fixture"
import { Global } from "@opencode-ai/core/global"
import { WithInstance } from "../../src/project/with-instance"
import { Provider } from "../../src/provider/provider"
import { ProviderID } from "../../src/provider/schema"
import { Filesystem } from "../../src/util/filesystem"
import { ModelCache } from "../../src/provider/model-cache"
function paid(providers: Awaited<ReturnType<typeof Provider.list>>) {
const item = providers[ProviderID.kilo]
expect(item).toBeDefined()
return Object.values(item.models).filter((model) => model.cost.input > 0).length
}
const authPath = path.join(Global.Path.data, "auth.json")
test("kilo loader keeps paid models without auth and when config apiKey is present", async () => {
// Reset state that may be stale from other test files sharing this process.
// Persisted auth from other tests and ModelCache's TTL map must not affect this test.
const prev = await Filesystem.readText(authPath).catch(() => undefined)
try {
await Filesystem.write(authPath, JSON.stringify({}))
ModelCache.clear("kilo")
await using base = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "kilo.json"),
JSON.stringify({
$schema: "https://app.kilo.ai/config.json",
}),
)
},
})
const none = await WithInstance.provide({
directory: base.path,
fn: async () => paid(await Provider.list()),
})
await using keyed = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "kilo.json"),
JSON.stringify({
$schema: "https://app.kilo.ai/config.json",
provider: {
kilo: {
options: {
apiKey: "test-key",
},
},
},
}),
)
},
})
const count = await WithInstance.provide({
directory: keyed.path,
fn: async () => paid(await Provider.list()),
})
expect(none).toBeGreaterThan(0)
expect(count).toBeGreaterThan(0)
} finally {
if (prev !== undefined) {
await Filesystem.write(authPath, prev)
}
if (prev === undefined) {
await unlink(authPath).catch(() => undefined)
}
}
})
test("kilo loader keeps paid models without auth and when auth exists", async () => {
const prev = await Filesystem.readText(authPath).catch(() => undefined)
try {
await Filesystem.write(authPath, JSON.stringify({}))
ModelCache.clear("kilo")
await using base = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "kilo.json"),
JSON.stringify({
$schema: "https://app.kilo.ai/config.json",
}),
)
},
})
const none = await WithInstance.provide({
directory: base.path,
fn: async () => paid(await Provider.list()),
})
await using keyed = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "kilo.json"),
JSON.stringify({
$schema: "https://app.kilo.ai/config.json",
}),
)
},
})
await Filesystem.write(
authPath,
JSON.stringify({
kilo: {
type: "api",
key: "test-key",
},
}),
)
const count = await WithInstance.provide({
directory: keyed.path,
fn: async () => paid(await Provider.list()),
})
expect(none).toBeGreaterThan(0)
expect(count).toBeGreaterThan(0)
} finally {
if (prev !== undefined) {
await Filesystem.write(authPath, prev)
}
if (prev === undefined) {
await unlink(authPath).catch(() => undefined)
}
}
})