test: run suite without split runner
This commit is contained in:
@@ -5179,7 +5179,7 @@ describe("BackgroundManager.handleEvent - session.error", () => {
|
||||
{
|
||||
info: {
|
||||
role: "assistant",
|
||||
time: { created: Date.now() },
|
||||
time: { created: 2_000 },
|
||||
},
|
||||
parts: [{ type: "text", text: "wake was already accepted" }],
|
||||
},
|
||||
@@ -5214,7 +5214,7 @@ describe("BackgroundManager.handleEvent - session.error", () => {
|
||||
if (!wake) {
|
||||
throw new Error("Missing dispatched parent wake")
|
||||
}
|
||||
wake.dispatchedAt = Date.now() - 1_000
|
||||
wake.dispatchedAt = 1_000
|
||||
|
||||
//#when
|
||||
manager.handleEvent({
|
||||
|
||||
@@ -3,11 +3,6 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
|
||||
import { tmpdir } from "node:os"
|
||||
import { join } from "node:path"
|
||||
|
||||
// NOTE: Do NOT import discoverInstalledPlugins at top level.
|
||||
// loader.test.ts in the same directory mocks "./discovery" with name: "demo",
|
||||
// and when run-ci-tests.ts groups this directory together, that mock leaks.
|
||||
// Dynamic import inside each test avoids the contamination.
|
||||
|
||||
const originalClaudePluginsHome = process.env.CLAUDE_PLUGINS_HOME
|
||||
const temporaryDirectories: string[] = []
|
||||
const originalCwd = process.cwd()
|
||||
|
||||
@@ -1,95 +1,70 @@
|
||||
/// <reference types="bun-types" />
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
|
||||
import { chmod, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"
|
||||
import { tmpdir } from "node:os"
|
||||
import path from "node:path"
|
||||
import { describe, expect, mock, test } from "bun:test"
|
||||
import type { TmuxCommandResult } from "../../../shared/tmux"
|
||||
|
||||
import { resolveCallerTmuxSession } from "./resolve-caller-tmux-session"
|
||||
|
||||
type TmuxStub = {
|
||||
type TmuxCall = {
|
||||
tmuxPath: string
|
||||
logPath: string
|
||||
args: string[]
|
||||
}
|
||||
|
||||
const temporaryDirectories: string[] = []
|
||||
|
||||
function shellSingleQuote(value: string): string {
|
||||
return `'${value.split("'").join(`'"'"'`)}'`
|
||||
}
|
||||
|
||||
async function createTmuxStub(options: { stdout: string; windowStdout?: string; exitCode: number }): Promise<TmuxStub> {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), "resolve-caller-tmux-session-"))
|
||||
temporaryDirectories.push(directory)
|
||||
|
||||
const logPath = path.join(directory, "tmux.log")
|
||||
const tmuxPath = path.join(directory, "tmux")
|
||||
const script = [
|
||||
"#!/bin/sh",
|
||||
`printf '%s\\n' \"$@\" >> ${shellSingleQuote(logPath)}`,
|
||||
`case "$*" in *'#{session_name}:#{window_index}'*) printf '%s' ${shellSingleQuote(options.windowStdout ?? options.stdout)} ;; *) printf '%s' ${shellSingleQuote(options.stdout)} ;; esac`,
|
||||
`exit ${options.exitCode}`,
|
||||
].join("\n")
|
||||
|
||||
await writeFile(tmuxPath, script)
|
||||
await chmod(tmuxPath, 0o755)
|
||||
|
||||
return { tmuxPath, logPath }
|
||||
}
|
||||
|
||||
async function readLogLines(logPath: string): Promise<string[]> {
|
||||
try {
|
||||
const content = await readFile(logPath, "utf8")
|
||||
return content.split("\n").filter((line) => line.length > 0)
|
||||
} catch {
|
||||
return []
|
||||
function tmuxResult(output: string, exitCode: number = 0): TmuxCommandResult {
|
||||
return {
|
||||
success: exitCode === 0,
|
||||
output,
|
||||
stdout: output,
|
||||
stderr: "",
|
||||
exitCode,
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
delete process.env.TMUX_PANE
|
||||
})
|
||||
function createRunCommandMock(results: TmuxCommandResult[]) {
|
||||
const calls: TmuxCall[] = []
|
||||
const runCommand = mock(async (tmuxPath: string, args: string[]): Promise<TmuxCommandResult> => {
|
||||
calls.push({ tmuxPath, args })
|
||||
return results.shift() ?? tmuxResult("", 1)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(temporaryDirectories.splice(0).map(async (directory) => rm(directory, { recursive: true, force: true })))
|
||||
})
|
||||
return { calls, runCommand }
|
||||
}
|
||||
|
||||
describe("resolveCallerTmuxSession", () => {
|
||||
test("#given TMUX_PANE unset #when resolve runs #then returns null and makes no tmux calls", async () => {
|
||||
// given
|
||||
const stub = await createTmuxStub({ stdout: "$7", exitCode: 0 })
|
||||
const { calls, runCommand } = createRunCommandMock([tmuxResult("$7")])
|
||||
|
||||
// when
|
||||
const result = await resolveCallerTmuxSession(stub.tmuxPath)
|
||||
const result = await resolveCallerTmuxSession("tmux", "", runCommand)
|
||||
|
||||
// then
|
||||
expect(result).toBeNull()
|
||||
expect(await readLogLines(stub.logPath)).toHaveLength(0)
|
||||
expect(calls).toHaveLength(0)
|
||||
})
|
||||
|
||||
test("#given TMUX_PANE=%42 and display returns session and window #when resolve runs #then returns caller tmux target", async () => {
|
||||
// given
|
||||
process.env.TMUX_PANE = "%42"
|
||||
const stub = await createTmuxStub({ stdout: "$7", windowStdout: "test-session:0", exitCode: 0 })
|
||||
const { calls, runCommand } = createRunCommandMock([
|
||||
tmuxResult("$7"),
|
||||
tmuxResult("test-session:0"),
|
||||
])
|
||||
|
||||
// when
|
||||
const result = await resolveCallerTmuxSession(stub.tmuxPath)
|
||||
const result = await resolveCallerTmuxSession("tmux", "%42", runCommand)
|
||||
|
||||
// then
|
||||
expect(result).toEqual({ sessionId: "$7", paneId: "%42", windowTarget: "test-session:0" })
|
||||
expect(await readLogLines(stub.logPath)).toEqual([
|
||||
"display", "-p", "-F", "#{session_id}", "-t", "%42",
|
||||
"display", "-p", "-F", "#{session_name}:#{window_index}", "-t", "%42",
|
||||
expect(calls).toEqual([
|
||||
{ tmuxPath: "tmux", args: ["display", "-p", "-F", "#{session_id}", "-t", "%42"] },
|
||||
{ tmuxPath: "tmux", args: ["display", "-p", "-F", "#{session_name}:#{window_index}", "-t", "%42"] },
|
||||
])
|
||||
})
|
||||
|
||||
test("#given TMUX_PANE=%42 and display returns 'garbage' #when resolve runs #then returns null", async () => {
|
||||
// given
|
||||
process.env.TMUX_PANE = "%42"
|
||||
const stub = await createTmuxStub({ stdout: "garbage", exitCode: 0 })
|
||||
const { runCommand } = createRunCommandMock([tmuxResult("garbage")])
|
||||
|
||||
// when
|
||||
const result = await resolveCallerTmuxSession(stub.tmuxPath)
|
||||
const result = await resolveCallerTmuxSession("tmux", "%42", runCommand)
|
||||
|
||||
// then
|
||||
expect(result).toBeNull()
|
||||
@@ -97,11 +72,10 @@ describe("resolveCallerTmuxSession", () => {
|
||||
|
||||
test("#given TMUX_PANE=%42 and display exits non-success #when resolve runs #then returns null", async () => {
|
||||
// given
|
||||
process.env.TMUX_PANE = "%42"
|
||||
const stub = await createTmuxStub({ stdout: "$7", exitCode: 1 })
|
||||
const { runCommand } = createRunCommandMock([tmuxResult("$7", 1)])
|
||||
|
||||
// when
|
||||
const result = await resolveCallerTmuxSession(stub.tmuxPath)
|
||||
const result = await resolveCallerTmuxSession("tmux", "%42", runCommand)
|
||||
|
||||
// then
|
||||
expect(result).toBeNull()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { runTmuxCommand } from "../../../shared/tmux"
|
||||
import type { TmuxCommandResult } from "../../../shared/tmux"
|
||||
|
||||
type ResolvedCallerTmuxSession = {
|
||||
sessionId: string
|
||||
@@ -6,16 +7,21 @@ type ResolvedCallerTmuxSession = {
|
||||
windowTarget: string
|
||||
}
|
||||
|
||||
type RunTmuxCommand = (tmuxPath: string, args: string[]) => Promise<TmuxCommandResult>
|
||||
|
||||
const TMUX_SESSION_ID_PATTERN = /^\$[0-9]+$/
|
||||
const TMUX_WINDOW_TARGET_PATTERN = /^[^:]+:[0-9]+$/
|
||||
|
||||
export async function resolveCallerTmuxSession(tmuxPath: string): Promise<ResolvedCallerTmuxSession | null> {
|
||||
const callerPaneId = process.env.TMUX_PANE
|
||||
export async function resolveCallerTmuxSession(
|
||||
tmuxPath: string,
|
||||
callerPaneId: string | undefined = process.env.TMUX_PANE,
|
||||
runCommand: RunTmuxCommand = runTmuxCommand,
|
||||
): Promise<ResolvedCallerTmuxSession | null> {
|
||||
if (!callerPaneId) {
|
||||
return null
|
||||
}
|
||||
|
||||
const sessionResult = await runTmuxCommand(tmuxPath, ["display", "-p", "-F", "#{session_id}", "-t", callerPaneId])
|
||||
const sessionResult = await runCommand(tmuxPath, ["display", "-p", "-F", "#{session_id}", "-t", callerPaneId])
|
||||
if (!sessionResult.success) {
|
||||
return null
|
||||
}
|
||||
@@ -25,7 +31,7 @@ export async function resolveCallerTmuxSession(tmuxPath: string): Promise<Resolv
|
||||
return null
|
||||
}
|
||||
|
||||
const windowResult = await runTmuxCommand(tmuxPath, ["display", "-p", "-F", "#{session_name}:#{window_index}", "-t", callerPaneId])
|
||||
const windowResult = await runCommand(tmuxPath, ["display", "-p", "-F", "#{session_name}:#{window_index}", "-t", callerPaneId])
|
||||
if (!windowResult.success) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
/// <reference path="../../../bun-test.d.ts" />
|
||||
import { describe, test, expect, mock, beforeEach, spyOn, afterAll } from 'bun:test'
|
||||
import { describe, test, expect, mock, beforeEach, spyOn, afterAll, afterEach } from 'bun:test'
|
||||
import type { TmuxConfig } from '../../config/schema'
|
||||
import type { WindowState, PaneAction } from './types'
|
||||
import type { ActionResult, ExecuteContext } from './action-executor'
|
||||
import type { TmuxSessionManager as TmuxSessionManagerType, TmuxUtilDeps } from './manager'
|
||||
import * as sharedModule from '../../shared'
|
||||
import * as sharedTmuxOriginal from '../../shared/tmux'
|
||||
|
||||
const sharedTmuxSnapshot = { ...sharedTmuxOriginal }
|
||||
|
||||
type ExecuteActionsResult = {
|
||||
success: boolean
|
||||
@@ -131,6 +134,11 @@ function registerModuleMocks(): void {
|
||||
|
||||
afterAll(() => { mock.restore() })
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore()
|
||||
mock.module('../../shared/tmux', () => sharedTmuxSnapshot)
|
||||
})
|
||||
|
||||
const trackedSessions = new Set<string>()
|
||||
const readySessions = new Set<string>()
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
/// <reference path="../../../bun-test.d.ts" />
|
||||
import { beforeEach, describe, expect, mock, test, afterAll } from "bun:test"
|
||||
import { afterEach, beforeEach, describe, expect, mock, test, afterAll } from "bun:test"
|
||||
import type { TmuxConfig } from "../../config/schema"
|
||||
import type { ActionResult, ExecuteContext, ExecuteActionsResult } from "./action-executor"
|
||||
import type { TmuxUtilDeps } from "./manager"
|
||||
import type { TrackedSession, WindowState } from "./types"
|
||||
import * as sharedTmuxOriginal from "../../shared/tmux"
|
||||
|
||||
const sharedTmuxSnapshot = { ...sharedTmuxOriginal }
|
||||
|
||||
const mockQueryWindowState = mock<(paneId: string) => Promise<WindowState | null>>(async () => ({
|
||||
windowWidth: 220,
|
||||
@@ -53,6 +56,11 @@ function registerModuleMocks(): void {
|
||||
|
||||
afterAll(() => { mock.restore() })
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore()
|
||||
mock.module("../../shared/tmux", () => sharedTmuxSnapshot)
|
||||
})
|
||||
|
||||
const mockTmuxDeps: TmuxUtilDeps = {
|
||||
isInsideTmux: mockIsInsideTmux,
|
||||
getCurrentPaneId: mockGetCurrentPaneId,
|
||||
|
||||
Reference in New Issue
Block a user