229 lines
6.9 KiB
Python
229 lines
6.9 KiB
Python
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()
|