From db20e11c6a01128bfc66c230a4d5f8562b105250 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sat, 30 May 2026 20:25:34 +0900 Subject: [PATCH] test(delegate-task): isolate browser provider coverage --- .../tools-browser-provider.test.ts | 163 ++++++++++++++++++ src/tools/delegate-task/tools.test.ts | 115 +----------- 2 files changed, 164 insertions(+), 114 deletions(-) create mode 100644 src/tools/delegate-task/tools-browser-provider.test.ts diff --git a/src/tools/delegate-task/tools-browser-provider.test.ts b/src/tools/delegate-task/tools-browser-provider.test.ts new file mode 100644 index 000000000..27b920c08 --- /dev/null +++ b/src/tools/delegate-task/tools-browser-provider.test.ts @@ -0,0 +1,163 @@ +/// + +import { mkdirSync, rmSync, writeFileSync } from "node:fs" +import { tmpdir } from "node:os" +import { join } from "node:path" +import { clearSkillCache } from "../../features/opencode-skill-loader/skill-content" + +declare const require: NodeJS.Require + +const { afterEach, beforeEach, describe, expect, test } = require("bun:test") + +const SYSTEM_DEFAULT_MODEL = "anthropic/claude-sonnet-4-6" +const TOOL_CONTEXT = { + sessionID: "parent-session", + messageID: "parent-message", + agent: "sisyphus", + abort: new AbortController().signal, +} + +type EnvSnapshot = Record<"CLAUDE_CONFIG_DIR" | "OPENCODE_CONFIG_DIR", string | undefined> +type PromptBody = { system?: string } +type PromptInput = { body?: PromptBody } + +let envSnapshot: EnvSnapshot +let testConfigDir: string | undefined + +function restoreEnvValue(key: keyof EnvSnapshot): void { + const value = envSnapshot[key] + if (value === undefined) { + delete process.env[key] + return + } + + process.env[key] = value +} + +function createAgentBrowserSkillFixture(content: string): void { + const unique = `delegate-agent-browser-${Date.now()}-${Math.random().toString(16).slice(2)}` + testConfigDir = join(tmpdir(), unique) + const skillDir = join(testConfigDir, "skills", "compound-engineering", "agent-browser") + mkdirSync(skillDir, { recursive: true }) + writeFileSync( + join(skillDir, "SKILL.md"), + `---\nname: agent-browser\ndescription: Test-owned browser automation skill\n---\n${content}`, + ) + process.env.CLAUDE_CONFIG_DIR = testConfigDir + process.env.OPENCODE_CONFIG_DIR = testConfigDir + clearSkillCache() +} + +describe("delegate task browserProvider propagation", () => { + beforeEach(() => { + envSnapshot = { + CLAUDE_CONFIG_DIR: process.env.CLAUDE_CONFIG_DIR, + OPENCODE_CONFIG_DIR: process.env.OPENCODE_CONFIG_DIR, + } + testConfigDir = undefined + clearSkillCache() + }) + + afterEach(() => { + restoreEnvValue("CLAUDE_CONFIG_DIR") + restoreEnvValue("OPENCODE_CONFIG_DIR") + clearSkillCache() + if (testConfigDir !== undefined) { + rmSync(testConfigDir, { recursive: true, force: true }) + } + }) + + test("resolves agent-browser builtin skill when browserProvider is passed", async () => { + // given + const { createDelegateTask } = require("./tools") + let promptBody: PromptBody | undefined + const promptMock = async (input: PromptInput) => { + promptBody = input.body + return { data: {} } + } + const mockClient = { + app: { agents: async () => ({ data: [] }) }, + config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) }, + session: { + get: async () => ({ data: { directory: "/project" } }), + create: async () => ({ data: { id: "ses_browser_provider" } }), + prompt: promptMock, + promptAsync: promptMock, + messages: async () => ({ + data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }], + }), + status: async () => ({ data: {} }), + }, + } + const tool = createDelegateTask({ + manager: { launch: async () => ({}) }, + client: mockClient, + browserProvider: "agent-browser", + }) + + // when + await tool.execute( + { + description: "Test browserProvider propagation", + prompt: "Do something", + category: "ultrabrain", + run_in_background: false, + load_skills: ["agent-browser"], + }, + TOOL_CONTEXT, + ) + + // then + const system = promptBody?.system ?? "" + expect(system).toContain("agent-browser") + expect(system).toContain("") + expect(system.startsWith("")).toBe(false) + }, { timeout: 20000 }) + + test("resolves discovered agent-browser skill by short name without browserProvider", async () => { + // given + const fixtureContent = "Test-owned agent-browser fixture content" + createAgentBrowserSkillFixture(fixtureContent) + const { createDelegateTask } = require("./tools") + let promptBody: PromptBody | undefined + const promptMock = async (input: PromptInput) => { + promptBody = input.body + return { data: {} } + } + const mockClient = { + app: { agents: async () => ({ data: [] }) }, + config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) }, + session: { + get: async () => ({ data: { directory: "/project" } }), + create: async () => ({ data: { id: "ses_no_browser_provider" } }), + prompt: promptMock, + promptAsync: promptMock, + messages: async () => ({ + data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }], + }), + status: async () => ({ data: {} }), + }, + } + const tool = createDelegateTask({ + manager: { launch: async () => ({}) }, + client: mockClient, + }) + + // when + const result = await tool.execute( + { + description: "Test missing browserProvider", + prompt: "Do something", + category: "ultrabrain", + run_in_background: false, + load_skills: ["agent-browser"], + }, + TOOL_CONTEXT, + ) + + // then + expect(result).toContain("Task completed") + expect(result).toContain("ses_no_browser_provider") + expect(promptBody?.system).toContain(fixtureContent) + }) +}) diff --git a/src/tools/delegate-task/tools.test.ts b/src/tools/delegate-task/tools.test.ts index 0df9e1d24..ee61f4699 100644 --- a/src/tools/delegate-task/tools.test.ts +++ b/src/tools/delegate-task/tools.test.ts @@ -696,7 +696,7 @@ describe("sisyphus-task", () => { //#given - manager.launch returns before sessionID is available const { createDelegateTask } = require("./tools") - const tasks = new Map() + const tasks = new Map() const mockManager = { getTask: (id: string) => tasks.get(id), launch: async () => { @@ -3222,119 +3222,6 @@ describe("sisyphus-task", () => { }) }) - describe("browserProvider propagation", () => { - test("should resolve agent-browser skill when browserProvider is passed", async () => { - // given - task configured with browserProvider: "agent-browser" - const { createDelegateTask } = require("./tools") - let promptBody: any - - const mockManager = { launch: async () => ({}) } - - const promptMock = async (input: any) => { - promptBody = input.body - return { data: {} } - } - - const mockClient = { - app: { agents: async () => ({ data: [] }) }, - config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) }, - session: { - get: async () => ({ data: { directory: "/project" } }), - create: async () => ({ data: { id: "ses_browser_provider" } }), - prompt: promptMock, - promptAsync: promptMock, - messages: async () => ({ - data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }] - }), - status: async () => ({ data: {} }), - }, - } - - // Pass browserProvider to createDelegateTask - const tool = createDelegateTask({ - manager: mockManager, - client: mockClient, - browserProvider: "agent-browser", - }) - - const toolContext = { - sessionID: "parent-session", - messageID: "parent-message", - agent: "sisyphus", - abort: new AbortController().signal, - } - - // when - request agent-browser skill - await tool.execute( - { - description: "Test browserProvider propagation", - prompt: "Do something", - category: "ultrabrain", - run_in_background: false, - load_skills: ["agent-browser"], - }, - toolContext - ) - - // then - agent-browser skill should be resolved - expect(promptBody).toBeDefined() - expect(promptBody.system).toBeDefined() - expect(promptBody.system).toContain("") - expect(String(promptBody.system).startsWith("")).toBe(false) - }, { timeout: 20000 }) - - test("should resolve agent-browser skill even when browserProvider is not set", async () => { - // given - delegate_task without browserProvider - const { createDelegateTask } = require("./tools") - const mockManager = { launch: async () => ({}) } - const mockClient = { - app: { agents: async () => ({ data: [] }) }, - config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) }, - session: { - get: async () => ({ data: { directory: "/project" } }), - create: async () => ({ data: { id: "ses_no_browser_provider" } }), - prompt: async () => { - return { data: {} } - }, - promptAsync: async () => ({ data: {} }), - messages: async () => ({ - data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }] - }), - status: async () => ({ data: {} }), - }, - } - - // No browserProvider passed - const tool = createDelegateTask({ - manager: mockManager, - client: mockClient, - }) - - const toolContext = { - sessionID: "parent-session", - messageID: "parent-message", - agent: "sisyphus", - abort: new AbortController().signal, - } - - // when - request agent-browser skill without browserProvider - const result = await tool.execute( - { - description: "Test missing browserProvider", - prompt: "Do something", - category: "ultrabrain", - run_in_background: false, - load_skills: ["agent-browser"], - }, - toolContext - ) - - // then - the external compound-engineering/agent-browser skill can resolve by unique short name - expect(result).toContain("Task completed") - expect(result).toContain("ses_no_browser_provider") - }) - }) - describe("delegate task with short skill name", () => { let envCleanup: Record