from __future__ import annotations 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 test_send_idempotency_header_replays_same_message_id(sofiia_client, sofiia_module, monkeypatch): async def _fake_infer(base_url, agent_id, text, **kwargs): return {"response": f"ok:{agent_id}:{text}", "backend": "fake", "model": "fake-model"} monkeypatch.setattr(sofiia_module, "infer", _fake_infer) chat_id = _create_chat(sofiia_client, "sofiia", "NODA2", "idem-header") headers = {"Idempotency-Key": "idem-header-1"} r1 = sofiia_client.post(f"/api/chats/{chat_id}/send", json={"text": "ping"}, headers=headers) r2 = sofiia_client.post(f"/api/chats/{chat_id}/send", json={"text": "ping"}, headers=headers) assert r1.status_code == 200 and r2.status_code == 200 j1, j2 = r1.json(), r2.json() assert j1["message"]["message_id"] == j2["message"]["message_id"] assert j1["idempotency"]["replayed"] is False assert j2["idempotency"]["replayed"] is True def test_send_idempotency_body_replays_same_message_id(sofiia_client, sofiia_module, monkeypatch): async def _fake_infer(base_url, agent_id, text, **kwargs): return {"response": f"ok:{agent_id}:{text}", "backend": "fake", "model": "fake-model"} monkeypatch.setattr(sofiia_module, "infer", _fake_infer) chat_id = _create_chat(sofiia_client, "sofiia", "NODA2", "idem-body") payload = {"text": "ping", "idempotency_key": "idem-body-1"} r1 = sofiia_client.post(f"/api/chats/{chat_id}/send", json=payload) r2 = sofiia_client.post(f"/api/chats/{chat_id}/send", json=payload) assert r1.status_code == 200 and r2.status_code == 200 j1, j2 = r1.json(), r2.json() assert j1["message"]["message_id"] == j2["message"]["message_id"] assert j1["idempotency"]["replayed"] is False assert j2["idempotency"]["replayed"] is True def test_send_idempotency_header_overrides_body(sofiia_client, sofiia_module, monkeypatch): async def _fake_infer(base_url, agent_id, text, **kwargs): return {"response": f"ok:{agent_id}:{text}", "backend": "fake", "model": "fake-model"} monkeypatch.setattr(sofiia_module, "infer", _fake_infer) chat_id = _create_chat(sofiia_client, "sofiia", "NODA2", "idem-override") r1 = sofiia_client.post( f"/api/chats/{chat_id}/send", json={"text": "ping-a", "idempotency_key": "body-a"}, headers={"Idempotency-Key": "header-wins"}, ) r2 = sofiia_client.post( f"/api/chats/{chat_id}/send", json={"text": "ping-b", "idempotency_key": "body-b"}, headers={"Idempotency-Key": "header-wins"}, ) assert r1.status_code == 200 and r2.status_code == 200 j1, j2 = r1.json(), r2.json() assert j1["message"]["message_id"] == j2["message"]["message_id"] assert j2["idempotency"]["replayed"] is True assert j2["idempotency"]["key"] == "header-wins" def test_send_idempotency_different_keys_create_different_message_ids(sofiia_client, sofiia_module, monkeypatch): async def _fake_infer(base_url, agent_id, text, **kwargs): return {"response": f"ok:{agent_id}:{text}", "backend": "fake", "model": "fake-model"} monkeypatch.setattr(sofiia_module, "infer", _fake_infer) chat_id = _create_chat(sofiia_client, "sofiia", "NODA2", "idem-different") r1 = sofiia_client.post( f"/api/chats/{chat_id}/send", json={"text": "ping", "idempotency_key": "k1"}, ) r2 = sofiia_client.post( f"/api/chats/{chat_id}/send", json={"text": "ping", "idempotency_key": "k2"}, ) assert r1.status_code == 200 and r2.status_code == 200 assert r1.json()["message"]["message_id"] != r2.json()["message"]["message_id"]