ORCH-6: test_projects.py covers resolvers and ORCH_PROJECTS_JSON parsing (valid/malformed/fallback). test_plane_webhook.py covers the webhook project filter via TestClient (unknown->ignored, orchestrator->orchestrator repo, enduro->enduro-trails, independent ORCH/ET prefixes); launcher mocked. test_webhooks.py: register proj-1 so existing ET fixtures pass.
178 lines
6.1 KiB
Python
178 lines
6.1 KiB
Python
"""ORCH-6: tests for the project registry (src/projects.py).
|
|
|
|
Covers resolvers (by plane_id, by repo, unknown -> None, known ids) against the
|
|
built-in default registry, plus ORCH_PROJECTS_JSON parsing (valid + malformed
|
|
-> default fallback).
|
|
|
|
The pure parser ``_parse_projects_json`` is tested directly so we don't mutate
|
|
the module-global registry. Resolver tests run against the default registry; if
|
|
another test (e.g. test_webhooks) set ORCH_PROJECTS_JSON in the env, we restore
|
|
the default via monkeypatch + reload_projects to keep this file order-independent.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from src import projects as P
|
|
from src.projects import (
|
|
ProjectConfig,
|
|
get_project_by_plane_id,
|
|
get_project_by_repo,
|
|
known_plane_project_ids,
|
|
reload_projects,
|
|
_parse_projects_json,
|
|
_DEFAULT_PROJECTS,
|
|
)
|
|
|
|
# Known ids from the default registry / task spec.
|
|
ENDURO_PLANE_ID = "7a79f0a9-5278-49cd-9007-9a338f238f9c"
|
|
ORCH_PLANE_ID = "8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a"
|
|
|
|
|
|
@pytest.fixture
|
|
def default_registry(monkeypatch):
|
|
"""Force the default (built-in) registry regardless of ORCH_PROJECTS_JSON
|
|
that other test modules may have set in the process env."""
|
|
monkeypatch.setattr(P.settings, "projects_json", "")
|
|
reload_projects()
|
|
yield
|
|
# Restore from current settings (whatever env says) after the test.
|
|
reload_projects()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Resolvers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_get_project_by_plane_id_orchestrator(default_registry):
|
|
proj = get_project_by_plane_id(ORCH_PLANE_ID)
|
|
assert proj is not None
|
|
assert proj.repo == "orchestrator"
|
|
assert proj.work_item_prefix == "ORCH"
|
|
assert proj.plane_project_id == ORCH_PLANE_ID
|
|
|
|
|
|
def test_get_project_by_plane_id_enduro(default_registry):
|
|
proj = get_project_by_plane_id(ENDURO_PLANE_ID)
|
|
assert proj is not None
|
|
assert proj.repo == "enduro-trails"
|
|
assert proj.work_item_prefix == "ET"
|
|
|
|
|
|
def test_get_project_by_plane_id_unknown_returns_none(default_registry):
|
|
assert get_project_by_plane_id("00000000-0000-0000-0000-000000000000") is None
|
|
|
|
|
|
def test_get_project_by_plane_id_empty_returns_none(default_registry):
|
|
assert get_project_by_plane_id("") is None
|
|
assert get_project_by_plane_id(None) is None
|
|
|
|
|
|
def test_get_project_by_repo(default_registry):
|
|
assert get_project_by_repo("enduro-trails").work_item_prefix == "ET"
|
|
assert get_project_by_repo("orchestrator").work_item_prefix == "ORCH"
|
|
|
|
|
|
def test_get_project_by_repo_unknown_returns_none(default_registry):
|
|
assert get_project_by_repo("does-not-exist") is None
|
|
assert get_project_by_repo("") is None
|
|
assert get_project_by_repo(None) is None
|
|
|
|
|
|
def test_known_plane_project_ids(default_registry):
|
|
ids = known_plane_project_ids()
|
|
assert isinstance(ids, set)
|
|
assert ENDURO_PLANE_ID in ids
|
|
assert ORCH_PLANE_ID in ids
|
|
assert len(ids) == len(_DEFAULT_PROJECTS)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# ORCH_PROJECTS_JSON parsing (pure function, no global mutation)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_parse_empty_returns_none():
|
|
assert _parse_projects_json("") is None
|
|
assert _parse_projects_json(" ") is None
|
|
assert _parse_projects_json(None) is None
|
|
|
|
|
|
def test_parse_valid_json():
|
|
raw = (
|
|
'[{"plane_project_id": "p-1", "repo": "repo-a", '
|
|
'"work_item_prefix": "AAA", "name": "Alpha"}]'
|
|
)
|
|
parsed = _parse_projects_json(raw)
|
|
assert parsed is not None
|
|
assert len(parsed) == 1
|
|
assert isinstance(parsed[0], ProjectConfig)
|
|
assert parsed[0].plane_project_id == "p-1"
|
|
assert parsed[0].repo == "repo-a"
|
|
assert parsed[0].work_item_prefix == "AAA"
|
|
assert parsed[0].name == "Alpha"
|
|
|
|
|
|
def test_parse_valid_json_multiple():
|
|
raw = (
|
|
'[{"plane_project_id": "p-1", "repo": "repo-a", "work_item_prefix": "A"},'
|
|
' {"plane_project_id": "p-2", "repo": "repo-b", "work_item_prefix": "B"}]'
|
|
)
|
|
parsed = _parse_projects_json(raw)
|
|
assert len(parsed) == 2
|
|
# name defaults to repo when omitted
|
|
assert parsed[0].name == "repo-a"
|
|
assert parsed[1].repo == "repo-b"
|
|
|
|
|
|
def test_parse_malformed_json_returns_none():
|
|
assert _parse_projects_json("{not valid json") is None
|
|
assert _parse_projects_json("[}") is None
|
|
|
|
|
|
def test_parse_not_an_array_returns_none():
|
|
# A JSON object (not array) is invalid -> fallback.
|
|
assert _parse_projects_json('{"plane_project_id": "p-1"}') is None
|
|
|
|
|
|
def test_parse_skips_bad_entries_keeps_good():
|
|
raw = (
|
|
'[{"repo": "missing-id"},' # missing required key -> skipped
|
|
' {"plane_project_id": "p-2", "repo": "repo-b", "work_item_prefix": "B"}]'
|
|
)
|
|
parsed = _parse_projects_json(raw)
|
|
assert parsed is not None
|
|
assert len(parsed) == 1
|
|
assert parsed[0].plane_project_id == "p-2"
|
|
|
|
|
|
def test_parse_all_bad_entries_returns_none():
|
|
# No valid entries -> None (fallback to default).
|
|
assert _parse_projects_json('[{"repo": "no-id"}, "not-an-object"]') is None
|
|
|
|
|
|
def test_reload_from_custom_json(monkeypatch):
|
|
"""End-to-end: set settings.projects_json, reload, resolvers reflect it."""
|
|
custom = (
|
|
'[{"plane_project_id": "custom-uuid", "repo": "custom-repo", '
|
|
'"work_item_prefix": "CUS", "name": "Custom"}]'
|
|
)
|
|
monkeypatch.setattr(P.settings, "projects_json", custom)
|
|
reload_projects()
|
|
try:
|
|
assert get_project_by_plane_id("custom-uuid").repo == "custom-repo"
|
|
assert get_project_by_repo("custom-repo").work_item_prefix == "CUS"
|
|
assert known_plane_project_ids() == {"custom-uuid"}
|
|
# The built-in defaults must NOT be present when JSON overrides.
|
|
assert get_project_by_plane_id(ENDURO_PLANE_ID) is None
|
|
finally:
|
|
reload_projects()
|
|
|
|
|
|
def test_reload_invalid_json_falls_back_to_default(monkeypatch):
|
|
monkeypatch.setattr(P.settings, "projects_json", "{garbage")
|
|
reload_projects()
|
|
try:
|
|
assert get_project_by_plane_id(ENDURO_PLANE_ID) is not None
|
|
assert get_project_by_plane_id(ORCH_PLANE_ID) is not None
|
|
finally:
|
|
reload_projects()
|