From 491f957315a9ff066b1951476723a590769dd8d7 Mon Sep 17 00:00:00 2001 From: Polish Hammer Date: Wed, 29 Jul 2026 14:17:41 +0200 Subject: [PATCH] tests: retarget /clear_pr_pls auth tests after the musician/radio split test_musician_auth.py still probed /clear_pr_pls on the musician, but that endpoint moved to betoniarka during the split - the musician now 404s it, so all three tests failed 404 != 401/200. This was pre-existing debt, unrelated to the AI/share/bridge work; it just kept the integration job red. Split the coverage to match the current architecture: * test_musician_auth.py exercises the same auth contract (no key -> 401, key -> 200, key unset -> open) against /get_share_list, an authenticated endpoint the musician still serves, with a valid body so the permitted case is a clean 200 rather than a 400; * new test_betoniarka_auth.py covers /clear_pr_pls where it now lives, pointing PRIORITY_PLAYLIST_PATH at a tmp file so the authorised case can truncate it, and checks /ping stays open; * conftest.py adds conjurer_betoniarka to sys.path so the service imports. Verified in a clean venv (pytest flask waitress requests), matching the CI integration job: 13 passed, up from 3 failed / 6 passed. Unit suite unaffected (23 passed). Co-Authored-By: Claude Opus 4.8 --- conftest.py | 1 + tests/integration/test_betoniarka_auth.py | 41 +++++++++++++++++++++++ tests/integration/test_musician_auth.py | 25 ++++++++++---- 3 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 tests/integration/test_betoniarka_auth.py diff --git a/conftest.py b/conftest.py index e99098f..db9a4db 100644 --- a/conftest.py +++ b/conftest.py @@ -13,6 +13,7 @@ for _path in ( _ROOT, os.path.join(_ROOT, "conjurer_musician"), os.path.join(_ROOT, "conjurer_librarian"), + os.path.join(_ROOT, "conjurer_betoniarka"), ): if _path not in sys.path: sys.path.insert(0, _path) diff --git a/tests/integration/test_betoniarka_auth.py b/tests/integration/test_betoniarka_auth.py new file mode 100644 index 0000000..0130299 --- /dev/null +++ b/tests/integration/test_betoniarka_auth.py @@ -0,0 +1,41 @@ +"""Integration: betoniarka enforces the shared key on /clear_pr_pls (which +moved here from the musician during the radio split) while /ping stays open. + +This is the same auth contract the musician test used to cover for +/clear_pr_pls, now exercised against the service that actually owns it. +""" +import betoniarka as b + + +def _client(tmp_path, key="test-secret"): + b.API_KEY = key + # /clear_pr_pls truncates this file; point it at a writable temp path so the + # authorised case reaches 200 instead of failing on the default + # /srv/betoniarka/data path that does not exist in CI. + b.PRIORITY_PLAYLIST_PATH = tmp_path / "priority_queue.playlist" + return b.app.test_client() + + +def test_clear_pr_pls_rejected_without_key(tmp_path): + client = _client(tmp_path) + assert client.get("/clear_pr_pls").status_code == 401 + + +def test_clear_pr_pls_accepted_with_key(tmp_path): + client = _client(tmp_path) + resp = client.get( + "/clear_pr_pls", headers={"X-Conjurer-Api-Key": "test-secret"} + ) + assert resp.status_code == 200 + # The endpoint's job is to empty the priority playlist. + assert b.PRIORITY_PLAYLIST_PATH.read_text() == "" + + +def test_ping_is_open(tmp_path): + client = _client(tmp_path) + assert client.get("/ping").status_code == 200 + + +def test_open_when_key_unset(tmp_path): + client = _client(tmp_path, key=None) + assert client.get("/clear_pr_pls").status_code == 200 diff --git a/tests/integration/test_musician_auth.py b/tests/integration/test_musician_auth.py index 7e70401..d6c5d68 100644 --- a/tests/integration/test_musician_auth.py +++ b/tests/integration/test_musician_auth.py @@ -1,23 +1,36 @@ """Integration: the musician Flask service enforces the shared key on its authenticated endpoints while leaving the open ones reachable. + +/clear_pr_pls used to live here but moved to betoniarka during the +musician/radio split - its auth contract is now covered in +test_betoniarka_auth.py. These tests exercise the same contract against an +endpoint the musician still serves. """ import conjurer_musician as m +# An authenticated endpoint the musician still owns. The body passes the +# endpoint's own validation, so a permitted request reaches 200 rather than a +# 400 that would not distinguish auth from a bad payload. +AUTHED_ENDPOINT = "/get_share_list" +VALID_BODY = {"entries": 1, "keywords": ["conjurer"]} + def _client(key="test-secret"): m.API_KEY = key return m.app.test_client() -def test_clear_pr_pls_rejected_without_key(): +def test_authed_endpoint_rejected_without_key(): client = _client() - assert client.get("/clear_pr_pls").status_code == 401 + assert client.post(AUTHED_ENDPOINT, json=VALID_BODY).status_code == 401 -def test_clear_pr_pls_accepted_with_key(): +def test_authed_endpoint_accepted_with_key(): client = _client() - resp = client.get( - "/clear_pr_pls", headers={"X-Conjurer-Api-Key": "test-secret"} + resp = client.post( + AUTHED_ENDPOINT, + json=VALID_BODY, + headers={"X-Conjurer-Api-Key": "test-secret"}, ) assert resp.status_code == 200 @@ -31,4 +44,4 @@ def test_mp3_list_is_open(): def test_open_when_key_unset(): client = _client(key=None) - assert client.get("/clear_pr_pls").status_code == 200 + assert client.post(AUTHED_ENDPOINT, json=VALID_BODY).status_code == 200