/ extensions / cmux / test / index.test.ts
index.test.ts
  1  import assert from "node:assert/strict";
  2  import { afterEach, test } from "node:test";
  3  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
  4  
  5  type ExecResult = {
  6  	code: number;
  7  	stdout: string;
  8  	stderr?: string;
  9  };
 10  
 11  type EventHandler = (event: unknown, ctx: TestContext) => Promise<void> | void;
 12  
 13  type TestContext = {
 14  	hasUI: boolean;
 15  	model?: { id: string };
 16  	sessionManager: {
 17  		getBranch(): unknown[];
 18  	};
 19  	getContextUsage(): { tokens?: number } | undefined;
 20  };
 21  
 22  type Harness = {
 23  	handlers: Map<string, EventHandler>;
 24  	execCalls: Array<{ command: string; args: string[] }>;
 25  	pi: ExtensionAPI;
 26  };
 27  
 28  const ORIGINAL_CMUX_SOCKET_PATH = process.env.CMUX_SOCKET_PATH;
 29  const ORIGINAL_CMUX_WORKSPACE_ID = process.env.CMUX_WORKSPACE_ID;
 30  
 31  function createContext(): TestContext {
 32  	return {
 33  		hasUI: true,
 34  		sessionManager: {
 35  			getBranch: () => [],
 36  		},
 37  		getContextUsage: () => undefined,
 38  	};
 39  }
 40  
 41  function createHarness(treeOutputs: string[]): Harness {
 42  	const handlers = new Map<string, EventHandler>();
 43  	const execCalls: Array<{ command: string; args: string[] }> = [];
 44  	let treeIndex = 0;
 45  
 46  	const pi = {
 47  		on(event: string, handler: EventHandler): void {
 48  			handlers.set(event, handler);
 49  		},
 50  		exec: async (command: string, args: string[]): Promise<ExecResult> => {
 51  			execCalls.push({ command, args });
 52  			assert.equal(command, "cmux");
 53  
 54  			if (args[0] === "tree") {
 55  				const stdout = treeOutputs[treeIndex] ?? treeOutputs.at(-1) ?? "";
 56  				treeIndex += 1;
 57  				return { code: 0, stdout };
 58  			}
 59  
 60  			return { code: 0, stdout: "" };
 61  		},
 62  		getSessionName: (): string => "alpha-session",
 63  		getThinkingLevel: (): "off" => "off",
 64  	} as unknown as ExtensionAPI;
 65  
 66  	return { handlers, execCalls, pi };
 67  }
 68  
 69  async function loadExtension() {
 70  	process.env.CMUX_SOCKET_PATH = "/tmp/cmux.sock";
 71  	process.env.CMUX_WORKSPACE_ID = "workspace-shell-123";
 72  	const moduleUrl = new URL(`../index.ts?test=${Date.now()}`, import.meta.url);
 73  	return (await import(moduleUrl.href)).default as (pi: ExtensionAPI) => void;
 74  }
 75  
 76  afterEach(() => {
 77  	if (ORIGINAL_CMUX_SOCKET_PATH === undefined) {
 78  		delete process.env.CMUX_SOCKET_PATH;
 79  	} else {
 80  		process.env.CMUX_SOCKET_PATH = ORIGINAL_CMUX_SOCKET_PATH;
 81  	}
 82  
 83  	if (ORIGINAL_CMUX_WORKSPACE_ID === undefined) {
 84  		delete process.env.CMUX_WORKSPACE_ID;
 85  	} else {
 86  		process.env.CMUX_WORKSPACE_ID = ORIGINAL_CMUX_WORKSPACE_ID;
 87  	}
 88  });
 89  
 90  test("agent_end sync uses the shell workspace id instead of cmux current-workspace", async () => {
 91  	const cmuxExtension = await loadExtension();
 92  	const harness = createHarness([
 93  		'workspace workspace:1 "alpha-session"\n  pane pane:1\n    surface surface:1\n',
 94  		'workspace workspace:1 "original-title"\n  pane pane:1\n    surface surface:1\n',
 95  	]);
 96  	cmuxExtension(harness.pi);
 97  
 98  	const sessionStartHandler = harness.handlers.get("session_start");
 99  	const agentEndHandler = harness.handlers.get("agent_end");
100  	assert.ok(sessionStartHandler, "expected session_start handler");
101  	assert.ok(agentEndHandler, "expected agent_end handler");
102  
103  	await sessionStartHandler({}, createContext());
104  	harness.execCalls.length = 0;
105  
106  	await agentEndHandler({}, createContext());
107  
108  	assert.equal(
109  		harness.execCalls.some((call) => call.command === "cmux" && call.args[0] === "current-workspace"),
110  		false,
111  	);
112  
113  	assert.deepEqual(
114  		harness.execCalls.find((call) => call.command === "cmux" && call.args[0] === "tree")?.args,
115  		["tree", "--workspace", "workspace-shell-123"],
116  	);
117  	assert.deepEqual(
118  		harness.execCalls.find((call) => call.command === "cmux" && call.args[0] === "rename-workspace")?.args,
119  		["rename-workspace", "--workspace", "workspace-shell-123", "alpha-session"],
120  	);
121  });