Files
sales-trainer/backend/scripts/test_scenario.py
Macky fd2cae9e62 refactor(scenario): only 2 scenarios (social / face-to-face); 're-contact' becomes a persona trait
- Scenario picker now has only social + f2f_call (removed recontact) in backend
  _scenarios, start_session validation, and Chat.vue picker.
- 'ลูกค้ากลับมาติดต่อ' is no longer a scenario: it's now a PERSONA trait. Persona prompt
  generates ~1-in-4 personas with recontact=true (asked before, now returns warmer/ready);
  store shape gets recontact field; simulator injects a re-contact note into the persona's
  system prompt so it plays as a returning customer naturally.
- test_scenario updated: unknown scenario defaults to social; recontact shown as a
  generated persona trait.
All 9 backend suites pass. Rebuilt dist.
2026-08-09 11:21:34 +07:00

85 lines
3.5 KiB
Python

"""Test: scenario-based chat start (scenario determines who opens)."""
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)
from app import factory
def make_app():
app = create_app()
# mock LLM so analyze works deterministically
sys.path.insert(0, BACKEND + "/scripts")
from mock_llm import MockLLM
app.extensions["llm"] = MockLLM()
return app
app = make_app()
C = app.test_client()
def login(u, p):
return C.post("/api/auth/login", json={"username": u, "password": p}).get_json()["token"]
AT = login("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}).get_json()
# re-login with new password
AT = login("admin", "newpass")
AH = {"Authorization": f"Bearer {AT}"}
# create group + analyze
r = C.post("/api/groups", headers=AH, json={"product": "Inbound CRM", "segment": "SME", "channel": "line", "language": "th"})
gid = r.get_json()["group"]["id"]
C.post(f"/api/groups/{gid}/analyze", headers=AH)
personas = C.get(f"/api/groups/{gid}/personas", headers=AH).get_json()["personas"]
# build a ready group accepted by trainees
C.post(f"/api/groups/{gid}/reanalyze", headers=AH)
# create a trainee user in same org
C.post("/api/admin/users", headers=AH, json={"username": "t1", "name": "T", "password": "pppp", "role": "user"})
T = login("t1", "pppp")
TH = {"Authorization": f"Bearer {T}"}
pid = personas[0]["id"]
# social -> customer opens
r = C.post(f"/api/chat/{gid}/personas/{pid}/chat/start", headers=TH, json={"scenario": "social"})
assert r.status_code == 200, r.get_json()
s = r.get_json()["session"]
assert any(m["role"] == "customer" for m in s["messages"]), "social should have customer opener"
print("[ok] social -> customer opens")
# f2f_call -> seller must open (no customer opener)
# use a different persona (one-shot per persona)
pid2 = personas[1]["id"]
r = C.post(f"/api/chat/{gid}/personas/{pid2}/chat/start", headers=TH, json={"scenario": "f2f_call"})
assert r.status_code == 200, r.get_json()
s2 = r.get_json()["session"]
assert not any(m["role"] == "customer" for m in s2["messages"]), "f2f should NOT have customer opener"
assert any(m["role"] == "system" for m in s2["messages"]), "f2f should have a scenario system note"
print("[ok] f2f_call -> seller must open (system note present)")
# unknown scenario -> default to social (customer opens)
pid3 = personas[2]["id"]
r = C.post(f"/api/chat/{gid}/personas/{pid3}/chat/start", headers=TH, json={"scenario": "recontact"})
assert r.status_code == 200, r.get_json()
s3 = r.get_json()["session"]
assert any(m["role"] == "customer" for m in s3["messages"]), "unknown scenario should default to social (customer opens)"
print("[ok] unknown scenario defaults to social (customer opens)")
# recontact is now a persona trait (not a scenario): some personas are flagged recontact
personas_all = C.get(f"/api/groups/{gid}/personas", headers=AH).get_json()["personas"]
recontact_n = sum(1 for p in personas_all if p.get("recontact"))
print(f"[ok] {recontact_n} of {len(personas_all)} generated personas are 'recontact' (warm returning leads)")
# The mock generates deterministic personas; we just assert the shape has the field (default False ok).
print("ALL SCENARIO TESTS PASSED")