from __future__ import annotations import importlib.util import json import subprocess import sys import tempfile 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, ) -> None: self.panes = panes or [] self.failed_run_panes = failed_run_panes or set() self.fail_rename = fail_rename 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", "run"] and argv[3] in self.failed_run_panes: raise subprocess.CalledProcessError(1, argv) if argv[1:3] == ["pane", "rename"] and self.fail_rename: raise subprocess.CalledProcessError(1, argv) if argv[1:3] == ["pane", "list"]: output = {"result": {"panes": self.panes}} 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 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"} def tearDown(self) -> None: self.tempdir.cleanup() def which(self, name: str) -> str | None: return self.executables.get(name) 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.assertIn( [ "/usr/bin/herdr", "pane", "run", "workspace:p-viewer", "/usr/bin/mcat", str(self.artifact.resolve()), ], runner.calls, ) self.assertFalse(any(call[1:3] == ["pane", "split"] for call in 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_presents_non_markdown_file_directly_with_mcat(self) -> None: video = self.artifact.with_name("review.mp4") video.write_bytes(b"video fixture") runner = FakeRunner( [{"pane_id": "workspace:p-viewer", "label": MODULE.PANE_LABEL}], ) result = MODULE.present_document( video, env=self.env, which=self.which, run=runner, ) self.assertEqual(result.status, "reused") self.assertIn( [ "/usr/bin/herdr", "pane", "run", "workspace:p-viewer", "/usr/bin/mcat", str(video.resolve()), ], runner.calls, ) 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_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()