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:
@@ -1,5 +1,10 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test"
|
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 {
|
function createPluginComponentsResult(): PluginComponentsResult {
|
||||||
return {
|
return {
|
||||||
@@ -13,10 +18,6 @@ function createPluginComponentsResult(): PluginComponentsResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function importFreshLoaderModule() {
|
|
||||||
return import(`./loader?plugin-loader-cache-test=${Date.now()}-${Math.random()}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("loadAllPluginComponents", () => {
|
describe("loadAllPluginComponents", () => {
|
||||||
const originalEnv = { ...process.env }
|
const originalEnv = { ...process.env }
|
||||||
|
|
||||||
@@ -138,20 +139,20 @@ describe("loadAllPluginComponents", () => {
|
|||||||
const loadPluginMcpServers = mock(async () => result.mcpServers)
|
const loadPluginMcpServers = mock(async () => result.mcpServers)
|
||||||
const loadPluginHooksConfigs = mock(() => result.hooksConfigs)
|
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()
|
clearPluginComponentsCache()
|
||||||
const enabledPluginsOverride = { "demo@test": true }
|
const enabledPluginsOverride = { "demo@test": true }
|
||||||
|
|
||||||
// when
|
// when
|
||||||
const firstResult = await loadAllPluginComponents({ enabledPluginsOverride })
|
const deps = {
|
||||||
const secondResult = await loadAllPluginComponents({ enabledPluginsOverride })
|
discoverInstalledPlugins,
|
||||||
|
loadPluginCommands,
|
||||||
|
loadPluginSkillsAsCommands,
|
||||||
|
loadPluginAgents,
|
||||||
|
loadPluginMcpServers,
|
||||||
|
loadPluginHooksConfigs,
|
||||||
|
}
|
||||||
|
const firstResult = await loadAllPluginComponentsWithDeps({ enabledPluginsOverride }, deps)
|
||||||
|
const secondResult = await loadAllPluginComponentsWithDeps({ enabledPluginsOverride }, deps)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(firstResult).toEqual(result)
|
expect(firstResult).toEqual(result)
|
||||||
@@ -176,19 +177,19 @@ describe("loadAllPluginComponents", () => {
|
|||||||
const loadPluginMcpServers = mock(async () => result.mcpServers)
|
const loadPluginMcpServers = mock(async () => result.mcpServers)
|
||||||
const loadPluginHooksConfigs = mock(() => result.hooksConfigs)
|
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()
|
clearPluginComponentsCache()
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await loadAllPluginComponents({ enabledPluginsOverride: { "demo@test": true } })
|
const deps = {
|
||||||
await loadAllPluginComponents({ enabledPluginsOverride: { "demo@test": false } })
|
discoverInstalledPlugins,
|
||||||
|
loadPluginCommands,
|
||||||
|
loadPluginSkillsAsCommands,
|
||||||
|
loadPluginAgents,
|
||||||
|
loadPluginMcpServers,
|
||||||
|
loadPluginHooksConfigs,
|
||||||
|
}
|
||||||
|
await loadAllPluginComponentsWithDeps({ enabledPluginsOverride: { "demo@test": true } }, deps)
|
||||||
|
await loadAllPluginComponentsWithDeps({ enabledPluginsOverride: { "demo@test": false } }, deps)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(discoverInstalledPlugins).toHaveBeenCalledTimes(2)
|
expect(discoverInstalledPlugins).toHaveBeenCalledTimes(2)
|
||||||
@@ -211,20 +212,20 @@ describe("loadAllPluginComponents", () => {
|
|||||||
const loadPluginMcpServers = mock(async () => result.mcpServers)
|
const loadPluginMcpServers = mock(async () => result.mcpServers)
|
||||||
const loadPluginHooksConfigs = mock(() => result.hooksConfigs)
|
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()
|
clearPluginComponentsCache()
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await loadAllPluginComponents()
|
const deps = {
|
||||||
|
discoverInstalledPlugins,
|
||||||
|
loadPluginCommands,
|
||||||
|
loadPluginSkillsAsCommands,
|
||||||
|
loadPluginAgents,
|
||||||
|
loadPluginMcpServers,
|
||||||
|
loadPluginHooksConfigs,
|
||||||
|
}
|
||||||
|
await loadAllPluginComponentsWithDeps(undefined, deps)
|
||||||
clearPluginComponentsCache()
|
clearPluginComponentsCache()
|
||||||
await loadAllPluginComponents()
|
await loadAllPluginComponentsWithDeps(undefined, deps)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(discoverInstalledPlugins).toHaveBeenCalledTimes(2)
|
expect(discoverInstalledPlugins).toHaveBeenCalledTimes(2)
|
||||||
@@ -247,20 +248,20 @@ describe("loadAllPluginComponents", () => {
|
|||||||
const loadPluginMcpServers = mock(async () => result.mcpServers)
|
const loadPluginMcpServers = mock(async () => result.mcpServers)
|
||||||
const loadPluginHooksConfigs = mock(() => result.hooksConfigs)
|
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()
|
clearPluginComponentsCache()
|
||||||
|
|
||||||
// when
|
// 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"
|
firstResult.commands["demo:command"]!.description = "mutated"
|
||||||
const secondResult = await loadAllPluginComponents()
|
const secondResult = await loadAllPluginComponentsWithDeps(undefined, deps)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(secondResult.commands["demo:command"]!.description).toBe("demo")
|
expect(secondResult.commands["demo:command"]!.description).toBe("demo")
|
||||||
|
|||||||
@@ -27,8 +27,26 @@ export interface PluginComponentsResult {
|
|||||||
errors: PluginLoadError[]
|
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 cachedPluginComponentsByKey = new Map<string, PluginComponentsResult>()
|
||||||
|
|
||||||
|
const defaultPluginComponentLoadDeps: PluginComponentLoadDeps = {
|
||||||
|
discoverInstalledPlugins,
|
||||||
|
loadPluginCommands,
|
||||||
|
loadPluginSkillsAsCommands,
|
||||||
|
loadPluginAgents,
|
||||||
|
loadPluginMcpServers,
|
||||||
|
loadPluginHooksConfigs,
|
||||||
|
}
|
||||||
|
|
||||||
function clonePluginComponentsResult(
|
function clonePluginComponentsResult(
|
||||||
result: PluginComponentsResult,
|
result: PluginComponentsResult,
|
||||||
): PluginComponentsResult {
|
): PluginComponentsResult {
|
||||||
@@ -54,7 +72,10 @@ export function clearPluginComponentsCache(): void {
|
|||||||
cachedPluginComponentsByKey.clear()
|
cachedPluginComponentsByKey.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadAllPluginComponents(options?: PluginLoaderOptions): Promise<PluginComponentsResult> {
|
async function loadAllPluginComponentsInternal(
|
||||||
|
options?: PluginLoaderOptions,
|
||||||
|
deps: PluginComponentLoadDeps = defaultPluginComponentLoadDeps,
|
||||||
|
): Promise<PluginComponentsResult> {
|
||||||
if (isClaudeCodePluginsDisabled()) {
|
if (isClaudeCodePluginsDisabled()) {
|
||||||
log("Claude Code plugin loading disabled via OPENCODE_DISABLE_CLAUDE_CODE env var")
|
log("Claude Code plugin loading disabled via OPENCODE_DISABLE_CLAUDE_CODE env var")
|
||||||
return {
|
return {
|
||||||
@@ -74,14 +95,14 @@ export async function loadAllPluginComponents(options?: PluginLoaderOptions): Pr
|
|||||||
return clonePluginComponentsResult(cachedPluginComponents)
|
return clonePluginComponentsResult(cachedPluginComponents)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { plugins, errors } = discoverInstalledPlugins(options)
|
const { plugins, errors } = deps.discoverInstalledPlugins(options)
|
||||||
|
|
||||||
const [commands, skills, agents, mcpServers, hooksConfigs] = await Promise.all([
|
const [commands, skills, agents, mcpServers, hooksConfigs] = await Promise.all([
|
||||||
Promise.resolve(loadPluginCommands(plugins)),
|
Promise.resolve(deps.loadPluginCommands(plugins)),
|
||||||
Promise.resolve(loadPluginSkillsAsCommands(plugins)),
|
Promise.resolve(deps.loadPluginSkillsAsCommands(plugins)),
|
||||||
Promise.resolve(loadPluginAgents(plugins)),
|
Promise.resolve(deps.loadPluginAgents(plugins)),
|
||||||
loadPluginMcpServers(plugins),
|
deps.loadPluginMcpServers(plugins),
|
||||||
Promise.resolve(loadPluginHooksConfigs(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`)
|
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)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user