Files
oh-my-opencode/src/shared/tmux/tmux-utils/window-spawn.test.ts
T

129 lines
4.1 KiB
TypeScript
Raw Normal View History

2026-05-15 16:26:57 +09:00
import { describe, expect, it } from "bun:test"
import type { TmuxConfig } from "../../../config/schema"
import type { TmuxCommandResult } from "../runner"
2026-05-15 16:26:57 +09:00
import { spawnTmuxWindow } from "./window-spawn"
const enabledTmuxConfig = {
enabled: true,
layout: "main-vertical",
main_pane_size: 60,
main_pane_min_width: 120,
agent_pane_min_width: 40,
isolation: "inline",
} satisfies TmuxConfig
2026-05-15 16:26:57 +09:00
type SpawnTmuxWindowDeps = NonNullable<Parameters<typeof spawnTmuxWindow>[5]>
function toStringArray(value: unknown): string[] {
if (!Array.isArray(value)) {
throw new Error("Expected array value")
}
const items: string[] = []
for (const item of value) {
items.push(String(item))
}
return items
}
2026-05-15 16:26:57 +09:00
function defaultTmuxCommandResults(): TmuxCommandResult[] {
return [
{ success: true, output: "%42", stdout: "%42", stderr: "", exitCode: 0 },
{ success: true, output: "", stdout: "", stderr: "", exitCode: 0 },
]
}
function createHarness() {
const calls: Array<[string, string[]]> = []
const tmuxCommandResults = defaultTmuxCommandResults()
const runTmuxCommand = async (command: string, args: string[]): Promise<TmuxCommandResult> => {
calls.push([command, [...args]])
const nextResult = tmuxCommandResults.shift()
if (!nextResult) {
throw new Error("No more tmux command results configured")
}
return nextResult
}
const deps: SpawnTmuxWindowDeps = {
log: () => undefined,
runTmuxCommand,
isInsideTmux: (): boolean => true,
isServerRunning: async (): Promise<boolean> => true,
getTmuxPath: async (): Promise<string | null> => "sh",
}
2026-05-15 16:26:57 +09:00
function getRunTmuxCommandCall(index: number): [string, string[]] {
const call = calls[index]
if (!call) {
throw new Error(`Expected tmux runner call at index ${index}`)
}
2026-05-15 16:26:57 +09:00
return [call[0], toStringArray(call[1])]
}
2026-05-15 16:26:57 +09:00
function getNewWindowCommand(): string {
const firstCall = getRunTmuxCommandCall(0)
const newWindowCommand = firstCall[1][7]
if (newWindowCommand === undefined) {
throw new Error("Expected new-window command")
}
2026-05-15 16:26:57 +09:00
return newWindowCommand
2026-05-07 17:47:53 +09:00
}
2026-05-15 16:26:57 +09:00
return { deps, getRunTmuxCommandCall, getNewWindowCommand }
}
describe("spawnTmuxWindow runner integration", () => {
it("#given healthy tmux environment #when spawnTmuxWindow called #then delegates new-window and select-pane to shared runner", async () => {
// given
2026-05-15 16:26:57 +09:00
const harness = createHarness()
const directory = "/tmp/omo-project/(window)"
// when
2026-05-15 16:26:57 +09:00
const result = await spawnTmuxWindow("session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", directory, harness.deps)
// then
2026-05-15 16:26:57 +09:00
const firstCall = harness.getRunTmuxCommandCall(0)
const secondCall = harness.getRunTmuxCommandCall(1)
expect(result).toEqual({ success: true, paneId: "%42" })
expect(firstCall[1].slice(0, 7)).toEqual(["new-window", "-d", "-n", "omo-agents", "-P", "-F", "#{pane_id}"])
expect(secondCall[1]).toEqual(["select-pane", "-t", "%42", "-T", "omo-subagent-worker"])
2026-05-15 16:26:57 +09:00
expect(harness.getNewWindowCommand()).toContain(` --dir '${directory}'`)
})
it("#given directory with spaces #when spawnTmuxWindow called #then wraps --dir value in single quotes", async () => {
// given
2026-05-15 16:26:57 +09:00
const harness = createHarness()
// when
2026-05-15 16:26:57 +09:00
await spawnTmuxWindow("session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", "/path with spaces/here", harness.deps)
// then
2026-05-15 16:26:57 +09:00
expect(harness.getNewWindowCommand()).toContain("--dir '/path with spaces/here'")
})
it("#given empty directory #when spawnTmuxWindow called #then falls back to process cwd", async () => {
// given
2026-05-15 16:26:57 +09:00
const harness = createHarness()
// when
2026-05-15 16:26:57 +09:00
await spawnTmuxWindow("session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", "", harness.deps)
// then
2026-05-15 16:26:57 +09:00
expect(harness.getNewWindowCommand()).toContain(`--dir '${process.cwd()}'`)
})
it("#given directory with single quotes #when spawnTmuxWindow called #then escapes the value with POSIX-safe single quoting", async () => {
// given
2026-05-15 16:26:57 +09:00
const harness = createHarness()
// when
2026-05-15 16:26:57 +09:00
await spawnTmuxWindow("session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", "/path/with'quote", harness.deps)
// then
2026-05-15 16:26:57 +09:00
expect(harness.getNewWindowCommand()).toContain("--dir '/path/with'\\''quote'")
})
})