"""Integration: the librarian's health/liveness surface. Two behaviours, both proven against the real Flask app: * /ping is busy-aware - while a search is grinding it pongs back immediately WITHOUT queueing (busy is healthy); when idle it routes the ping through the internal queue for the worker to answer. * /query_status reports whether a uuid is still known (queued/processing), which is what the bot's per-query watchdog polls to catch a lost result. Only the SYNC routes (/ping, /query_status) are exercised - the async /query route needs flask[async], which the integration job doesn't install, so query state is seeded directly on the module. """ import sys import types # conjurer_librarian does `from habanero import Crossref` at import time and # habanero isn't installed in the integration job. Stub it before importing the # service (we never build a real Librarian here, so Crossref is just a name). if "habanero" not in sys.modules: _habanero = types.ModuleType("habanero") _habanero.Crossref = object sys.modules["habanero"] = _habanero import conjurer_librarian as lib # noqa: E402 def _client(key=None): lib.API_KEY = key return lib.app.test_client() def _reset(): with lib._active_lock: lib.active_queries.clear() lib.worker_busy.clear() while not lib.librarian_queue.empty(): lib.librarian_queue.get() def test_query_status_known_vs_unknown(): _reset() client = _client() with lib._active_lock: lib.active_queries["abc"] = "queued" known = client.post("/query_status", json={"UUID": "abc"}).get_json()["data"] assert known == {"uuid": "abc", "known": True, "state": "queued"} unknown = client.post("/query_status", json={"UUID": "nope"}).get_json()["data"] assert unknown == {"uuid": "nope", "known": False, "state": "unknown"} def test_ping_idle_routes_through_internal_queue(monkeypatch): _reset() posted = [] monkeypatch.setattr(lib.requests, "post", lambda *a, **k: posted.append((a, k))) client = _client() resp = client.post("/ping", json={"UUID": "ping-idle", "callback": "http://bot-a:5000"}) assert resp.status_code == 200 # Idle => it went onto the internal queue for the worker (carrying the # callback so the worker pongs the right bot), NOT posted directly. assert posted == [] assert lib.librarian_queue.get_nowait() == { "__ping__": "ping-idle", "callback": "http://bot-a:5000", } def test_ping_while_busy_pongs_directly_to_the_pinging_bot(monkeypatch): _reset() lib.worker_busy.set() # a search is grinding posted = [] class _Resp: status_code = 200 def fake_post(url, json=None, headers=None, timeout=None): posted.append({"url": url, "json": json}) return _Resp() monkeypatch.setattr(lib.requests, "post", fake_post) client = _client() resp = client.post("/ping", json={"UUID": "ping-busy", "callback": "http://bot-b:5000"}) assert resp.status_code == 200 # Busy => direct pong, NOTHING queued, and it goes to the CALLBACK bot (not # the static default) so a shared librarian health-checks each bot correctly. assert lib.librarian_queue.empty() assert len(posted) == 1 assert posted[0]["json"] == {"__pong__": "ping-busy"} assert posted[0]["url"] == "http://bot-b:5000" + lib.SEND_RESULTS def test_ping_without_callback_pongs_to_default_bot(monkeypatch): _reset() lib.worker_busy.set() posted = [] class _Resp: status_code = 200 monkeypatch.setattr( lib.requests, "post", lambda url, **k: posted.append(url) or _Resp(), ) client = _client() client.post("/ping", json={"UUID": "ping-nocb"}) # no callback assert posted == [lib.MAIN_BOT_ADDRESS + lib.SEND_RESULTS] def test_query_status_enforces_api_key(): _reset() client = _client(key="secret") denied = client.post("/query_status", json={"UUID": "x"}) assert denied.status_code == 401 ok = client.post( "/query_status", json={"UUID": "x"}, headers={"X-Conjurer-Api-Key": "secret"} ) assert ok.status_code == 200