/// import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from "bun:test" import type { PluginInput } from "@opencode-ai/plugin" import { OhMyOpenCodeConfigSchema } from "./config/schema/oh-my-opencode-config" import { createManagers } from "./create-managers" import * as openclawRuntimeDispatch from "./openclaw/runtime-dispatch" import { createModelCacheState } from "./plugin-state" const markServerRunningInProcess = mock(() => {}) let backgroundManagerOptions: { onSubagentSessionCreated?: (event: { sessionID: string; parentID: string; title: string }) => Promise } | null = null const trackedPaneBySession = new Map() class MockBackgroundManager { constructor( _ctx: PluginInput, _config?: unknown, options?: { tmuxConfig?: unknown onSubagentSessionCreated?: (event: { sessionID: string; parentID: string; title: string }) => Promise onShutdown?: () => void | Promise enableParentSessionNotifications?: boolean }, ) { backgroundManagerOptions = options ?? null } } class MockSkillMcpManager { constructor(..._args: unknown[]) {} } class MockTmuxSessionManager { constructor(_ctx: PluginInput, _config: unknown) {} async cleanup(): Promise {} async onSessionCreated(event: { properties?: { info?: { id?: string } } }): Promise { const sessionID = event.properties?.info?.id if (sessionID) { trackedPaneBySession.set(sessionID, `%pane-${sessionID}`) } } getTrackedPaneId(sessionID: string): string | undefined { return trackedPaneBySession.get(sessionID) } } function createConfigHandler(): ReturnType { return async () => {} } function initTaskToastManager(): ReturnType { return {} as ReturnType } function registerManagerForCleanup(): void {} function createDeps(): NonNullable[0]["deps"]> { return { BackgroundManagerClass: MockBackgroundManager as typeof import("./features/background-agent").BackgroundManager, SkillMcpManagerClass: MockSkillMcpManager as typeof import("./features/skill-mcp-manager").SkillMcpManager, TmuxSessionManagerClass: MockTmuxSessionManager as typeof import("./features/tmux-subagent").TmuxSessionManager, initTaskToastManagerFn: initTaskToastManager, registerManagerForCleanupFn: registerManagerForCleanup, createConfigHandlerFn: createConfigHandler, markServerRunningInProcessFn: markServerRunningInProcess, } } function createTmuxConfig(enabled: boolean) { return { enabled, layout: "main-vertical" as const, main_pane_size: 60, main_pane_min_width: 120, agent_pane_min_width: 40, isolation: "inline" as const, } } function createContext(directory: string): PluginInput { const shell = Object.assign( () => { throw new Error("shell should not be called in this test") }, { braces: () => [], escape: (input: string) => input, env() { return shell }, cwd() { return shell }, nothrow() { return shell }, throws() { return shell }, }, ) return { project: { id: "project-id", worktree: directory, time: { created: Date.now() }, }, directory, worktree: directory, serverUrl: new URL("http://localhost:4096"), $: shell, client: {} as PluginInput["client"], } } describe("createManagers", () => { let dispatchOpenClawEvent: ReturnType beforeEach(() => { dispatchOpenClawEvent = spyOn(openclawRuntimeDispatch, "dispatchOpenClawEvent") markServerRunningInProcess.mockClear() dispatchOpenClawEvent.mockReset() backgroundManagerOptions = null trackedPaneBySession.clear() }) afterEach(() => { dispatchOpenClawEvent.mockRestore() }) it("#given tmux integration is disabled #when managers are created #then it does not mark the tmux server as running", () => { const args = { ctx: createContext("/tmp"), pluginConfig: OhMyOpenCodeConfigSchema.parse({}), tmuxConfig: createTmuxConfig(false), modelCacheState: createModelCacheState(), backgroundNotificationHookEnabled: false, deps: createDeps(), } createManagers(args) expect(markServerRunningInProcess).not.toHaveBeenCalled() }) it("#given tmux integration is enabled #when managers are created #then it marks the tmux server as running", () => { const args = { ctx: createContext("/tmp"), pluginConfig: OhMyOpenCodeConfigSchema.parse({}), tmuxConfig: createTmuxConfig(true), modelCacheState: createModelCacheState(), backgroundNotificationHookEnabled: false, deps: createDeps(), } createManagers(args) expect(markServerRunningInProcess).toHaveBeenCalledTimes(1) }) it("#given openclaw is enabled #when the background session-created callback runs #then it dispatches openclaw with the tracked pane id", async () => { const args = { ctx: createContext("/tmp/project"), pluginConfig: OhMyOpenCodeConfigSchema.parse({ openclaw: { enabled: true, gateways: {}, hooks: {}, }, }), tmuxConfig: createTmuxConfig(true), modelCacheState: createModelCacheState(), backgroundNotificationHookEnabled: false, deps: createDeps(), } createManagers(args) await backgroundManagerOptions?.onSubagentSessionCreated?.({ sessionID: "ses-bg-1", parentID: "ses-parent", title: "child task", }) expect(dispatchOpenClawEvent).toHaveBeenCalledTimes(1) expect(dispatchOpenClawEvent).toHaveBeenCalledWith({ config: args.pluginConfig.openclaw, rawEvent: "session.created", context: { sessionId: "ses-bg-1", projectPath: "/tmp/project", tmuxPaneId: "%pane-ses-bg-1", }, }) }) })