"""Test: chat RESUME (no re-pick scenario) + decision detection for real-LLM text.""" import os, sys, tempfile, warnings from pathlib import Path warnings.filterwarnings("ignore") BACKEND = str(Path(__file__).resolve().parents[1]) sys.path.insert(0, BACKEND) from app.factory import create_app from app.config import Config td = tempfile.mkdtemp(); Config.DATA_DIR = Path(td) sys.path.insert(0, BACKEND + "/scripts") from mock_llm import MockLLM app = create_app(); app.extensions["llm"] = MockLLM() C = app.test_client() def tok(u,p): return C.post("/api/auth/login", json={"username":u,"password":p}).get_json()["token"] AT = tok("admin","1234"); AH={"Authorization":f"Bearer {AT}"} C.post("/api/auth/setup", headers=AH, json={"username":"admin","email":"a@b.co","password":"newpass","accepted_terms":True}) AT = tok("admin","newpass"); AH={"Authorization":f"Bearer {AT}"} C.post("/api/groups", headers=AH, json={"product":"POS CRM","segment":"SME","language":"th"}) gid = C.get("/api/groups", headers=AH).get_json()["groups"][0]["id"] C.post(f"/api/groups/{gid}/analyze", headers=AH) pid = C.get(f"/api/groups/{gid}/personas", headers=AH).get_json()["personas"][0]["id"] # 1. start chat r1 = C.post(f"/api/chat/{gid}/personas/{pid}/chat/start", headers=AH, json={"scenario":"social","locale":"th"}) assert r1.status_code == 200, r1.get_json() sid1 = r1.get_json()["session"]["id"] # 2. call start AGAIN (as if re-entering) -> should RESUME the same session, not create new r2 = C.post(f"/api/chat/{gid}/personas/{pid}/chat/start", headers=AH, json={"scenario":"f2f_call","locale":"th"}) assert r2.status_code == 200, r2.get_json() sid2 = r2.get_json()["session"]["id"] assert sid1 == sid2, f"resume must return same session (got {sid2}, want {sid1})" assert r2.get_json()["scenario"] == "social", "resume keeps ORIGINAL scenario (not re-pick)" print("[ok] resume returns same active session + keeps original scenario") # 3. resume endpoint also returns the active session rr = C.get(f"/api/chat/{gid}/personas/{pid}/chat/resume", headers=AH) assert rr.status_code == 200 and rr.get_json()["session"]["id"] == sid1 print("[ok] /chat/resume returns active session") # 4. decision comes from the LLM judge (evaluate_turn), NOT a fixed-text list. # The mock judge returns {mood, decision: buy, ...} for the eval prompt. from app.services.simulator import Simulator sim2 = Simulator(MockLLM()) res = sim2.evaluate_turn( persona={"name": "สมชาย", "pains": [], "tolerance": 2}, messages=[ {"role": "customer", "text": "สวัสดีครับ"}, {"role": "seller", "text": "สวัสดีครับ มีอะไรช่วยได้ไหม"}, {"role": "customer", "text": "แพงเกินไป ผมซื้อไม่ไหวแล้วครับ ขอตัวก่อน"}, ], ) print("[ok] evaluate_turn decision:", res.get("decision"), "| mood:", res.get("mood")) assert res.get("decision") in ("buy", "walk", "pending"), res # The decision is produced by the LLM judge object structure (has the fields we consume in send) assert isinstance(res, dict) and "mood" in res and "score_delta" in res and "reason" in res print("[ok] evaluate_turn returns mood/decision/score_delta/reason (context-based, not fixed text)") print("ALL RESUME+DECISION TESTS PASSED")