from __future__ import annotations import base64 import importlib.util import json import os import subprocess import sys import tempfile import threading import time import unittest from pathlib import Path MODULE_PATH = Path(__file__).parents[1] / "scripts" / "present.py" SPEC = importlib.util.spec_from_file_location("artifact_presenter", MODULE_PATH) assert SPEC and SPEC.loader MODULE = importlib.util.module_from_spec(SPEC) sys.modules[SPEC.name] = MODULE SPEC.loader.exec_module(MODULE) class FakeRunner: def __init__( self, panes: list[dict[str, str]] | None = None, *, failed_run_panes: set[str] | None = None, fail_rename: bool = False, foreground_processes: dict[str, list[str]] | None = None, shell_processes: dict[str, str] | None = None, viewer_teardown_processes: dict[str, list[list[str]]] | None = None, write_start_acknowledgment: bool = True, list_delay_seconds: float = 0, ) -> None: self.panes = panes or [] self.failed_run_panes = failed_run_panes or set() self.fail_rename = fail_rename self.foreground_processes = { pane_id: list(processes) for pane_id, processes in (foreground_processes or {}).items() } self.shell_processes = shell_processes or {} self.viewer_teardown_processes = { pane_id: [list(processes) for processes in snapshots] for pane_id, snapshots in (viewer_teardown_processes or {}).items() } self.viewer_closing_panes: set[str] = set() self.write_start_acknowledgment = write_start_acknowledgment self.pending_commands: dict[str, str] = {} self.list_delay_seconds = list_delay_seconds self.calls: list[list[str]] = [] def __call__(self, command, **_kwargs): argv = [str(value) for value in command] self.calls.append(argv) if argv[1:3] == ["pane", "send-text"]: pane_id = argv[3] if pane_id in self.failed_run_panes: raise subprocess.CalledProcessError(1, argv) self.pending_commands[pane_id] = argv[4] command_parts = argv[4].strip().split() decoded = [ base64.urlsafe_b64decode(value.encode("ascii")).decode("utf-8") for value in command_parts[-4:] ] if self.write_start_acknowledgment and decoded[-2] == "/usr/bin/mcat": Path(decoded[-4]).write_text(decoded[-3], encoding="utf-8") if argv[1:3] == ["pane", "send-keys"] and argv[4] == "q": pane_id = argv[3] self.viewer_closing_panes.add(pane_id) if argv[1:3] == ["pane", "rename"] and self.fail_rename: raise subprocess.CalledProcessError(1, argv) if argv[1:3] == ["pane", "rename"]: pane_id, label = argv[3], argv[4] pane = next( (candidate for candidate in self.panes if candidate["pane_id"] == pane_id), None, ) if pane is None: self.panes.append({"pane_id": pane_id, "label": label}) else: pane["label"] = label if argv[1:3] == ["pane", "list"]: pane_snapshot = [dict(pane) for pane in self.panes] if self.list_delay_seconds: time.sleep(self.list_delay_seconds) output = {"result": {"panes": pane_snapshot}} elif argv[1:3] == ["pane", "process-info"]: pane_id = argv[4] shell_name = self.shell_processes.get(pane_id, "fish") teardown = self.viewer_teardown_processes.get(pane_id, []) if pane_id in self.viewer_closing_panes and teardown: process_names = teardown.pop(0) elif pane_id in self.viewer_closing_panes: process_names = [shell_name] self.viewer_closing_panes.remove(pane_id) self.foreground_processes[pane_id] = process_names else: process_names = self.foreground_processes.get(pane_id, [shell_name]) output = { "result": { "process_info": { "pane_id": pane_id, "shell_pid": 100, "foreground_processes": [ { "name": name, "pid": 100 if name == shell_name else index + 101, } for index, name in enumerate(process_names) ], } } } elif argv[1:3] == ["pane", "split"]: output = {"result": {"pane": {"pane_id": "workspace:p-new"}}} else: output = {} return subprocess.CompletedProcess(argv, 0, json.dumps(output), "") class OrcaAwareRunner(FakeRunner): def __init__(self, *args, fail_orca_open: bool = False, **kwargs) -> None: super().__init__(*args, **kwargs) self.fail_orca_open = fail_orca_open def __call__(self, command, **kwargs): argv = [str(value) for value in command] if argv[1:3] == ["file", "open"]: self.calls.append(argv) if self.fail_orca_open: raise subprocess.CalledProcessError(1, argv) output = {"ok": True, "result": {"opened": True}} return subprocess.CompletedProcess(argv, 0, json.dumps(output), "") return super().__call__(command, **kwargs) class ArtifactPresenterTests(unittest.TestCase): def setUp(self) -> None: self.tempdir = tempfile.TemporaryDirectory() self.artifact = Path(self.tempdir.name) / "report.md" self.artifact.write_text("# Report\n", encoding="utf-8") self.env = { "HERDR_WORKSPACE_ID": "workspace", "HERDR_PANE_ID": "workspace:p-source", } self.executables = { "herdr": "/usr/bin/herdr", "mcat": "/usr/bin/mcat", "orca": "/usr/bin/orca", } def tearDown(self) -> None: self.tempdir.cleanup() def which(self, name: str) -> str | None: return self.executables.get(name) def mcat_run_call( self, runner: FakeRunner, pane_id: str, artifact: Path | None = None, ) -> list[str]: expected_artifact = str((artifact or self.artifact).resolve()) return next( call for call in runner.calls if call[1:3] == ["pane", "send-text"] and call[3] == pane_id and [ base64.urlsafe_b64decode(value.encode("ascii")).decode("utf-8") for value in call[4].strip().split()[-2:] ] == ["/usr/bin/mcat", expected_artifact] ) def test_reuses_named_pane(self) -> None: runner = FakeRunner( [{"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}], ) result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "reused") self.assertEqual(result.pane_id, "workspace:p-viewer") self.mcat_run_call(runner, "workspace:p-viewer") self.assertFalse(any(call[1:3] == ["pane", "split"] for call in runner.calls)) def test_closes_active_mcat_before_reusing_named_pane(self) -> None: runner = FakeRunner( [{"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}], foreground_processes={"workspace:p-viewer": ["mcat", "less"]}, ) result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "reused") close_viewer = [ "/usr/bin/herdr", "pane", "send-keys", "workspace:p-viewer", "q", ] launch_viewer = self.mcat_run_call(runner, "workspace:p-viewer") self.assertIn(close_viewer, runner.calls) self.assertLess(runner.calls.index(close_viewer), runner.calls.index(launch_viewer)) def test_waits_through_transient_viewer_teardown_process(self) -> None: runner = FakeRunner( [{"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}], foreground_processes={"workspace:p-viewer": ["mcat", "less"]}, viewer_teardown_processes={"workspace:p-viewer": [["less"]]}, ) result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "reused") process_checks = [ call for call in runner.calls if call[1:3] == ["pane", "process-info"] ] self.assertEqual(len(process_checks), 4) self.mcat_run_call(runner, "workspace:p-viewer") def test_requires_start_acknowledgment_after_launch(self) -> None: runner = FakeRunner( [{"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}], ) result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "reused") launch = self.mcat_run_call(runner, "workspace:p-viewer") command = launch[4].strip().split() decoded = [ base64.urlsafe_b64decode(value.encode("ascii")).decode("utf-8") for value in command[-4:] ] acknowledgment = Path(decoded[-4]) self.assertEqual(Path(command[1]).name, "launch-viewer.py") self.assertEqual(acknowledgment.name, "viewer-started") self.assertEqual(len(decoded[-3]), 24) def test_does_not_report_success_without_start_acknowledgment(self) -> None: runner = FakeRunner( [{"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}], write_start_acknowledgment=False, ) original_attempts = MODULE.VIEWER_START_ATTEMPTS original_delay = MODULE.PANE_READY_DELAY_SECONDS MODULE.VIEWER_START_ATTEMPTS = 2 MODULE.PANE_READY_DELAY_SECONDS = 0 try: result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) finally: MODULE.VIEWER_START_ATTEMPTS = original_attempts MODULE.PANE_READY_DELAY_SECONDS = original_delay self.assertEqual(result.status, "artifact-only") self.assertIn("could not initialize Herdr viewer", result.reason) def test_accepts_idle_shell_by_pid_without_shell_name_allowlist(self) -> None: runner = FakeRunner( [{"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}], foreground_processes={"workspace:p-viewer": ["xonsh"]}, shell_processes={"workspace:p-viewer": "xonsh"}, ) result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "reused") self.assertFalse( any( call[1:3] == ["pane", "send-keys"] and call[4] == "q" for call in runner.calls ) ) def test_empty_foreground_state_is_not_treated_as_ready(self) -> None: runner = FakeRunner( [{"pane_id": "workspace:p-transitioning", "label": MODULE.PANE_LABEL}], foreground_processes={"workspace:p-transitioning": []}, ) original_attempts = MODULE.PANE_READY_ATTEMPTS original_delay = MODULE.PANE_READY_DELAY_SECONDS MODULE.PANE_READY_ATTEMPTS = 2 MODULE.PANE_READY_DELAY_SECONDS = 0 try: result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) finally: MODULE.PANE_READY_ATTEMPTS = original_attempts MODULE.PANE_READY_DELAY_SECONDS = original_delay self.assertEqual(result.status, "opened") process_checks = [ call for call in runner.calls if call[1:3] == ["pane", "process-info"] and call[4] == "workspace:p-transitioning" ] self.assertEqual(len(process_checks), 2) def test_does_not_interrupt_unrelated_foreground_process(self) -> None: runner = FakeRunner( [{"pane_id": "workspace:p-busy", "label": MODULE.PANE_LABEL}], foreground_processes={"workspace:p-busy": ["vim"]}, ) result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "opened") self.assertFalse( any( call[1:3] == ["pane", "send-keys"] and call[4] == "q" for call in runner.calls ) ) self.assertIn( [ "/usr/bin/herdr", "pane", "rename", "workspace:p-busy", f"{MODULE.PANE_LABEL} stale", ], runner.calls, ) def test_tries_each_named_pane_before_creating_another(self) -> None: runner = FakeRunner( [ {"pane_id": "workspace:p-stale", "label": MODULE.PANE_LABEL}, {"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}, ], failed_run_panes={"workspace:p-stale"}, ) result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "reused") self.assertEqual(result.pane_id, "workspace:p-viewer") self.assertIn( [ "/usr/bin/herdr", "pane", "rename", "workspace:p-stale", f"{MODULE.PANE_LABEL} stale", ], runner.calls, ) self.assertFalse(any(call[1:3] == ["pane", "split"] for call in runner.calls)) def test_opens_editor_friendly_file_in_orca_managed_session(self) -> None: source = self.artifact.with_name("module.ts") source.write_text("export const answer = 42;\n", encoding="utf-8") runner = OrcaAwareRunner() result = MODULE.present_document( source, env={**self.env, "ORCA_WORKSPACE_ID": "workspace"}, which=self.which, run=runner, ) self.assertEqual(result.status, "orca-opened") self.assertEqual( runner.calls, [["/usr/bin/orca", "file", "open", str(source.resolve()), "--json"]], ) def test_uses_configured_orca_cli_command(self) -> None: self.executables["orca-wsl"] = "/opt/orca/orca-wsl" runner = OrcaAwareRunner() result = MODULE.present_document( self.artifact, env={"TERM_PROGRAM": "Orca", "ORCA_CLI_COMMAND": "orca-wsl"}, which=self.which, run=runner, ) self.assertEqual(result.status, "orca-opened") self.assertEqual(runner.calls[0][0], "/opt/orca/orca-wsl") def test_falls_back_to_mcat_when_orca_cannot_open_text_file(self) -> None: runner = OrcaAwareRunner( [{"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}], fail_orca_open=True, ) result = MODULE.present_document( self.artifact, env={**self.env, "ORCA_TERMINAL_HANDLE": "term-1"}, which=self.which, run=runner, ) self.assertEqual(result.status, "reused") self.assertEqual(runner.calls[0][1:3], ["file", "open"]) self.mcat_run_call(runner, "workspace:p-viewer") def test_presents_binary_file_directly_with_mcat_in_orca_session(self) -> None: video = self.artifact.with_name("review.mp4") video.write_bytes(b"video\0fixture") runner = OrcaAwareRunner( [{"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}], ) result = MODULE.present_document( video, env={**self.env, "TERM_PROGRAM": "Orca"}, which=self.which, run=runner, ) self.assertEqual(result.status, "reused") self.assertFalse(any(call[1:3] == ["file", "open"] for call in runner.calls)) self.mcat_run_call(runner, "workspace:p-viewer", video) def test_encodes_artifact_paths_before_terminal_delivery(self) -> None: artifact = self.artifact.with_name("report '$(touch unsafe)'.md") artifact.write_text("# Safe\n", encoding="utf-8") runner = FakeRunner( [{"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}], ) result = MODULE.present_document( artifact, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "reused") launch = self.mcat_run_call(runner, "workspace:p-viewer", artifact) command_parts = launch[4].strip().split() self.assertTrue(all(MODULE.SAFE_COMMAND_TOKEN.fullmatch(part) for part in command_parts)) self.assertNotIn("$(touch unsafe)", launch[4]) def test_creates_right_pane_when_viewer_is_absent(self) -> None: runner = FakeRunner() result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "opened") split = next(call for call in runner.calls if call[1:3] == ["pane", "split"]) self.assertEqual( split, [ "/usr/bin/herdr", "pane", "split", "--pane", "workspace:p-source", "--direction", "right", "--cwd", str(self.artifact.parent.resolve()), "--no-focus", ], ) self.assertIn( [ "/usr/bin/herdr", "pane", "rename", "workspace:p-new", MODULE.PANE_LABEL, ], runner.calls, ) def test_closes_new_pane_when_rename_fails(self) -> None: runner = FakeRunner(fail_rename=True) result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "artifact-only") self.assertIn("could not initialize Herdr viewer", result.reason) self.assertIn( ["/usr/bin/herdr", "pane", "close", "workspace:p-new"], runner.calls, ) def test_falls_back_to_artifact_when_mcat_is_unavailable(self) -> None: self.executables.pop("mcat") result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=FakeRunner(), ) self.assertEqual(result.status, "artifact-only") self.assertEqual(result.reason, "mcat is unavailable") def test_pane_lock_serializes_concurrent_presenters(self) -> None: first_acquired = threading.Event() release_first = threading.Event() second_acquired = threading.Event() original_runtime = os.environ.get("XDG_RUNTIME_DIR") os.environ["XDG_RUNTIME_DIR"] = self.tempdir.name def first_presenter() -> None: with MODULE.lock_pane("workspace:p-viewer"): first_acquired.set() release_first.wait(timeout=1) def second_presenter() -> None: first_acquired.wait(timeout=1) with MODULE.lock_pane("workspace:p-viewer"): second_acquired.set() first = threading.Thread(target=first_presenter) second = threading.Thread(target=second_presenter) try: first.start() second.start() self.assertTrue(first_acquired.wait(timeout=1)) self.assertFalse(second_acquired.wait(timeout=0.05)) release_first.set() self.assertTrue(second_acquired.wait(timeout=1)) finally: release_first.set() first.join(timeout=1) second.join(timeout=1) if original_runtime is None: os.environ.pop("XDG_RUNTIME_DIR", None) else: os.environ["XDG_RUNTIME_DIR"] = original_runtime def test_workspace_lock_prevents_duplicate_viewer_creation(self) -> None: runner = FakeRunner(list_delay_seconds=0.05) results: list[MODULE.PresentationResult] = [] results_lock = threading.Lock() original_runtime = os.environ.get("XDG_RUNTIME_DIR") os.environ["XDG_RUNTIME_DIR"] = self.tempdir.name def present() -> None: result = MODULE.present_document( self.artifact, env=self.env, which=self.which, run=runner, ) with results_lock: results.append(result) threads = [threading.Thread(target=present) for _ in range(2)] try: for thread in threads: thread.start() for thread in threads: thread.join(timeout=2) finally: if original_runtime is None: os.environ.pop("XDG_RUNTIME_DIR", None) else: os.environ["XDG_RUNTIME_DIR"] = original_runtime self.assertEqual(sorted(result.status for result in results), ["opened", "reused"]) splits = [call for call in runner.calls if call[1:3] == ["pane", "split"]] self.assertEqual(len(splits), 1) def test_rejects_missing_artifact(self) -> None: with self.assertRaisesRegex(ValueError, "artifact is not a file"): MODULE.present_document( self.artifact.with_name("missing.md"), env=self.env, which=self.which, run=FakeRunner(), ) if __name__ == "__main__": unittest.main()