diff --git a/packages/opencode/test/plugin/trigger.test.ts b/packages/opencode/test/plugin/trigger.test.ts new file mode 100644 index 000000000000..d89d930c69ea --- /dev/null +++ b/packages/opencode/test/plugin/trigger.test.ts @@ -0,0 +1,111 @@ +import { afterAll, afterEach, describe, expect, test } from "bun:test" +import path from "path" +import { pathToFileURL } from "url" +import { tmpdir } from "../fixture/fixture" + +const disableDefault = process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS +process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS = "1" + +const { Plugin } = await import("../../src/plugin/index") +const { Instance } = await import("../../src/project/instance") + +afterEach(async () => { + await Instance.disposeAll() +}) + +afterAll(() => { + if (disableDefault === undefined) { + delete process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS + return + } + process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS = disableDefault +}) + +async function project(source: string) { + return tmpdir({ + init: async (dir) => { + const file = path.join(dir, "plugin.ts") + await Bun.write(file, source) + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify( + { + $schema: "https://opencode.ai/config.json", + plugin: [pathToFileURL(file).href], + }, + null, + 2, + ), + ) + }, + }) +} + +describe("plugin.trigger", () => { + test("runs synchronous hooks without crashing", async () => { + await using tmp = await project( + [ + "export default async () => ({", + ' "experimental.chat.system.transform": (_input, output) => {', + ' output.system.unshift("sync")', + " },", + "})", + "", + ].join("\n"), + ) + + const out = await Instance.provide({ + directory: tmp.path, + fn: async () => { + const out = { system: [] as string[] } + await Plugin.trigger( + "experimental.chat.system.transform", + { + model: { + providerID: "anthropic", + modelID: "claude-sonnet-4-6", + } as any, + }, + out, + ) + return out + }, + }) + + expect(out.system).toEqual(["sync"]) + }) + + test("awaits asynchronous hooks", async () => { + await using tmp = await project( + [ + "export default async () => ({", + ' "experimental.chat.system.transform": async (_input, output) => {', + " await Bun.sleep(1)", + ' output.system.unshift("async")', + " },", + "})", + "", + ].join("\n"), + ) + + const out = await Instance.provide({ + directory: tmp.path, + fn: async () => { + const out = { system: [] as string[] } + await Plugin.trigger( + "experimental.chat.system.transform", + { + model: { + providerID: "anthropic", + modelID: "claude-sonnet-4-6", + } as any, + }, + out, + ) + return out + }, + }) + + expect(out.system).toEqual(["async"]) + }) +})