Skip to content

test: add regression coverage for sync plugin hooks#19589

Merged
Hona merged 2 commits intoanomalyco:devfrom
Hona:test/plugin-trigger-sync-hook-regression
Mar 29, 2026
Merged

test: add regression coverage for sync plugin hooks#19589
Hona merged 2 commits intoanomalyco:devfrom
Hona:test/plugin-trigger-sync-hook-regression

Conversation

@Hona
Copy link
Copy Markdown
Member

@Hona Hona commented Mar 29, 2026

Summary

  • add a regression test for synchronous plugin hooks in Plugin.trigger
  • cover both synchronous and asynchronous experimental.chat.system.transform hooks
  • verify with bun test \"test/plugin/trigger.test.ts\" from packages/opencode

Copilot AI review requested due to automatic review settings March 29, 2026 03:25
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds regression coverage in packages/opencode to ensure Plugin.trigger correctly handles both synchronous and asynchronous implementations of the experimental.chat.system.transform hook.

Changes:

  • Adds a new plugin.trigger test suite validating sync hooks don’t crash and async hooks are awaited.
  • Creates temporary per-test plugin projects via a generated opencode.json and plugin.ts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +83 to +88
{
model: {
providerID: "anthropic",
modelID: "claude-sonnet-4-6",
} as any,
},
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hook type for "experimental.chat.system.transform" expects input.model to be a full SDK Model (per the Hooks definition in packages/plugin/src/index.ts), but the test passes { providerID, modelID } cast as any. This makes the regression less representative and could hide failures in plugins that legitimately read fields like model.id; consider passing a more realistic Model shape (or resolving one via the same provider/model helper used in runtime) so the test matches real inputs.

Copilot uses AI. Check for mistakes.
Comment on lines +117 to +122
{
model: {
providerID: "anthropic",
modelID: "claude-sonnet-4-6",
} as any,
},
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: this test passes a minimal { providerID, modelID } object cast as any for input.model, but the hook contract is SDK Model. Using a more realistic Model (or at least including the expected fields like model.id) will make this regression test closer to real Plugin.trigger usage and less brittle if hooks start depending on model properties.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +37
import { afterAll, afterEach, describe, expect, test } from "bun:test"
import fs from "fs/promises"
import os from "os"
import path from "path"
import { pathToFileURL } from "url"

const env = {
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME,
XDG_DATA_HOME: process.env.XDG_DATA_HOME,
XDG_STATE_HOME: process.env.XDG_STATE_HOME,
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME,
OPENCODE_TEST_HOME: process.env.OPENCODE_TEST_HOME,
OPENCODE_DISABLE_DEFAULT_PLUGINS: process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS,
}

const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-plugin-trigger-xdg-"))

process.env.XDG_CONFIG_HOME = path.join(root, "config")
process.env.XDG_DATA_HOME = path.join(root, "data")
process.env.XDG_STATE_HOME = path.join(root, "state")
process.env.XDG_CACHE_HOME = path.join(root, "cache")
process.env.OPENCODE_TEST_HOME = path.join(root, "home")
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(async () => {
for (const [key, value] of Object.entries(env)) {
if (value === undefined) delete process.env[key]
else process.env[key] = value
}
await fs.rm(root, { recursive: true, force: true })
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test file redefines XDG_* and OPENCODE_TEST_HOME at module scope, but packages/opencode/bunfig.toml already preloads ./test/preload.ts which sets those env vars for the whole suite. Overriding them again adds global env churn and can cause cross-test interference if Bun executes files in parallel; consider relying on preload.ts (remove the XDG/OPENCODE_TEST_HOME rewrites + dynamic imports) or at least scope the env mutation to only what this test needs (e.g. just OPENCODE_DISABLE_DEFAULT_PLUGINS) with a tight beforeAll/afterAll.

Suggested change
import { afterAll, afterEach, describe, expect, test } from "bun:test"
import fs from "fs/promises"
import os from "os"
import path from "path"
import { pathToFileURL } from "url"
const env = {
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME,
XDG_DATA_HOME: process.env.XDG_DATA_HOME,
XDG_STATE_HOME: process.env.XDG_STATE_HOME,
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME,
OPENCODE_TEST_HOME: process.env.OPENCODE_TEST_HOME,
OPENCODE_DISABLE_DEFAULT_PLUGINS: process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS,
}
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-plugin-trigger-xdg-"))
process.env.XDG_CONFIG_HOME = path.join(root, "config")
process.env.XDG_DATA_HOME = path.join(root, "data")
process.env.XDG_STATE_HOME = path.join(root, "state")
process.env.XDG_CACHE_HOME = path.join(root, "cache")
process.env.OPENCODE_TEST_HOME = path.join(root, "home")
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(async () => {
for (const [key, value] of Object.entries(env)) {
if (value === undefined) delete process.env[key]
else process.env[key] = value
}
await fs.rm(root, { recursive: true, force: true })
import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test"
import fs from "fs/promises"
import os from "os"
import path from "path"
import { pathToFileURL } from "url"
import { Plugin } from "../../src/plugin/index"
import { Instance } from "../../src/project/instance"
let originalDisableDefaultPlugins = process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS
beforeAll(() => {
originalDisableDefaultPlugins = process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS
process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS = "1"
})
afterEach(async () => {
await Instance.disposeAll()
})
afterAll(async () => {
if (originalDisableDefaultPlugins === undefined) {
delete process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS
} else {
process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS = originalDisableDefaultPlugins
}

Copilot uses AI. Check for mistakes.
@Hona Hona merged commit ba1f683 into anomalyco:dev Mar 29, 2026
8 checks passed
e-n-0 pushed a commit to e-n-0/opencode that referenced this pull request Mar 29, 2026
loocor pushed a commit to loocor/opencode that referenced this pull request Mar 30, 2026
afanty2021 pushed a commit to afanty2021/opencode that referenced this pull request Mar 30, 2026
balcsida pushed a commit to balcsida/opencode that referenced this pull request Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants