mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-01 23:30:58 +00:00
80 lines
3.3 KiB
TypeScript
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 } : {}) })
|
|
}
|