fix(tests): inject plugin loader dependencies

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-04-06 18:45:33 +09:00
parent 320dc0248e
commit 03409938ec
2 changed files with 85 additions and 52 deletions
@@ -1,5 +1,10 @@
import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test"
import type { PluginComponentsResult } from "./loader"
import {
clearPluginComponentsCache,
loadAllPluginComponents,
loadAllPluginComponentsWithDeps,
type PluginComponentsResult,
} from "./loader"
function createPluginComponentsResult(): PluginComponentsResult {
return {
@@ -13,10 +18,6 @@ function createPluginComponentsResult(): PluginComponentsResult {
}
}
async function importFreshLoaderModule() {
return import(`./loader?plugin-loader-cache-test=${Date.now()}-${Math.random()}`)
}
describe("loadAllPluginComponents", () => {
const originalEnv = { ...process.env }
@@ -138,20 +139,20 @@ describe("loadAllPluginComponents", () => {
const loadPluginMcpServers = mock(async () => result.mcpServers)
const loadPluginHooksConfigs = mock(() => result.hooksConfigs)
mock.module("./discovery", () => ({ discoverInstalledPlugins }))
mock.module("./command-loader", () => ({ loadPluginCommands }))
mock.module("./skill-loader", () => ({ loadPluginSkillsAsCommands }))
mock.module("./agent-loader", () => ({ loadPluginAgents }))
mock.module("./mcp-server-loader", () => ({ loadPluginMcpServers }))
mock.module("./hook-loader", () => ({ loadPluginHooksConfigs }))
const { clearPluginComponentsCache, loadAllPluginComponents } = await importFreshLoaderModule()
clearPluginComponentsCache()
const enabledPluginsOverride = { "demo@test": true }
// when
const firstResult = await loadAllPluginComponents({ enabledPluginsOverride })
const secondResult = await loadAllPluginComponents({ enabledPluginsOverride })
const deps = {
discoverInstalledPlugins,
loadPluginCommands,
loadPluginSkillsAsCommands,
loadPluginAgents,
loadPluginMcpServers,
loadPluginHooksConfigs,
}
const firstResult = await loadAllPluginComponentsWithDeps({ enabledPluginsOverride }, deps)
const secondResult = await loadAllPluginComponentsWithDeps({ enabledPluginsOverride }, deps)
// then
expect(firstResult).toEqual(result)
@@ -176,19 +177,19 @@ describe("loadAllPluginComponents", () => {
const loadPluginMcpServers = mock(async () => result.mcpServers)
const loadPluginHooksConfigs = mock(() => result.hooksConfigs)
mock.module("./discovery", () => ({ discoverInstalledPlugins }))
mock.module("./command-loader", () => ({ loadPluginCommands }))
mock.module("./skill-loader", () => ({ loadPluginSkillsAsCommands }))
mock.module("./agent-loader", () => ({ loadPluginAgents }))
mock.module("./mcp-server-loader", () => ({ loadPluginMcpServers }))
mock.module("./hook-loader", () => ({ loadPluginHooksConfigs }))
const { clearPluginComponentsCache, loadAllPluginComponents } = await importFreshLoaderModule()
clearPluginComponentsCache()
// when
await loadAllPluginComponents({ enabledPluginsOverride: { "demo@test": true } })
await loadAllPluginComponents({ enabledPluginsOverride: { "demo@test": false } })
const deps = {
discoverInstalledPlugins,
loadPluginCommands,
loadPluginSkillsAsCommands,
loadPluginAgents,
loadPluginMcpServers,
loadPluginHooksConfigs,
}
await loadAllPluginComponentsWithDeps({ enabledPluginsOverride: { "demo@test": true } }, deps)
await loadAllPluginComponentsWithDeps({ enabledPluginsOverride: { "demo@test": false } }, deps)
// then
expect(discoverInstalledPlugins).toHaveBeenCalledTimes(2)
@@ -211,20 +212,20 @@ describe("loadAllPluginComponents", () => {
const loadPluginMcpServers = mock(async () => result.mcpServers)
const loadPluginHooksConfigs = mock(() => result.hooksConfigs)
mock.module("./discovery", () => ({ discoverInstalledPlugins }))
mock.module("./command-loader", () => ({ loadPluginCommands }))
mock.module("./skill-loader", () => ({ loadPluginSkillsAsCommands }))
mock.module("./agent-loader", () => ({ loadPluginAgents }))
mock.module("./mcp-server-loader", () => ({ loadPluginMcpServers }))
mock.module("./hook-loader", () => ({ loadPluginHooksConfigs }))
const { clearPluginComponentsCache, loadAllPluginComponents } = await importFreshLoaderModule()
clearPluginComponentsCache()
// when
await loadAllPluginComponents()
const deps = {
discoverInstalledPlugins,
loadPluginCommands,
loadPluginSkillsAsCommands,
loadPluginAgents,
loadPluginMcpServers,
loadPluginHooksConfigs,
}
await loadAllPluginComponentsWithDeps(undefined, deps)
clearPluginComponentsCache()
await loadAllPluginComponents()
await loadAllPluginComponentsWithDeps(undefined, deps)
// then
expect(discoverInstalledPlugins).toHaveBeenCalledTimes(2)
@@ -247,20 +248,20 @@ describe("loadAllPluginComponents", () => {
const loadPluginMcpServers = mock(async () => result.mcpServers)
const loadPluginHooksConfigs = mock(() => result.hooksConfigs)
mock.module("./discovery", () => ({ discoverInstalledPlugins }))
mock.module("./command-loader", () => ({ loadPluginCommands }))
mock.module("./skill-loader", () => ({ loadPluginSkillsAsCommands }))
mock.module("./agent-loader", () => ({ loadPluginAgents }))
mock.module("./mcp-server-loader", () => ({ loadPluginMcpServers }))
mock.module("./hook-loader", () => ({ loadPluginHooksConfigs }))
const { clearPluginComponentsCache, loadAllPluginComponents } = await importFreshLoaderModule()
clearPluginComponentsCache()
// when
const firstResult = await loadAllPluginComponents()
const deps = {
discoverInstalledPlugins,
loadPluginCommands,
loadPluginSkillsAsCommands,
loadPluginAgents,
loadPluginMcpServers,
loadPluginHooksConfigs,
}
const firstResult = await loadAllPluginComponentsWithDeps(undefined, deps)
firstResult.commands["demo:command"]!.description = "mutated"
const secondResult = await loadAllPluginComponents()
const secondResult = await loadAllPluginComponentsWithDeps(undefined, deps)
// then
expect(secondResult.commands["demo:command"]!.description).toBe("demo")
@@ -27,8 +27,26 @@ export interface PluginComponentsResult {
errors: PluginLoadError[]
}
export interface PluginComponentLoadDeps {
discoverInstalledPlugins: typeof discoverInstalledPlugins
loadPluginCommands: typeof loadPluginCommands
loadPluginSkillsAsCommands: typeof loadPluginSkillsAsCommands
loadPluginAgents: typeof loadPluginAgents
loadPluginMcpServers: typeof loadPluginMcpServers
loadPluginHooksConfigs: typeof loadPluginHooksConfigs
}
const cachedPluginComponentsByKey = new Map<string, PluginComponentsResult>()
const defaultPluginComponentLoadDeps: PluginComponentLoadDeps = {
discoverInstalledPlugins,
loadPluginCommands,
loadPluginSkillsAsCommands,
loadPluginAgents,
loadPluginMcpServers,
loadPluginHooksConfigs,
}
function clonePluginComponentsResult(
result: PluginComponentsResult,
): PluginComponentsResult {
@@ -54,7 +72,10 @@ export function clearPluginComponentsCache(): void {
cachedPluginComponentsByKey.clear()
}
export async function loadAllPluginComponents(options?: PluginLoaderOptions): Promise<PluginComponentsResult> {
async function loadAllPluginComponentsInternal(
options?: PluginLoaderOptions,
deps: PluginComponentLoadDeps = defaultPluginComponentLoadDeps,
): Promise<PluginComponentsResult> {
if (isClaudeCodePluginsDisabled()) {
log("Claude Code plugin loading disabled via OPENCODE_DISABLE_CLAUDE_CODE env var")
return {
@@ -74,14 +95,14 @@ export async function loadAllPluginComponents(options?: PluginLoaderOptions): Pr
return clonePluginComponentsResult(cachedPluginComponents)
}
const { plugins, errors } = discoverInstalledPlugins(options)
const { plugins, errors } = deps.discoverInstalledPlugins(options)
const [commands, skills, agents, mcpServers, hooksConfigs] = await Promise.all([
Promise.resolve(loadPluginCommands(plugins)),
Promise.resolve(loadPluginSkillsAsCommands(plugins)),
Promise.resolve(loadPluginAgents(plugins)),
loadPluginMcpServers(plugins),
Promise.resolve(loadPluginHooksConfigs(plugins)),
Promise.resolve(deps.loadPluginCommands(plugins)),
Promise.resolve(deps.loadPluginSkillsAsCommands(plugins)),
Promise.resolve(deps.loadPluginAgents(plugins)),
deps.loadPluginMcpServers(plugins),
Promise.resolve(deps.loadPluginHooksConfigs(plugins)),
])
log(`Loaded ${plugins.length} plugins with ${Object.keys(commands).length} commands, ${Object.keys(skills).length} skills, ${Object.keys(agents).length} agents, ${Object.keys(mcpServers).length} MCP servers`)
@@ -100,3 +121,14 @@ export async function loadAllPluginComponents(options?: PluginLoaderOptions): Pr
return clonePluginComponentsResult(result)
}
export async function loadAllPluginComponents(options?: PluginLoaderOptions): Promise<PluginComponentsResult> {
return loadAllPluginComponentsInternal(options)
}
export async function loadAllPluginComponentsWithDeps(
options: PluginLoaderOptions | undefined,
deps: PluginComponentLoadDeps,
): Promise<PluginComponentsResult> {
return loadAllPluginComponentsInternal(options, deps)
}