opencode/packages/core/test/tool-todo.test.ts

80 lines
3.3 KiB
TypeScript

import { describe, expect } from "bun:test"
import { Effect, Layer, Stream } from "effect"
import { AppNodeBuilder } from "@opencode/core/effect/app-node-builder"
import { Config } from "@opencode/core/config"
import { Image } from "@opencode/core/image"
import { Permission } from "@opencode/core/permission"
import { Session } from "@opencode/core/session"
import { Tool } from "@opencode/core/tool"
import { TodoTool } from "@opencode/core/tool/plugin/todo"
import { Document, Info, type Entry } from "@opencode/schema/config"
import { ConfigExperimental } from "@opencode/schema/config/experimental"
import { testEffect } from "./lib/effect"
import { imagePassthrough } from "./lib/image"
import { permissionLayer } from "./lib/permission"
import { executeTool, registerToolPlugin, toolDefinitions, toolIdentity } from "./lib/tool"
const sessionID = Session.ID.make("ses_todo_tool_test")
const enabled = new ConfigExperimental.Info({ todo: true })
const todos = [
{ content: "Write tests", status: "in_progress" },
{ content: "Ship it", status: "pending" },
{ content: "Read the code", status: "completed" },
{ content: "Rewrite everything", status: "cancelled" },
] as const
const it = testEffect(
AppNodeBuilder.build(Tool.node, [Image.node.replace(imagePassthrough), Permission.node.replace(permissionLayer())]),
)
const setup = (experimental: ConfigExperimental.Info | undefined) =>
Effect.gen(function* () {
yield* registerToolPlugin(TodoTool.Plugin, { event: { subscribe: () => Stream.never } }).pipe(
Effect.provide(Layer.mock(Config.Service)({ entries: () => Effect.succeed([document(experimental)]) })),
Effect.provide(permissionLayer({ assert: () => Effect.void })),
)
return yield* Tool.Service
})
const names = (registry: Tool.Interface) =>
toolDefinitions(registry).pipe(Effect.map((definitions) => definitions.map((definition) => definition.name)))
describe("TodoTool", () => {
it.effect("is absent unless experimental.todo is enabled", () =>
Effect.gen(function* () {
expect(yield* names(yield* setup(undefined))).not.toContain(TodoTool.name)
expect(yield* names(yield* setup(new ConfigExperimental.Info({ todo: false })))).not.toContain(TodoTool.name)
}),
)
it.effect("registers todowrite on the native tool list and summarizes the update", () =>
Effect.gen(function* () {
const registry = yield* setup(enabled)
const definition = (yield* toolDefinitions(registry)).find((tool) => tool.name === TodoTool.name)
expect(definition?.inputSchema).toHaveProperty("properties.todos.type", "array")
expect(definition?.inputSchema).toHaveProperty("properties.todos.items.required", ["content", "status"])
expect(
yield* executeTool(registry, {
sessionID,
...toolIdentity,
call: { type: "tool-call", id: "call-todo", name: TodoTool.name, input: { todos } },
}),
).toEqual({
status: "completed",
output: { todos },
content: [
{
type: "text",
text: 'Todos updated: 1/4 completed, 1 in progress, 1 pending, 1 cancelled. In progress: "Write tests"',
},
],
metadata: { todos },
})
}),
)
})
function document(experimental: ConfigExperimental.Info | undefined): Entry {
return new Document({ type: "document", info: new Info(experimental ? { experimental } : {}) })
}