"""Tests for .opencode/tools/pipeline-status.ts — the pipeline_status custom tool. Covers the spawnSync-based implementation that replaced the original ``Bun.$`` spawn (issue #99 / PR-?). The TS tool file is Bun-runtime code (TypeScript + ``import.meta.dir``) and there is no bun/tsx/esbuild on the CI runner — only node + pytest. We exercise the tool's ``execute()`` function via a tiny CommonJS loader (``tests/_ts_loader.mjs``) which: - strips TS-only import type annotations, - stubs ``@opencode-ai/plugin``'s ``tool()`` (identity) + ``tool.schema`` (chainable zod shim), - replaces ``import.meta.dir`` with the real ``.opencode/tools`` directory, - exposes the ``execute()`` function via a JSON-stdout protocol. Three modes are used by these tests: - ``load`` — sanity-check that the tool loads and has ``pr_number`` arg. - ``exec_stub`` — call execute with a stubbed spawnSync to verify: (a) args passed correctly, (b) stdout is trimmed on success, (c) non-zero exit returns an actionable error message. - ``exec_real`` — call execute against the real pipeline-status.py (integration test, PR #23 is a known-good reference). """ import json import subprocess from pathlib import Path import pytest REPO_ROOT = Path(__file__).resolve().parent.parent LOADER = REPO_ROOT / "tests" / "_ts_loader.mjs" TS_FILE = REPO_ROOT / ".opencode" / "tools" / "pipeline-status.ts" def _gh_available() -> bool: """True if `gh auth status` succeeds (local dev machine, not CI runner).""" try: return ( subprocess.run( ["gh", "auth", "status"], capture_output=True, check=False, timeout=10, ).returncode == 0 ) except (FileNotFoundError, subprocess.TimeoutExpired): return False # Integration tests below hit the real pipeline-status.py which makes gh/git # calls to GitHub. The CI runner has no gh auth — pipeline-status.py returns # non-zero exit and our tool surfaces the error string. Skip those tests in # that environment; the unit tests cover the same code paths. _GH_OK = _gh_available() _SKIP_REASON = "gh CLI not authenticated — skip real pipeline-status.py call" def _run_loader(*args: str, stdin: str | None = None) -> dict: """Invoke the loader and parse its JSON stdout.""" proc = subprocess.run( ["node", str(LOADER), *args], capture_output=True, text=True, check=False, cwd=str(REPO_ROOT), input=stdin, timeout=60, ) if proc.returncode != 0: raise RuntimeError( f"_ts_loader.mjs {' '.join(args)} failed (exit {proc.returncode}):\n" f"stdout: {proc.stdout}\nstderr: {proc.stderr}" ) return json.loads(proc.stdout) def test_loader_can_load_tool(): """Sanity: the TS tool loads and has the pr_number argument.""" if not TS_FILE.exists(): pytest.skip("pipeline-status.ts not present") out = _run_loader("load") assert "description" in out assert "pr_number" in out["args"] def test_execute_passes_correct_args(): """execute calls spawnSync with ["python3",