From ba27d555c2d318e1b13f9b9f2c576d276159912c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 15:28:13 +0900 Subject: [PATCH] fix(reliability): bound Figma-evidence Git metadata lookup --- scripts/build_figma_evidence_sync.py | 8 +- ...est_figma_evidence_git_metadata_timeout.py | 73 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 tests/test_figma_evidence_git_metadata_timeout.py diff --git a/scripts/build_figma_evidence_sync.py b/scripts/build_figma_evidence_sync.py index 058ce5159..7567ebcd3 100644 --- a/scripts/build_figma_evidence_sync.py +++ b/scripts/build_figma_evidence_sync.py @@ -63,6 +63,9 @@ def _resolve_path(value: str | Path, *, base: Path) -> Path: return base / path +GIT_METADATA_TIMEOUT_SECONDS = 5.0 + + def _source_commit(repo_root: Path) -> str: try: completed = subprocess.run( @@ -71,7 +74,10 @@ def _source_commit(repo_root: Path) -> str: capture_output=True, text=True, check=True, + timeout=GIT_METADATA_TIMEOUT_SECONDS, ) + except subprocess.TimeoutExpired: + raise RuntimeError("source commit lookup timed out") from None except Exception: return "unknown" return completed.stdout.strip() or "unknown" @@ -422,4 +428,4 @@ def main(argv: list[str] | None = None) -> int: if __name__ == "__main__": - raise SystemExit(main()) + raise SystemExit(main()) \ No newline at end of file diff --git a/tests/test_figma_evidence_git_metadata_timeout.py b/tests/test_figma_evidence_git_metadata_timeout.py new file mode 100644 index 000000000..444cd9785 --- /dev/null +++ b/tests/test_figma_evidence_git_metadata_timeout.py @@ -0,0 +1,73 @@ +"""Reliability contracts for Figma-evidence source commit discovery.""" + +from __future__ import annotations + +import importlib.util +import subprocess +from pathlib import Path + +import pytest + + +def _load_figma_evidence_sync(): + """Load the Figma evidence sync builder as a standalone script module.""" + script = Path(__file__).resolve().parents[1] / "scripts" / "build_figma_evidence_sync.py" + spec = importlib.util.spec_from_file_location("build_figma_evidence_sync_timeout", script) + assert spec is not None + module = importlib.util.module_from_spec(spec) + assert spec.loader is not None + spec.loader.exec_module(module) + return module + + +def test_source_commit_timeout_is_bounded_and_fails_closed(monkeypatch, tmp_path): + """A hung local Git lookup must fail promptly with a stable package error.""" + module = _load_figma_evidence_sync() + seen: dict[str, object] = {} + + def fake_run(*args, **kwargs): + seen.update(kwargs) + raise subprocess.TimeoutExpired( + cmd=["git", "rev-parse", "HEAD"], + timeout=kwargs.get("timeout", 999), + output="FIGMA_EVIDENCE_TIMEOUT_STDOUT_SECRET", + stderr="FIGMA_EVIDENCE_TIMEOUT_STDERR_SECRET", + ) + + monkeypatch.setattr(module.subprocess, "run", fake_run) + + with pytest.raises(RuntimeError, match=r"^source commit lookup timed out$"): + module._source_commit(tmp_path) + + timeout = seen.get("timeout") + assert isinstance(timeout, (int, float)) and not isinstance(timeout, bool) + assert 0 < timeout <= 30 + + +def test_source_commit_forwards_bounded_deadline_on_success(monkeypatch, tmp_path): + """Successful Git metadata lookup uses the same package-owned deadline.""" + module = _load_figma_evidence_sync() + seen: dict[str, object] = {} + + def fake_run(*args, **kwargs): + seen.update(kwargs) + return subprocess.CompletedProcess(args=args[0], returncode=0, stdout="abc123\n") + + monkeypatch.setattr(module.subprocess, "run", fake_run) + + assert module._source_commit(tmp_path) == "abc123" + timeout = seen.get("timeout") + assert isinstance(timeout, (int, float)) and not isinstance(timeout, bool) + assert 0 < timeout <= 30 + + +def test_source_commit_keeps_non_timeout_unknown_fallback(monkeypatch, tmp_path): + """Ordinary non-timeout Git failures retain the historical unknown fallback.""" + module = _load_figma_evidence_sync() + + def fake_run(*args, **kwargs): + raise subprocess.CalledProcessError(returncode=128, cmd=args[0]) + + monkeypatch.setattr(module.subprocess, "run", fake_run) + + assert module._source_commit(tmp_path) == "unknown"