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