Masters-four-Tab-OpenAI / backend /app /test_knowledgebase_api.py
Pete Dunn
router lifecycle inventory normalization
eb20a73
Raw
History Blame Contribute Delete
15.5 kB
from __future__ import annotations
import sys
import time
from pathlib import Path
from fastapi.testclient import TestClient
BACKEND_DIR = Path(__file__).resolve().parents[1]
if str(BACKEND_DIR) not in sys.path:
sys.path.insert(0, str(BACKEND_DIR))
from app import main as main_mod
class StubUnifiedCore:
def normalize_query(self, message: str, mode: str = "auto"): # noqa: ANN001
raw = str(message or "")
normalized = raw.replace("repalcement", "replacement")
return {
"original_message": raw,
"normalized_message": normalized,
"changed": raw != normalized,
"corrections": [{"from": "repalcement", "to": "replacement"}] if raw != normalized else [],
}
def handle_message(self, message: str, state: dict, mode: str = "auto", audience: str = "auto", show_citations: bool = True): # noqa: ANN001
return {
"assistant": "**Result**\n\nUnified stub answer.",
"state": {"mode": mode, "pending": {}},
"prompt_version": "unified-1.0",
"sources": [
{
"id": "S1",
"domain": "router_docs",
"doc": "XR60-Datasheet.pdf",
"relative_path": "/router_rag_files/01_documents/XR60-Datasheet.pdf",
"chunk_id": "chunk-1",
"excerpt": "XR60 spec excerpt",
}
]
if show_citations
else [],
"files": ["/router_rag_files/01_documents/XR60-Datasheet.pdf"],
"effective_audience": "external",
"meta": {"domain": "router_docs", "domain_label": "Router docs/specs"},
}
def list_files(self): # noqa: ANN201
return {"router_docs": ["01_documents/XR60-Datasheet.pdf"]}
def health(self): # noqa: ANN201
return {"ok": True, "domains": {"router_docs": {"ok": True}}}
def record_router_workbook_feedback( # noqa: ANN001, ANN201
self,
*,
request_id: str,
verdict: str,
detail: str = "",
meta: dict | None = None,
):
return {
"ok": True,
"feedback": {
"request_id": request_id,
"verdict": verdict,
"detail": detail,
"retrieval_mode": str((meta or {}).get("retrieval_mode") or ""),
},
}
def handle_router_inventory_import( # noqa: ANN001
self,
*,
file_bytes: bytes | None,
filename: str,
pasted_text: str,
mode: str = "router_lifecycle",
audience: str = "auto",
show_citations: bool = True,
):
return {
"assistant": "**Result**\n\nWorkbook-backed inventory import analyzed 2 row(s).",
"state": {},
"prompt_version": "unified-1.0",
"sources": [],
"files": ["router_workbook.xlsx"],
"effective_audience": "external",
"meta": {
"domain": "router_lifecycle",
"domain_label": "Router lifecycle",
"retrieval_mode": "deterministic_router_workbook_inventory_import",
"router_fleet_view": {
"title": "Workbook fleet normalization",
"subtitle": "Per-row workbook normalization, lifecycle, and replacement confidence.",
"source_label": "Uploaded inventory",
"uploaded_filename": filename or "fleet.csv",
"row_count": 2,
"matched_count": 1,
"unmatched_count": 1,
"review_required_count": 1,
"shown_row_count": 2,
"truncated_count": 0,
"rows": [],
},
},
}
def test_knowledgebase_message_endpoint_happy_path(monkeypatch) -> None:
monkeypatch.setattr(main_mod, "_get_knowledgebase_core", lambda: StubUnifiedCore())
with TestClient(main_mod.app) as client:
res = client.post(
"/api/knowledgebase/message",
json={
"message": "Compare RV50X vs XR60 from docs only",
"state": {},
"mode": "router_docs",
"audience": "auto",
"show_citations": True,
"request_id": "kb-req-123",
},
headers={"x-request-id": "kb-req-header"},
)
assert res.status_code == 200
body = res.json()
assert "Unified stub answer" in str(body.get("assistant") or "")
assert body.get("meta", {}).get("domain") == "router_docs"
assert body.get("meta", {}).get("request_id") == "kb-req-header"
assert res.headers.get("x-request-id") == "kb-req-header"
assert isinstance(body.get("suggested_replies"), list)
assert isinstance(body.get("response_shell"), list)
def test_knowledgebase_tabs_default_to_unified() -> None:
with TestClient(main_mod.app) as client:
res = client.get("/api/ui/tabs")
assert res.status_code == 200
body = res.json()
assert body.get("knowledgebase") is True
assert body.get("pots_pricing_lite") is False
def test_knowledgebase_tabs_expose_pots_pricing_lite_with_estimator_fallback(monkeypatch) -> None:
monkeypatch.setenv("MASTERS_TOOLKIT_TAB_POTS_SAVINGS_ESTIMATOR_ENABLED", "true")
with TestClient(main_mod.app) as client:
res = client.get("/api/ui/tabs")
assert res.status_code == 200
body = res.json()
assert body.get("pots_estimator") is True
assert body.get("pots_pricing_lite") is True
def test_knowledgebase_message_hard_timeout_returns_guidance(monkeypatch) -> None:
class SlowUnifiedCore:
def handle_message(self, message: str, state: dict, mode: str = "auto", audience: str = "auto", show_citations: bool = True): # noqa: ANN001
time.sleep(0.05)
return {
"assistant": "late answer",
"state": {},
"sources": [],
"files": [],
"meta": {"domain": "knowledgebase"},
}
monkeypatch.setattr(main_mod, "_get_knowledgebase_core", lambda: SlowUnifiedCore())
monkeypatch.setenv("MASTERS_TOOLKIT_CHAT_HARD_TIMEOUT_S", "0.01")
monkeypatch.setenv("UNIFIED_KB_FALLBACK_EXTRA_BUDGET_S", "0")
with TestClient(main_mod.app) as client:
res = client.post(
"/api/knowledgebase/message",
json={
"message": "very long synthesis request",
"state": {"mode": "auto"},
"mode": "auto",
"audience": "auto",
"show_citations": True,
},
)
assert res.status_code == 200
body = res.json()
assert "paused this response" in str(body.get("assistant") or "").lower()
assert body.get("meta", {}).get("retrieval_mode") == "hard_timeout_guidance"
assert body.get("state", {}).get("pending", {}).get("type") == "clarify_speed"
def test_knowledgebase_normalize_endpoint(monkeypatch) -> None:
monkeypatch.setattr(main_mod, "_get_knowledgebase_core", lambda: StubUnifiedCore())
with TestClient(main_mod.app) as client:
res = client.post(
"/api/knowledgebase/normalize",
json={"message": "need repalcement options", "mode": "auto"},
)
assert res.status_code == 200
body = res.json()
assert body.get("changed") is True
assert "replacement" in str(body.get("normalized_message") or "")
def test_router_lifecycle_normalize_chat_message_preserves_distinct_customer_names() -> None:
main_mod._get_knowledgebase_core.cache_clear()
msg = "McDonalds 24 AER2200\nMcDonald's 12 MG52\nMcDonald 49 XR60"
normalized, meta = main_mod._normalize_chat_message(msg, mode_hint="router_lifecycle")
assert normalized == msg
assert meta.get("changed") is False
assert meta.get("corrections") == []
def test_router_lifecycle_normalize_chat_message_skips_instruction_wrapped_inventory_spellcheck() -> None:
main_mod._get_knowledgebase_core.cache_clear()
msg = (
"Normalize this list and give row-by-row lifecycle plus replacements: "
"McDonalds 24 AER2200, McDonald's 12 MG52, McDonald 49 XR60"
)
normalized, meta = main_mod._normalize_chat_message(msg, mode_hint="router_lifecycle")
assert normalized == msg
assert meta.get("changed") is False
assert meta.get("corrections") == []
def test_knowledgebase_control_reset_endpoint(monkeypatch) -> None:
monkeypatch.setattr(main_mod, "_get_knowledgebase_core", lambda: StubUnifiedCore())
with TestClient(main_mod.app) as client:
res = client.post(
"/api/knowledgebase/control",
json={"action": "reset", "state": {"pending": {"type": "x"}}, "mode": "auto"},
headers={"x-request-id": "kb-control-1"},
)
assert res.status_code == 200
body = res.json()
assert "Unified stub answer" in str(body.get("assistant") or "")
assert body.get("meta", {}).get("request_id") == "kb-control-1"
assert body.get("meta", {}).get("control_action") == "reset"
assert res.headers.get("x-request-id") == "kb-control-1"
def test_knowledgebase_router_inventory_import_endpoint(monkeypatch) -> None:
monkeypatch.setattr(main_mod, "_get_knowledgebase_core", lambda: StubUnifiedCore())
with TestClient(main_mod.app) as client:
res = client.post(
"/api/knowledgebase/router_workbook/inventory/import",
files={"file": ("fleet.csv", b"Customer,Model,Quantity\nDarden,Current-500,5\n", "text/csv")},
data={
"mode": "router_lifecycle",
"audience": "auto",
"show_citations": "true",
},
)
assert res.status_code == 200
body = res.json()
assert "Workbook-backed inventory import analyzed" in str(body.get("assistant") or "")
assert body.get("meta", {}).get("retrieval_mode") == "deterministic_router_workbook_inventory_import"
assert body.get("meta", {}).get("router_fleet_view", {}).get("uploaded_filename") == "fleet.csv"
def test_knowledgebase_router_workbook_feedback_endpoint(monkeypatch) -> None:
monkeypatch.setattr(main_mod, "_get_knowledgebase_core", lambda: StubUnifiedCore())
with TestClient(main_mod.app) as client:
res = client.post(
"/api/knowledgebase/router_workbook/feedback",
json={
"request_id": "kb-req-123",
"verdict": "needs_followup",
"detail": "This answer still needs a stronger compare explanation.",
"meta": {
"retrieval_mode": "deterministic_router_workbook_compare",
"router_intelligence_intent": "compare",
},
},
)
assert res.status_code == 200
body = res.json()
assert body.get("ok") is True
assert body.get("feedback", {}).get("request_id") == "kb-req-123"
assert body.get("feedback", {}).get("verdict") == "needs_followup"
def test_knowledgebase_telemetry_tracks_control_and_messages(monkeypatch) -> None:
monkeypatch.setattr(main_mod, "_get_knowledgebase_core", lambda: StubUnifiedCore())
with TestClient(main_mod.app) as client:
msg = client.post(
"/api/knowledgebase/message",
json={"message": "hello", "state": {}, "mode": "auto", "request_id": "telemetry-msg"},
)
ctl = client.post(
"/api/knowledgebase/control",
json={"action": "reset", "state": {}, "mode": "auto", "request_id": "telemetry-ctl"},
)
tel = client.get("/api/knowledgebase/telemetry")
assert msg.status_code == 200
assert ctl.status_code == 200
assert tel.status_code == 200
body = tel.json()
assert int(body.get("message_count") or 0) >= 1
assert int(body.get("control_count") or 0) >= 1
def test_get_knowledgebase_core_wires_rapid_router_catalog_provider(monkeypatch) -> None:
import app.knowledgebase.core as kb_core_mod
captured: dict = {}
class FakeUnifiedCore:
def __init__(self, **kwargs): # noqa: ANN003
captured.update(kwargs)
class StubRapidRouterCore:
def get_store_for_client(self): # noqa: ANN201
return {"config": {}, "products": [{"id": "stub_router"}]}
def get_catalog_status(self): # noqa: ANN201
return {"loaded": True, "product_count": 1, "latest_import": {"filename": "stub_router_catalog.xlsx"}}
monkeypatch.setattr(main_mod, "_get_router_rag_core", lambda: object())
monkeypatch.setattr(main_mod, "_get_router_core", lambda: object())
monkeypatch.setattr(main_mod, "_get_masters_core", lambda: object())
monkeypatch.setattr(main_mod, "_get_pots_core", lambda: object())
monkeypatch.setattr(main_mod, "_get_rapid_router_core", lambda: StubRapidRouterCore())
monkeypatch.setattr(kb_core_mod, "UnifiedKnowledgebaseCore", FakeUnifiedCore)
main_mod._get_knowledgebase_core.cache_clear()
try:
core = main_mod._get_knowledgebase_core()
assert isinstance(core, FakeUnifiedCore)
provider = captured.get("rapid_router_catalog_provider")
assert callable(provider)
data = provider()
assert isinstance(data, dict)
assert data.get("products", [{}])[0].get("id") == "stub_router"
workbook_provider = captured.get("rapid_router_intelligence_provider")
assert callable(workbook_provider)
workbook_core = workbook_provider()
assert isinstance(workbook_core, StubRapidRouterCore)
assert workbook_core.get_catalog_status().get("loaded") is True
finally:
main_mod._get_knowledgebase_core.cache_clear()
def test_maybe_bootstrap_rapid_router_catalog_loads_detected_workbook(tmp_path, monkeypatch) -> None:
workbook_path = tmp_path / "device_master_source_of_truth_v26_site_survey_integrated_export.xlsx"
workbook_path.write_bytes(b"fake-workbook")
loaded: dict[str, object] = {}
class StubRapidRouterCore:
def get_catalog_status(self): # noqa: ANN201
return {"loaded": False}
def load_catalog_workbook(self, *, workbook_bytes: bytes, filename: str): # noqa: ANN201
loaded["bytes"] = workbook_bytes
loaded["filename"] = filename
return {"ok": True}
monkeypatch.setattr(
main_mod,
"_find_rapid_router_catalog_workbook_candidate",
lambda: {
"found": True,
"path": workbook_path,
"filename": workbook_path.name,
"searched": True,
"searched_roots": [str(workbook_path.parent)],
},
)
main_mod._set_rapid_router_catalog_bootstrap_status(
found=False,
path="",
filename="",
searched=False,
load_attempted=False,
load_succeeded=False,
already_loaded=False,
error="",
)
main_mod._maybe_bootstrap_rapid_router_catalog(StubRapidRouterCore())
assert loaded["bytes"] == b"fake-workbook"
assert loaded["filename"] == workbook_path.name
status = main_mod._rapid_router_catalog_bootstrap_status()
assert status["found"] is True
assert status["path"] == str(workbook_path)
assert status["load_attempted"] is True
assert status["load_succeeded"] is True