diff --git a/tests/scripts_test_retriever_smoke.py b/tests/scripts_test_retriever_smoke.py new file mode 100644 index 0000000..d13611b --- /dev/null +++ b/tests/scripts_test_retriever_smoke.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +scripts/test_retriever_smoke.py - einfacher Smoke-Test für den mindnet-Retriever + +Zweck: + - Startet eine Query gegen die laufende FastAPI-Instanz + - Prüft, ob der /query-Endpunkt antwortet und sinnvolle Treffer liefert + - Gibt Top-K-Ergebnisse inkl. Scores auf stdout aus + +Verwendung: + 1) Stelle sicher, dass der mindnet-Service läuft, z. B.: + uvicorn app.main:create_app --factory --reload + + 2) Führe dieses Skript aus: + python scripts/test_retriever_smoke.py --query "karate" --mode hybrid + + 3) Optional: + python scripts/test_retriever_smoke.py --query "embeddings" --mode semantic --top-k 5 + +Hinweis: + - Standard-URL ist http://127.0.0.1:8000 + - Endpoint ist /query (bestehender Router), der QueryRequest erwartet +""" + +from __future__ import annotations + +import argparse +import json +import sys +from typing import Any, Dict + +import requests + + +def _build_payload(args: argparse.Namespace) -> Dict[str, Any]: + payload: Dict[str, Any] = { + "mode": args.mode, + "query": args.query, + "top_k": args.top_k, + } + if args.expand_depth is not None and args.expand_depth > 0: + expand: Dict[str, Any] = {"depth": args.expand_depth} + if args.expand_edge_types: + expand["edge_types"] = args.expand_edge_types + payload["expand"] = expand + return payload + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description="Smoke-Test für mindnet-Retriever (/query)") + parser.add_argument("--url", default="http://127.0.0.1:8000/query", help="Basis-URL des Query-Endpunkts") + parser.add_argument("--query", required=True, help="Text-Query für den Retriever") + parser.add_argument("--mode", choices=["semantic", "hybrid"], default="hybrid", help="Retrieval-Modus") + parser.add_argument("--top-k", type=int, default=5, help="Anzahl der gewünschten Treffer") + parser.add_argument("--expand-depth", type=int, default=0, help="Edge-Expansionstiefe (nur hybrid)") + parser.add_argument( + "--expand-edge-types", + nargs="*", + default=None, + help="Liste von Edge-Typen für die Expansion (z. B. references depends_on)", + ) + + args = parser.parse_args(argv) + + payload = _build_payload(args) + + try: + resp = requests.post(args.url, json=payload, timeout=10) + except Exception as exc: # pragma: no cover - Netzwerkfehler + print(f"[ERROR] HTTP-Request fehlgeschlagen: {exc}", file=sys.stderr) + return 1 + + if resp.status_code != 200: + print(f"[ERROR] Unerwarteter Statuscode: {resp.status_code}", file=sys.stderr) + print(resp.text, file=sys.stderr) + return 1 + + try: + data = resp.json() + except Exception as exc: # pragma: no cover - JSON-Fehler + print(f"[ERROR] Antwort ist kein gültiges JSON: {exc}", file=sys.stderr) + print(resp.text, file=sys.stderr) + return 1 + + print("=== mindnet Retriever Smoke-Test ===") + print(f"URL: {args.url}") + print(f"Mode: {args.mode}") + print(f"Query: {args.query!r}") + print(f"Top-K: {args.top_k}") + print() + + results = data.get("results") or [] + used_mode = data.get("used_mode") + latency_ms = data.get("latency_ms") + + print(f"used_mode = {used_mode}, latency = {latency_ms} ms") + print(f"Treffer insgesamt: {len(results)}") + print() + + for i, hit in enumerate(results, start=1): + note_id = hit.get("note_id") + node_id = hit.get("node_id") + total = hit.get("total_score") + sem = hit.get("semantic_score") + edge_bonus = hit.get("edge_bonus") + cent_bonus = hit.get("centrality_bonus") + src = hit.get("source") or {} + path = src.get("path") + section = src.get("section") + + print(f"[{i}] note_id={note_id} node_id={node_id}") + print(f" total={total:.4f} (semantic={sem:.4f} edge={edge_bonus:.4f} centrality={cent_bonus:.4f})") + if path or section: + print(f" source: {path or '-'} :: {section or '-'}") + print() + + return 0 + + +if __name__ == "__main__": # pragma: no cover + raise SystemExit(main())