"""Question/engine binding across actual HTTP/MCP boundaries, without an LLM.""" import asyncio import json import os import sys import pytest from test_claude_packages import backend, ROOT from packages.claude import client QUESTIONS = [ "Čomu sa venuje diplomová práca Jána Holpa? Stručne ju predstav a uveď zdroj.", "Porovnaj zadanie a výsledky Ondreja Megelu a Jána Holpa.\nPlán nie je výsledok; uveď zdroje.", 'Ktorí študenti majú tag "RAG" a zároveň diplomový projekt z roku 2026?', ] @pytest.mark.parametrize('engine',['rag','graphrag']) def test_context_token_crosses_real_http_without_model_rewriting(backend,engine): result=client.query_backend('Daj obe.',engine,context_token='opaque-context') assert result['accepted_for_delivery'] and result['query']=='Daj obe.' body=backend['requests'][-1][1] assert body['query']=='Daj obe.' and body['context_token']=='opaque-context' @pytest.mark.parametrize("engine", ["rag", "graphrag"]) def test_latest_question_and_all_conditions_cross_http_unchanged(backend, engine): for question in QUESTIONS: r = client.query_backend(question, engine, limit=7) assert r["accepted_for_delivery"] and r["query"] == question and r["engine"] == engine path, body, _ = backend["requests"][-1] assert ( path == "/answer" and body["query"] == question and body["engine"] == engine and body["limit"] == 7 ) assert len(backend["requests"]) == len(QUESTIONS) @pytest.mark.parametrize("engine", ["rag", "graphrag"]) def test_only_a_matching_leading_command_is_removed(backend, engine): question = 'Aké práce má Ján Holp v roku 2020? Zachovaj výraz "RAG".' r = client.query_backend(" /" + engine.upper() + " " + question + " ", engine) assert r["accepted_for_delivery"] and r["query"] == question assert backend["requests"][-1][1]["query"] == question other = "graphrag" if engine == "rag" else "rag" r = client.query_backend("/" + other + " " + question, engine) assert r["reason"] == "engine_mismatch" and not r["accepted_for_delivery"] assert len(backend["requests"]) == 1 @pytest.mark.parametrize("question", ["/rag", "/rag ", "/graphrag"]) def test_command_without_a_question_never_reaches_backend(backend, question): engine = "graphrag" if question.startswith("/graphrag") else "rag" assert client.query_backend(question, engine)["reason"] == "invalid_query" assert not backend["requests"] @pytest.mark.parametrize( "change", [ {"query": "A previous unrelated question", "engine": "rag"}, {"query": QUESTIONS[0], "engine": "graphrag"}, ], ) def test_stale_question_or_wrong_engine_cannot_deliver_another_answer(backend, change): backend["raw"] = json.dumps( {**backend["checked"], "answer": "OTHER_ANSWER_SHOULD_NOT_ESCAPE", **change} ).encode() r = client.query_backend(QUESTIONS[0], "rag") assert r["reason"] == "question_mismatch" and not r["accepted_for_delivery"] assert "OTHER_ANSWER" not in json.dumps(r) def test_missing_question_echo_is_not_accepted(backend): backend["raw"] = json.dumps({**backend["checked"], "engine": "rag"}).encode() r = client.query_backend(QUESTIONS[0], "rag") assert r["reason"] == "invalid_backend_response" and not r["accepted_for_delivery"] @pytest.mark.parametrize('engine',['rag','graphrag']) def test_safe_request_bound_diagnostic_survives_http_and_presentation(backend,engine): from tools.answers.delivery_contract import fingerprint q='Aké práce má Eva Nová?' backend['checked']['delivery_check']={'layer':'delivery','code':'partial','request_fingerprint':fingerprint(q,engine)} result=client.answer_for_presentation(client.query_backend(q,engine)) assert result['delivery_check']==backend['checked']['delivery_check'] @pytest.mark.parametrize('change',[ {'layer':'SECRET PROVIDER KEY'},{'code':'PRIVATE raw model response'}, {'request_fingerprint':'0'*64},{'extra':'PRIVATE error trace'}]) def test_corrupt_or_unsafe_diagnostic_is_never_published(backend,change): from tools.answers.delivery_contract import fingerprint q='Aké práce má Eva Nová?' backend['checked']['delivery_check']={'layer':'delivery','code':'complete','request_fingerprint':fingerprint(q,'rag'),**change} result=client.query_backend(q,'rag') assert not result['accepted_for_delivery'] and 'PRIVATE' not in json.dumps(result) and 'SECRET' not in json.dumps(result) def test_consecutive_mcp_calls_do_not_reuse_previous_queries(backend): from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client from packages.claude.mcp_server import QUESTION_POLICY async def run(): params = StdioServerParameters( command=sys.executable, args=[str(ROOT / "packages/claude/mcp_server.py")], env={**os.environ, "PYTHONDONTWRITEBYTECODE": "1"}, ) async with stdio_client(params) as (read, write): async with ClientSession(read, write) as session: initialized = await session.initialize() assert QUESTION_POLICY in initialized.instructions for engine, question in [ ("rag", QUESTIONS[0]), ("graphrag", QUESTIONS[2]), ("rag", QUESTIONS[1]), ]: result = await session.call_tool("zpwiki_" + engine, {"query": question}) assert not result.isError and result.structuredContent["query"] == question assert backend["requests"][-1][1]["query"] == question assert backend["requests"][-1][1]["engine"] == engine asyncio.run(run())