from __future__ import annotations import json import logging def _create_chat(client, agent_id: str, node_id: str, ref: str) -> str: r = client.post( "/api/chats", json={ "agent_id": agent_id, "node_id": node_id, "source": "web", "external_chat_ref": ref, }, ) assert r.status_code == 200, r.text return r.json()["chat"]["chat_id"] def _event_messages(caplog, event: str): out = [] for rec in caplog.records: try: payload = json.loads(rec.getMessage()) except Exception: continue if payload.get("event") == event: out.append(payload) return out def test_structured_logging_send_and_replay(sofiia_client, sofiia_module, monkeypatch, caplog): def _router_url(node_id: str) -> str: return {"NODA1": "http://noda1-router.test", "NODA2": "http://noda2-router.test"}.get(node_id, "") async def _fake_infer(base_url, agent_id, text, **kwargs): return {"response": "ok-structured", "backend": "fake", "model": "fake-model"} monkeypatch.setattr(sofiia_module, "get_router_url", _router_url) monkeypatch.setattr(sofiia_module, "infer", _fake_infer) chat_id = _create_chat(sofiia_client, "sofiia", "NODA1", "log-send") headers = {"Idempotency-Key": "idem-log-1", "X-Request-Id": "req-123"} with caplog.at_level(logging.INFO, logger="sofiia"): r1 = sofiia_client.post(f"/api/chats/{chat_id}/send", json={"text": "hello"}, headers=headers) r2 = sofiia_client.post(f"/api/chats/{chat_id}/send", json={"text": "hello"}, headers=headers) assert r1.status_code == 200, r1.text assert r2.status_code == 200, r2.text assert r2.json().get("idempotency", {}).get("replayed") is True send_events = _event_messages(caplog, "chat.send") replay_events = _event_messages(caplog, "chat.send.replay") ok_events = _event_messages(caplog, "chat.send.ok") assert send_events, "Expected chat.send structured log" assert replay_events, "Expected chat.send.replay structured log" assert ok_events, "Expected chat.send.ok structured log" first_send = send_events[0] assert first_send["chat_id"] == chat_id assert first_send["node_id"] == "NODA1" assert first_send["event"] == "chat.send" assert first_send["request_id"] == "req-123" assert "idempotency_key_hash" in first_send replay = replay_events[0] assert replay["chat_id"] == chat_id assert replay["status"] == "ok" assert replay["replayed"] is True def test_structured_logging_pagination_events(sofiia_client, caplog): with caplog.at_level(logging.INFO, logger="sofiia"): r = sofiia_client.get("/api/chats?nodes=NODA1,NODA2&limit=5") assert r.status_code == 200, r.text list_events = _event_messages(caplog, "chat.list") assert list_events, "Expected chat.list structured log" entry = list_events[0] assert entry["event"] == "chat.list" assert entry["limit"] == 5 assert "cursor_present" in entry assert "has_more" in entry assert entry["status"] == "ok"