agent-ecosystem/test/main/features/tmux-installer/TmuxInstallerMacOsHostE2E.test.ts
2026-04-14 20:16:54 +03:00

127 lines
4.9 KiB
TypeScript

/**
* @vitest-environment node
*/
import { execFileSync } from 'node:child_process';
import { beforeAll, describe, expect, it } from 'vitest';
import { TmuxStatusSourceAdapter } from '@features/tmux-installer/main/adapters/output/sources/TmuxStatusSourceAdapter';
import { isTmuxRuntimeReadyForCurrentPlatform } from '@features/tmux-installer/main/composition/runtimeSupport';
import { TmuxInstallStrategyResolver } from '@features/tmux-installer/main/infrastructure/installer/TmuxInstallStrategyResolver';
import { TmuxPackageManagerResolver } from '@features/tmux-installer/main/infrastructure/platform/TmuxPackageManagerResolver';
import { TmuxPlatformResolver } from '@features/tmux-installer/main/infrastructure/platform/TmuxPlatformResolver';
import { TmuxPlatformCommandExecutor } from '@features/tmux-installer/main/infrastructure/runtime/TmuxPlatformCommandExecutor';
import { buildEnrichedEnv } from '@main/utils/cliEnv';
import { resolveInteractiveShellEnv } from '@main/utils/shellEnv';
const isDarwin = process.platform === 'darwin';
const tmuxPath = isDarwin ? tryResolveBinary('tmux') : null;
const brewPath = isDarwin ? tryResolveBinary('brew') : null;
function createIsolatedTmuxNames(): { socketName: string; sessionName: string; marker: string } {
const suffix = `${process.pid}-${Date.now()}`;
return {
socketName: `codex-e2e-${suffix}`,
sessionName: `codex_e2e_${suffix.replace(/-/g, '_')}`,
marker: `codex-macos-runtime-${suffix}`,
};
}
function tryResolveBinary(command: string): string | null {
try {
return execFileSync('/usr/bin/which', [command], { encoding: 'utf8' }).trim() || null;
} catch {
return null;
}
}
describe.runIf(isDarwin)('tmux installer macOS host e2e', () => {
let env: NodeJS.ProcessEnv;
beforeAll(async () => {
await resolveInteractiveShellEnv();
env = buildEnrichedEnv();
});
it('resolves the current host as native macOS', async () => {
const result = await new TmuxPlatformResolver().resolve();
expect(result.platform).toBe('darwin');
expect(result.nativeSupported).toBe(true);
expect(result.linux).toBeNull();
});
it.skipIf(!brewPath)('chooses Homebrew as the macOS install strategy on this machine', async () => {
const result = await new TmuxInstallStrategyResolver().resolve();
expect(result.capability.strategy).toBe('homebrew');
expect(result.capability.supported).toBe(true);
expect(result.command).not.toBeNull();
expect(result.command?.command).toBe('brew');
expect(result.command?.args).toEqual(['install', 'tmux']);
});
it.skipIf(!tmuxPath)('finds the real tmux binary on this macOS host', async () => {
const resolver = new TmuxPackageManagerResolver();
const resolvedTmuxPath = await resolver.resolveTmuxBinary(env, 'darwin');
expect(resolvedTmuxPath).toBe(tmuxPath);
expect(resolvedTmuxPath).toContain('tmux');
});
it.skipIf(!tmuxPath)('reports tmux as runtime-ready for the live macOS host path', async () => {
const status = await new TmuxStatusSourceAdapter().getStatus();
expect(status.platform).toBe('darwin');
expect(status.host.available).toBe(true);
expect(status.effective.available).toBe(true);
expect(status.effective.location).toBe('host');
expect(status.effective.runtimeReady).toBe(true);
expect(status.host.binaryPath).toBe(tmuxPath);
expect(status.effective.binaryPath).toBe(tmuxPath);
expect(status.effective.version ?? status.host.version).toMatch(/^tmux /);
});
it.skipIf(!tmuxPath)('keeps the current platform on the tmux runtime path', async () => {
await expect(isTmuxRuntimeReadyForCurrentPlatform()).resolves.toBe(true);
});
it.skipIf(!tmuxPath)('runs a real isolated tmux session on macOS and executes a shell command in it', async () => {
const executor = new TmuxPlatformCommandExecutor();
const { socketName, sessionName, marker } = createIsolatedTmuxNames();
try {
const createSession = await executor.execTmux(
['-L', socketName, 'new-session', '-d', '-s', sessionName, '/bin/sh'],
5_000
);
expect(createSession.exitCode).toBe(0);
const listPanes = await executor.execTmux(
['-L', socketName, 'list-panes', '-t', sessionName, '-F', '#{pane_id}'],
5_000
);
expect(listPanes.exitCode).toBe(0);
const paneId = listPanes.stdout.trim();
expect(paneId).toMatch(/^%/);
const sendKeys = await executor.execTmux(
['-L', socketName, 'send-keys', '-t', paneId, `printf '${marker}'`, 'Enter'],
5_000
);
expect(sendKeys.exitCode).toBe(0);
await new Promise((resolve) => setTimeout(resolve, 150));
const capturePane = await executor.execTmux(
['-L', socketName, 'capture-pane', '-p', '-t', paneId],
5_000
);
expect(capturePane.exitCode).toBe(0);
expect(capturePane.stdout).toContain(marker);
} finally {
await executor.execTmux(['-L', socketName, 'kill-session', '-t', sessionName], 3_000);
}
});
});