"""Tests for expanding Jenkins multibranch folders into branch jobs.""" from __future__ import annotations from ariadne.services import hermes_multibranch as module def _folder(*names: str) -> dict: return {"jobs": [{"name": n} for n in names]} def test_a_folder_has_child_jobs_and_no_last_build() -> None: assert module.is_folder(_folder("master")) is True def test_a_buildable_job_is_never_treated_as_a_folder() -> None: """Both conditions are required, or a real job could be expanded away.""" assert module.is_folder({"lastBuild": {"number": 1}, "jobs": [{"name": "x"}]}) is False assert module.is_folder({"lastBuild": {"number": 1}}) is False assert module.is_folder({}) is False assert module.is_folder(None) is False assert module.is_folder("not-a-dict") is False def test_branches_expand_to_nested_jenkins_paths() -> None: """Jenkins addresses a branch as parent/job/branch.""" paths = module.branch_job_paths(_folder("master", "develop"), "demo-branches") assert paths == ["demo-branches/job/master", "demo-branches/job/develop"] def test_encoded_branch_names_are_passed_through_untouched() -> None: """Re-encoding or decoding a branch name yields a 404.""" paths = module.branch_job_paths(_folder("hermes-repair%2F4"), "demo") assert paths == ["demo/job/hermes-repair%2F4"] def test_expansion_is_capped() -> None: """A busy repository must not multiply the watched job count without limit.""" folder = _folder(*[f"b{i}" for i in range(20)]) assert len(module.branch_job_paths(folder, "demo")) == module.DEFAULT_MAX_BRANCHES assert len(module.branch_job_paths(folder, "demo", max_branches=2)) == 2 assert module.branch_job_paths(folder, "demo", max_branches=0) == [] assert module.branch_job_paths(folder, "demo", max_branches=-1) == [] def test_malformed_children_are_skipped_not_fatal() -> None: folder = {"jobs": [None, {"name": ""}, {"name": " "}, {"nope": 1}, {"name": "ok"}]} assert module.branch_job_paths(folder, "demo") == ["demo/job/ok"] def test_a_name_containing_a_slash_is_refused() -> None: """A raw slash would escape the parent and address an unrelated job.""" assert module.branch_job_paths(_folder("a/b"), "demo") == [] def test_non_folders_expand_to_nothing() -> None: assert module.branch_job_paths({"lastBuild": {"number": 3}}, "demo") == [] assert module.branch_job_paths(None, "demo") == [] def test_tick_expands_a_folder_into_its_branches(monkeypatch) -> None: """End to end: a multibranch job must stop being silently skipped.""" from ariadne.services import hermes_autotriage as autotriage from tests.hermes_autotriage_harness import _prepare env = _prepare(monkeypatch) payloads = { "hermes-triage-demo": {"jobs": [{"name": "master"}, {"name": "hermes-repair%2F4"}]}, "hermes-triage-demo/job/master": { "lastBuild": {"number": 3, "result": "SUCCESS", "building": False, "url": "u"} }, "hermes-triage-demo/job/hermes-repair%2F4": { "lastBuild": {"number": 4, "result": "SUCCESS", "building": False, "url": "u"} }, } monkeypatch.setattr( autotriage.hermes_jenkins_client, "fetch_job_payload", lambda job: payloads.get(job) ) jobs = autotriage.run_hermes_autotriage(env.storage)["jobs"] assert jobs["hermes-triage-demo"]["status"] == "folder" assert jobs["hermes-triage-demo/job/master"]["status"] == "healthy" assert jobs["hermes-triage-demo/job/hermes-repair%2F4"]["status"] == "healthy"