Post-merge fixes after rebasing the distributed worker pool onto the review-goal-semantics train tip: - Pool SCM submission tests target the train's relocated receive-pack scanner: FEATURE_REF_RE now lives in receive_pack_scan, bodies are built via the shared _receive_command helper (valid pack), and the update-rejection assertion matches the train's message. - Take the train's canonical test_hermes_scm_broker, test_hermes_cli_dispatch_runtime and test_hermes_cli_execution_edges, which exercise the train's broker/dispatch/execution behavior. - Runtime staging tests patch os.fchown alongside os.chown so the UID-10000 _write_secret path passes under a non-root gate runner (production ownership behavior unchanged). - Split the jenkins build-evidence contracts out of test_hermes_runtime_access into test_hermes_runtime_evidence to keep both files under the 500-LOC hygiene ceiling after the merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
105 lines
3.2 KiB
Python
105 lines
3.2 KiB
Python
"""Contracts for Hermes' Jenkins build-evidence reader boundary."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).parents[2]
|
|
HERMES = ROOT / "services" / "hermes"
|
|
SCRIPTS = HERMES / "scripts"
|
|
SCM_SCRIPTS = HERMES / "scm-common" / "scripts"
|
|
if str(SCRIPTS) not in sys.path:
|
|
sys.path.insert(0, str(SCRIPTS))
|
|
if str(SCM_SCRIPTS) not in sys.path:
|
|
sys.path.insert(0, str(SCM_SCRIPTS))
|
|
|
|
|
|
def _load(name: str):
|
|
root = SCM_SCRIPTS if name == "gitea_api" else SCRIPTS
|
|
spec = importlib.util.spec_from_file_location(name, root / f"{name}.py")
|
|
assert spec and spec.loader
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[spec.name] = module
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def test_jenkins_evidence_ignores_nested_execution_result():
|
|
evidence_reader = _load("jenkins_build_evidence")
|
|
evidence = evidence_reader.parse_build_xml(
|
|
"""
|
|
<flow-build>
|
|
<actions><revision><hash>abc123</hash></revision></actions>
|
|
<timestamp>1000</timestamp><duration>0</duration>
|
|
<execution><result>SUCCESS</result></execution>
|
|
</flow-build>
|
|
""",
|
|
job="demo",
|
|
branch="master",
|
|
number=15,
|
|
)
|
|
|
|
assert evidence.revision == "abc123"
|
|
assert evidence.result is None
|
|
assert evidence.building is True
|
|
|
|
|
|
def test_jenkins_evidence_reads_only_top_level_terminal_result():
|
|
evidence_reader = _load("jenkins_build_evidence")
|
|
evidence = evidence_reader.parse_build_xml(
|
|
"""
|
|
<flow-build>
|
|
<actions><revision><hash>abc123</hash></revision></actions>
|
|
<timestamp>1000</timestamp><duration>250</duration>
|
|
<result>FAILURE</result>
|
|
<execution><result>SUCCESS</result></execution>
|
|
</flow-build>
|
|
""",
|
|
job="demo",
|
|
branch=None,
|
|
number=4,
|
|
)
|
|
|
|
assert evidence.result == "FAILURE"
|
|
assert evidence.building is False
|
|
assert evidence.duration_ms == 250
|
|
|
|
|
|
def test_jenkins_evidence_removes_hidden_links_and_ansi_control_sequences():
|
|
evidence_reader = _load("jenkins_build_evidence")
|
|
|
|
value = (
|
|
"before\n\x1b[8mha:////private-jenkins-payload\x1b[0m[Pipeline] sh\n"
|
|
"\x1b[31m5 passed\x1b[0m\n"
|
|
)
|
|
|
|
assert evidence_reader.sanitize_log(value) == "before\n[Pipeline] sh\n5 passed\n"
|
|
|
|
|
|
def test_jenkins_evidence_falls_back_to_multibranch_suffix(monkeypatch):
|
|
evidence_reader = _load("jenkins_build_evidence")
|
|
calls = []
|
|
|
|
def fake_find(job, branch, commit, log_lines):
|
|
calls.append((job, branch, commit, log_lines))
|
|
if job == "demo":
|
|
raise ValueError("not a multibranch job")
|
|
return "evidence"
|
|
|
|
monkeypatch.setattr(evidence_reader, "_find_build_for_job", fake_find)
|
|
|
|
assert evidence_reader.find_build("demo", "master", "abc", 20) == "evidence"
|
|
assert [call[0] for call in calls] == ["demo", "demo-branches"]
|
|
|
|
|
|
@pytest.mark.parametrize("branch", ["../master", "/master", "feature//unsafe"])
|
|
def test_jenkins_evidence_rejects_unsafe_branch_paths(branch: str):
|
|
evidence_reader = _load("jenkins_build_evidence")
|
|
|
|
with pytest.raises(ValueError):
|
|
evidence_reader._validate_branch(branch)
|