test: use testEffect for plugin triggers (#25053)

This commit is contained in:
Kit Langton 2026-04-30 14:24:53 -04:00 committed by GitHub
parent f4ce240a2e
commit 87cd9446d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,18 +1,18 @@
import { afterAll, afterEach, describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { afterAll, describe, expect } from "bun:test"
import { Effect, Layer } from "effect"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import path from "path"
import { pathToFileURL } from "url"
import { tmpdir } from "../fixture/fixture"
import { ModelID, ProviderID } from "../../src/provider/schema"
import { provideTmpdirInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
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()
})
const it = testEffect(Layer.mergeAll(Plugin.defaultLayer, CrossSpawnSpawner.defaultLayer))
const systemHook = "experimental.chat.system.transform"
afterAll(() => {
if (disableDefault === undefined) {
@ -22,95 +22,81 @@ afterAll(() => {
process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS = disableDefault
})
async function project(source: string) {
return tmpdir({
init: async (dir) => {
function withProject<A, E, R>(source: string, self: Effect.Effect<A, E, R>) {
return provideTmpdirInstance((dir) =>
Effect.gen(function* () {
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,
),
yield* Effect.all(
[
Effect.promise(() => Bun.write(file, source)),
Effect.promise(() =>
Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify(
{
$schema: "https://opencode.ai/config.json",
plugin: [pathToFileURL(file).href],
},
null,
2,
),
),
),
],
{ discard: true, concurrency: 2 },
)
},
})
return yield* self
}),
)
}
const triggerSystemTransform = Effect.fn("PluginTriggerTest.triggerSystemTransform")(function* () {
const plugin = yield* Plugin.Service
const out = { system: [] as string[] }
yield* plugin.trigger(
systemHook,
{
model: {
providerID: ProviderID.anthropic,
modelID: ModelID.make("claude-sonnet-4-6"),
},
},
out,
)
return out.system
})
describe("plugin.trigger", () => {
test("runs synchronous hooks without crashing", async () => {
await using tmp = await project(
it.live("runs synchronous hooks without crashing", () =>
withProject(
[
"export default async () => ({",
' "experimental.chat.system.transform": (_input, output) => {',
` ${JSON.stringify(systemHook)}: (_input, output) => {`,
' output.system.unshift("sync")',
" },",
"})",
"",
].join("\n"),
)
Effect.gen(function* () {
expect(yield* triggerSystemTransform()).toEqual(["sync"])
}),
),
)
const out = await Instance.provide({
directory: tmp.path,
fn: async () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const out = { system: [] as string[] }
yield* plugin.trigger(
"experimental.chat.system.transform",
{
model: {
providerID: "anthropic",
modelID: "claude-sonnet-4-6",
} as any,
},
out,
)
return out
}).pipe(Effect.provide(Plugin.defaultLayer), Effect.runPromise),
})
expect(out.system).toEqual(["sync"])
})
test("awaits asynchronous hooks", async () => {
await using tmp = await project(
it.live("awaits asynchronous hooks", () =>
withProject(
[
"export default async () => ({",
' "experimental.chat.system.transform": async (_input, output) => {',
` ${JSON.stringify(systemHook)}: async (_input, output) => {`,
" await Bun.sleep(1)",
' output.system.unshift("async")',
" },",
"})",
"",
].join("\n"),
)
const out = await Instance.provide({
directory: tmp.path,
fn: async () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const out = { system: [] as string[] }
yield* plugin.trigger(
"experimental.chat.system.transform",
{
model: {
providerID: "anthropic",
modelID: "claude-sonnet-4-6",
} as any,
},
out,
)
return out
}).pipe(Effect.provide(Plugin.defaultLayer), Effect.runPromise),
})
expect(out.system).toEqual(["async"])
})
Effect.gen(function* () {
expect(yield* triggerSystemTransform()).toEqual(["async"])
}),
),
)
})