"""Unit tests for the bridge's `!testme` / `!testme --quick` trigger parser (WC7 + D1). Pure: imports bridge/bridge.py (stdlib-only, no side effects at import — main() is __main__-guarded). Locks the two accepted forms and the must-NOT-trigger cases the Adversary probes (`!testmexyz`, etc.). """ from __future__ import annotations import os import sys # bridge.py reads HMAC/DRONE/GITEA secret FILES at import; point them at /dev/null (readable, empty) # so the import works in a unit context — parse_trigger doesn't use any of them. for _v in ("HMAC_FILE", "DRONE_TOKEN_FILE", "GITEA_TOKEN_FILE"): os.environ.setdefault(_v, "/dev/null") sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "bridge")) import bridge # noqa: E402 def test_plain_testme_is_cold(): assert bridge.parse_trigger("!testme") == (True, False) assert bridge.parse_trigger(" !testme ") == (True, False) # trimmed def test_quick_form(): assert bridge.parse_trigger("!testme --quick") == (True, True) assert bridge.parse_trigger(" !testme --quick \n") == (True, True) def test_non_trigger_forms_rejected(): for body in ( "!testmexyz", # the Adversary's classic negative "!testme xyz", "!testme--quick", # no space → not the quick form "!testme --quick", # double space → not an exact match (conservative) "please !testme", "testme", "", None, ): assert bridge.parse_trigger(body) == (False, False), body # --- Phase 3 U3: YunoHost-style PR comment builders (R2) ----------------------------------------- def test_start_comment_is_yunohost_shaped(): b = bridge.start_comment_body("uptime-kuma", "dfed87a39f8a", "https://drone.x/cc-ci/42") assert bridge.COMMENT_MARKER in b # re-!testme updates the same comment assert "🌻" in b and "ā³" in b # marker + in-progress assert "uptime-kuma" in b and "dfed87a3" in b assert "https://drone.x/cc-ci/42" in b def test_result_comment_image_forward_when_card_available(monkeypatch): monkeypatch.setattr(bridge, "artifact_available", lambda url: True) monkeypatch.setattr(bridge, "DASH_URL", "https://ci.example") b = bridge.result_comment_body( "uptime-kuma", "dfed87a39f8a", "42", "https://drone.x/cc-ci/42", "success" ) assert bridge.COMMENT_MARKER in b assert "āœ…" in b and "passed" in b # the card + badge are embedded as linked images at the stable /runs// URLs assert "![cc-ci result card](https://ci.example/runs/42/summary.png)" in b assert "https://ci.example/runs/42/badge.svg" in b assert "(https://drone.x/cc-ci/42)" in b # links to the run def test_result_comment_text_fallback_when_card_missing(monkeypatch): # Render failed / not served → MUST degrade to text, never a broken image (R7). monkeypatch.setattr(bridge, "artifact_available", lambda url: False) b = bridge.result_comment_body( "hedgedoc", "abc1234def", "9", "https://drone.x/cc-ci/9", "failure" ) assert "summary.png" not in b # no image embed assert "![" not in b # no markdown image at all assert "āŒ" in b and "failure" in b assert "https://drone.x/cc-ci/9" in b def test_find_existing_comment_matches_marker(monkeypatch): monkeypatch.setattr( bridge, "list_comments", lambda fn, n: [ {"id": 1, "body": "just a normal comment"}, {"id": 2, "body": bridge.COMMENT_MARKER + "\n🌻 old run"}, ], ) assert bridge.find_existing_comment("org/repo", 5) == 2 def test_find_existing_comment_none_when_absent(monkeypatch): monkeypatch.setattr(bridge, "list_comments", lambda fn, n: [{"id": 1, "body": "hello"}]) assert bridge.find_existing_comment("org/repo", 5) is None