"""Tests for the dispatch participant client and HTTP inbox.""" from __future__ import annotations import importlib import os import pytest from fastapi.testclient import TestClient from app.integrations import dispatch_client def test_build_envelope_has_required_fields(): env = dispatch_client.build_envelope("LegacyhubDocumentIndexed", "card-1", {"x": 1}, event_id="e1") assert env["message_type"] == "LegacyhubDocumentIndexed" assert env["card_uid"] == "card-1" assert env["event_id"] == "e1" assert env["participant_code"] == "legacyhub" assert "message_id" in env and "created_at" in env assert env["body"] == {"x": 1} def test_deterministic_event_id_is_stable_and_distinct(): a = dispatch_client.deterministic_event_id("LegacyhubDocumentIndexed", "asset_1") b = dispatch_client.deterministic_event_id("LegacyhubDocumentIndexed", "asset_1") c = dispatch_client.deterministic_event_id("LegacyhubDocumentIndexed", "asset_2") assert a == b assert a != c def test_publish_noop_when_disabled(monkeypatch): # Patch the settings object the module actually holds (it binds settings at # import; other tests may have reloaded app.config to a different instance). monkeypatch.setattr(dispatch_client.settings, "dispatch_enabled", False) assert dispatch_client.publish("X", "card", {}) is None class _FakeResp: def raise_for_status(self) -> None: pass class _FakeHttpx: def __init__(self) -> None: self.calls: list[dict] = [] def post(self, url, json, headers, timeout): # noqa: A002 - mirror httpx signature self.calls.append({"url": url, "json": json, "headers": headers}) return _FakeResp() def test_publish_posts_when_enabled(monkeypatch): monkeypatch.setattr(dispatch_client.settings, "dispatch_enabled", True) monkeypatch.setattr(dispatch_client.settings, "dispatch_api_url", "http://dispatch-api:47137") monkeypatch.setattr(dispatch_client.settings, "dispatch_api_key", "k") fake = _FakeHttpx() monkeypatch.setattr(dispatch_client, "httpx", fake) env = dispatch_client.publish("LegacyhubDocumentIndexed", "card-1", {"x": 1}, event_id="e1") assert env is not None assert len(fake.calls) == 1 assert fake.calls[0]["url"].endswith("/messages") assert fake.calls[0]["headers"]["X-API-Key"] == "k" def test_publish_document_indexed_uses_asset_id_as_card(monkeypatch): monkeypatch.setattr(dispatch_client.settings, "dispatch_enabled", True) monkeypatch.setattr(dispatch_client.settings, "dispatch_api_url", "http://dispatch-api:47137") fake = _FakeHttpx() monkeypatch.setattr(dispatch_client, "httpx", fake) env = dispatch_client.publish_document_indexed( "doc-uuid", asset_metadata={"asset_id": "asset_9", "owner_module": "qms"} ) assert env is not None assert env["card_uid"] == "asset_9" assert env["body"]["asset_id"] == "asset_9" assert env["body"]["owner_module"] == "qms" # ---- inbox route ---- @pytest.fixture def dispatch_app(monkeypatch): monkeypatch.setenv("INGEST_API_KEY", "svc-key") monkeypatch.delenv("API_KEY", raising=False) import app.config as cfg import app.main as main_module cfg.get_settings.cache_clear() importlib.reload(cfg) importlib.reload(main_module) yield main_module.app for _k in ("API_KEY", "INGEST_API_KEY", "AUTH_REQUIRE_IDENTITY"): os.environ.pop(_k, None) cfg.get_settings.cache_clear() importlib.reload(cfg) importlib.reload(main_module) _ENVELOPE = {"event_id": "evt-1", "message_type": "ReindexRequested", "body": {"asset_id": "a1"}} def test_inbox_rejects_without_service_key(dispatch_app): from app.config import settings client = TestClient(dispatch_app) res = client.post(f"{settings.app_api_prefix}/dispatch/inbox", json=_ENVELOPE) assert res.status_code == 401 def test_inbox_confirms_with_service_key(dispatch_app, monkeypatch): from app.api import routes_dispatch_inbox from app.config import settings monkeypatch.setattr( routes_dispatch_inbox, "handle_inbox_message", lambda env: {"status": "confirmed", "handled": True}, ) client = TestClient(dispatch_app) res = client.post( f"{settings.app_api_prefix}/dispatch/inbox", headers={"X-API-Key": "svc-key"}, json=_ENVELOPE, ) assert res.status_code == 200 assert res.json()["status"] == "confirmed" assert res.json()["handled"] is True assert res.json()["event_id"] == "evt-1"