Files
Kilo-Org_kilocode/packages/opencode/test/kilocode/daemon.test.ts
T
Catriel Müller 680f50581a refactor: fix test
2026-06-12 12:23:11 -03:00

190 lines
6.4 KiB
TypeScript

import { afterEach, describe, expect, test } from "bun:test"
import path from "path"
import { Daemon } from "../../src/kilocode/daemon/daemon"
import { DaemonClient } from "../../src/kilocode/daemon/client"
import { tmpdir } from "../fixture/fixture"
const original = {
state: process.env.KILO_TEST_DAEMON_STATE_DIR,
log: process.env.KILO_TEST_DAEMON_LOG_DIR,
disabled: process.env.KILO_NO_DAEMON,
}
afterEach(async () => {
if (process.env.KILO_TEST_DAEMON_STATE_DIR !== original.state) await Daemon.stop().catch(() => undefined)
restore()
})
function restore() {
if (original.state === undefined) delete process.env.KILO_TEST_DAEMON_STATE_DIR
else process.env.KILO_TEST_DAEMON_STATE_DIR = original.state
if (original.log === undefined) delete process.env.KILO_TEST_DAEMON_LOG_DIR
else process.env.KILO_TEST_DAEMON_LOG_DIR = original.log
if (original.disabled === undefined) delete process.env.KILO_NO_DAEMON
else process.env.KILO_NO_DAEMON = original.disabled
}
function dirs(root: string) {
process.env.KILO_TEST_DAEMON_STATE_DIR = path.join(root, "state")
process.env.KILO_TEST_DAEMON_LOG_DIR = path.join(root, "log")
return {
XDG_DATA_HOME: path.join(root, "xdg-data"),
XDG_CONFIG_HOME: path.join(root, "xdg-config"),
XDG_STATE_HOME: path.join(root, "xdg-state"),
XDG_CACHE_HOME: path.join(root, "xdg-cache"),
KILO_TEST_DAEMON_EPHEMERAL_PORT: "1",
}
}
function opts(root: string): Daemon.Options {
return {
hostname: "127.0.0.1",
port: 0,
mdns: false,
mdnsDomain: "kilo.local",
cors: [],
command: [process.execPath, "--conditions=browser", path.join(process.cwd(), "src/index.ts")],
env: dirs(root),
timeout: 20_000,
}
}
describe("daemon manager", () => {
test("reports not running without daemon state", async () => {
await using tmp = await tmpdir()
dirs(tmp.path)
const status = await Daemon.status()
expect(status.running).toBe(false)
expect(status.stale).toBe(false)
expect(status.reason).toBe("not running")
})
test("strips inherited cwd flags from daemon child command", () => {
expect(Daemon.clean(["--conditions=browser", "--cwd", "packages/opencode", "--inspect"])).toStrictEqual([
"--conditions=browser",
"--inspect",
])
expect(Daemon.clean(["--cwd=packages/opencode", "--conditions=browser"])).toStrictEqual(["--conditions=browser"])
})
test("does not forward bundled bun entrypoints to the daemon child", () => {
const proc = {
argv: ["/tmp/kilo", "/$bunfs/root/src/index.js", "daemon", "start"],
execArgv: ["--user-agent=kilo/test", "--use-system-ca", "--"],
execPath: "/tmp/kilo",
}
expect(Daemon.command(undefined, proc)).toStrictEqual(["/tmp/kilo"])
expect(
Daemon.command(undefined, {
...proc,
argv: ["C:/tmp/kilo.exe", "B:/~BUN/root/src/index.js", "daemon", "start"],
execPath: "C:/tmp/kilo.exe",
}),
).toStrictEqual(["C:/tmp/kilo.exe"])
expect(
Daemon.command(undefined, {
...proc,
argv: ["C:/tmp/kilo.exe", "b:\\~BUN\\root\\src\\index.js", "daemon", "start"],
execPath: "C:/tmp/kilo.exe",
}),
).toStrictEqual(["C:/tmp/kilo.exe"])
})
test("forwards source entrypoints to the daemon child", () => {
expect(
Daemon.command(undefined, {
argv: ["/tmp/bun", "/tmp/kilo/src/index.ts", "daemon", "start"],
execArgv: ["--conditions=browser"],
execPath: "/tmp/bun",
}),
).toStrictEqual(["/tmp/bun", "--conditions=browser", "/tmp/kilo/src/index.ts"])
})
test("reuses one daemon across caller directories", async () => {
await using tmp = await tmpdir()
const env = opts(tmp.path)
const first = await Daemon.start(env)
const cwd = process.cwd()
try {
process.chdir(path.dirname(tmp.path))
const second = await Daemon.start(env)
expect(second.reused).toBe(true)
expect(second.state?.pid).toBe(first.state?.pid)
} finally {
process.chdir(cwd)
}
}, 20_000)
test("starts, reuses, authenticates, and stops a daemon", async () => {
await using tmp = await tmpdir()
const started = await Daemon.start(opts(tmp.path))
expect(started.started).toBe(true)
expect(started.running).toBe(true)
expect(started.state?.pid).toBeGreaterThan(0)
expect(started.state?.token).toBeTruthy()
expect(started.state?.port).toBeGreaterThan(0)
const blocked = await fetch(`${started.state!.url}/config?directory=${encodeURIComponent(tmp.path)}`)
expect(blocked.status).toBe(401)
const config = await fetch(`${started.state!.url}/config?directory=${encodeURIComponent(tmp.path)}`, {
headers: { authorization: `Basic ${started.state!.token}` },
})
expect(config.status).toBe(200)
const health = await fetch(`${started.state!.url}/global/health`, {
headers: { authorization: `Basic ${started.state!.token}` },
})
expect(health.status).toBe(200)
const reused = await Daemon.start(opts(tmp.path))
expect(reused.reused).toBe(true)
expect(reused.state?.pid).toBe(started.state?.pid)
const stopped = await Daemon.stop()
expect(stopped.stopped).toBe(true)
expect((await Daemon.status()).running).toBe(false)
const again = await Daemon.start(opts(tmp.path))
expect(again.running).toBe(true)
const restarted = await fetch(`${again.state!.url}/global/health`, {
headers: { authorization: `Basic ${again.state!.token}` },
})
expect(restarted.status).toBe(200)
}, 20_000)
test("daemon client does not start a daemon while attaching", async () => {
await using tmp = await tmpdir()
dirs(tmp.path)
const daemon = await DaemonClient.connect()
expect(daemon).toBeUndefined()
expect((await Daemon.status()).running).toBe(false)
})
test("daemon client honors the escape hatch", async () => {
await using tmp = await tmpdir()
const started = await Daemon.start(opts(tmp.path))
process.env.KILO_NO_DAEMON = "1"
const daemon = await DaemonClient.connect()
expect(daemon).toBeUndefined()
expect((await Daemon.status()).state?.pid).toBe(started.state?.pid)
}, 20_000)
test("daemon client returns authenticated attach settings", async () => {
await using tmp = await tmpdir()
const started = await Daemon.start(opts(tmp.path))
const daemon = await DaemonClient.connect()
expect(daemon?.url).toBe(started.state?.url)
expect(daemon?.headers.Authorization).toBe(`Basic ${daemon?.state.token}`)
}, 20_000)
})