tests: retarget /clear_pr_pls auth tests after the musician/radio split
CI / compile (pull_request) Successful in 11s
CI / unit (pull_request) Successful in 12s
CI / integration (pull_request) Successful in 11s
build / build (push) Failing after 7s
CI / compile (push) Successful in 10s
CI / unit (push) Successful in 14s
CI / integration (push) Successful in 12s
CI / compile (pull_request) Successful in 11s
CI / unit (pull_request) Successful in 12s
CI / integration (pull_request) Successful in 11s
build / build (push) Failing after 7s
CI / compile (push) Successful in 10s
CI / unit (push) Successful in 14s
CI / integration (push) Successful in 12s
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 <noreply@anthropic.com>
This commit was merged in pull request #3.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user