feat(skills): add artifact presenter
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user