Files
Kilo-Org_kilocode/packages/opencode/test/tool/recall.test.ts
T
Mark IJbema 5d5fab36bb fix(cli): repair broken imports and typecheck after opencode v1.14.29 merge
Fixes 257 typecheck errors surfaced after the upstream merge:

- Update imports to new module paths after upstream PR #24554 removed
  module barrel files (@/config, @/session, @/util, etc.) and PR #24309
  renamed @opencode-ai/shared to @opencode-ai/core.
- Migrate loadMode to use Info.zod.safeParse for consistency with
  loadAgents and ConfigCommand (unblocks KilocodeConfig.handleInvalid).
- Drop removed Npm.outdated mock from tests.
- Add missing Config.defaultLayer to bash-permission-metadata test runtime.
- Fix Tips component signature (kilocode drops upstream's 'connected' prop).
- Fix toast.tsx undefined 'duration' reference (should be toastOptions.duration).
- Move filesystem-containment test from deleted shared package to core.
2026-04-30 17:16:31 +02:00

153 lines
5.4 KiB
TypeScript

// kilocode_change - new file
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test"
import { $ } from "bun"
import { Effect } from "effect"
import path from "path"
import { Instance } from "../../src/project/instance"
import * as Config from "../../src/config/config"
import { RecallTool } from "../../src/tool/recall"
import { AppRuntime } from "../../src/effect/app-runtime"
import { resetDatabase } from "../fixture/db"
import { tmpdir } from "../fixture/fixture"
import type { Tool } from "../../src/tool/tool"
import { SessionID, MessageID } from "../../src/session/schema"
import { RemoteSender } from "../../src/kilo-sessions/remote-sender"
beforeEach(() => {
spyOn(RemoteSender, "create").mockReturnValue({ handle() {}, dispose() {} })
})
const ctx: Tool.Context = {
sessionID: SessionID.make("ses_test"),
messageID: MessageID.make("msg_test"),
callID: "call_test",
agent: "code",
abort: AbortSignal.any([]),
messages: [],
metadata: () => Effect.void,
ask: () => Effect.void,
}
afterEach(async () => {
mock.restore()
await resetDatabase()
})
describe("tool.recall", () => {
test("search is limited to the current project worktrees", async () => {
await using first = await tmpdir({ git: true })
await using second = await tmpdir({ git: true })
const worktree = path.join(first.path, "..", path.basename(first.path) + "-worktree")
try {
await $`git worktree add ${worktree} -b test-branch-${Date.now()}`.cwd(first.path).quiet()
await Bun.write(path.join(first.path, ".git", "opencode"), "stale-project-id")
spyOn(Config, "get").mockImplementation(
async () => ({ share: "manual" }) as Awaited<ReturnType<typeof Config.get>>,
)
try {
const { Session } = await import("../../src/session/session")
await Instance.provide({
directory: first.path,
fn: async () => Session.create({ title: "search-target root" }),
})
await Instance.provide({
directory: worktree,
fn: async () => Session.create({ title: "search-target worktree" }),
})
await Instance.provide({
directory: second.path,
fn: async () => Session.create({ title: "search-target other" }),
})
const result = await Instance.provide({
directory: first.path,
fn: async () => {
const info = await AppRuntime.runPromise(RecallTool)
const tool = await AppRuntime.runPromise(info.init())
return AppRuntime.runPromise(tool.execute({ mode: "search", query: "search-target" }, ctx))
},
})
expect(result.output).toContain("search-target root")
expect(result.output).toContain("search-target worktree")
expect(result.output).not.toContain("search-target other")
} finally {
mock.restore()
}
} finally {
await $`git worktree remove ${worktree}`.cwd(first.path).quiet().nothrow()
}
})
test("read rejects sessions from another project", async () => {
await using first = await tmpdir({ git: true })
await using second = await tmpdir({ git: true })
spyOn(Config, "get").mockImplementation(async () => ({ share: "manual" }) as Awaited<ReturnType<typeof Config.get>>)
try {
const { Session } = await import("../../src/session/session")
const session = await Instance.provide({
directory: second.path,
fn: async () => Session.create({ title: "other-project-session" }),
})
const err = await Instance.provide({
directory: first.path,
fn: async () => {
const info = await AppRuntime.runPromise(RecallTool)
const tool = await AppRuntime.runPromise(info.init())
return AppRuntime.runPromise(tool.execute({ mode: "read", sessionID: session.id }, ctx)).catch(
(error: unknown) => error as Error,
)
},
})
expect(err).toBeInstanceOf(Error)
expect((err as Error).message).toContain("belongs to a different workspace")
} finally {
mock.restore()
}
})
test("read allows sessions from sibling worktrees when project IDs drift", async () => {
await using first = await tmpdir({ git: true })
const worktree = path.join(first.path, "..", path.basename(first.path) + "-worktree")
try {
await $`git worktree add ${worktree} -b test-branch-${Date.now()}`.cwd(first.path).quiet()
await Bun.write(path.join(first.path, ".git", "opencode"), "stale-project-id")
spyOn(Config, "get").mockImplementation(
async () => ({ share: "manual" }) as Awaited<ReturnType<typeof Config.get>>,
)
try {
const { Session } = await import("../../src/session/session")
const session = await Instance.provide({
directory: worktree,
fn: async () => Session.create({ title: "worktree readable" }),
})
const result = await Instance.provide({
directory: first.path,
fn: async () => {
const info = await AppRuntime.runPromise(RecallTool)
const tool = await AppRuntime.runPromise(info.init())
return AppRuntime.runPromise(tool.execute({ mode: "read", sessionID: session.id }, ctx))
},
})
expect(result.output).toContain("# Session: worktree readable")
} finally {
mock.restore()
}
} finally {
await $`git worktree remove ${worktree}`.cwd(first.path).quiet().nothrow()
}
})
})