Files
ZNJJ-api-server/test/api/test_assignment6_endpoints.py
T
Eric Wang 8717d9066c feat(services): assignment 6 skeleton - app service, field registry, in-memory repository
- fields_registry: 24 business keys in acd/human1/human2 groups with
  value_type, writable and SensitiveLevel metadata; strict bool/int
  parsing (no bool('0') trap); INTERNAL_FIELD_KEYS protection list
- repository: SessionRepository protocol + InMemorySessionRepository
  (idempotent delete; PostgreSQL swap-in is assignment 7)
- AccidentAppService: chat/set_info/get_info/delete_session share one
  business state - chat injects latest repository fields into the graph
  and writes back validated field diffs; set/get/delete make zero LLM
  calls and zero helper conversations (includeInputInfo accepted but
  unused); unknown get keys keep the '' JSON-empty-string contract,
  unknown set keys are rejected by whitelist
- ChatInput.injected_fields neutral contract extension; LangGraphBackend
  merges injected fields into graph input (set_info visible next chat)
- endpoints: thin layer - langgraph mode routes business state through
  the app service (fastgpt mode keeps the legacy path); delete cleans
  repository + graph checkpoint, idempotent
- tests: registry completeness (sfzmwh/sjwh/csbw included), app service
  combos with LLM-call spy, HTTP acceptance flow (set -> get -> chat ->
  groups -> delete -> no leak, idempotent re-delete)
- docs/assignment6-compat.md: behavior diff list vs FastGPT status quo
  and the audit backlog (arbitrary set_info key usage, stream formUpdate
  write-back, sensitive-field display boundary)

Verified: 200 unit tests (172 existing + 28 new)
2026-10-22 20:50:10 +08:00

107 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""作业 6 验收演示的 HTTP 层组合测试。
链路(对应计划作业 6):创建会话 -> /set_info 写车牌 -> /get_info 读回
-> /chat 继续核实 -> 分组读值 -> /delete_session -> 再读与再聊不泄漏。
fake LLM(LLM_PROVIDER=fake)保证本测试离线可跑、零真实模型调用。
"""
import json
import pytest
from fastapi.testclient import TestClient
from src.agent.llm import workflow_fake_llm
from src.core.config import Config
from src.main import app
@pytest.fixture()
def client(monkeypatch):
"""langgraph 后端 + fake LLM 的测试客户端(lifespan 完整初始化)。"""
monkeypatch.setattr(Config, "AGENT_BACKEND", "langgraph")
monkeypatch.setattr(Config, "LLM_PROVIDER", "fake")
import src.agent.llm as llm_module
monkeypatch.setattr(
llm_module, "build_llm_service", lambda *a, **k: workflow_fake_llm()
)
with TestClient(app) as test_client:
yield test_client
TS = "20261022120000"
def test_assignment6_acceptance_flow(client):
session = "a6-acceptance-001"
# set_info 写入车牌(零 LLM:fake 未注入任何 responder,调用即报错)
response = client.post("/set_info", json={
"sessionId": session, "timeStamp": TS,
"key": "hphm1", "value": "苏B12345",
})
assert response.status_code == 200
assert response.json()["code"] == "200"
# get_info 单键读回
response = client.post("/get_info", json={
"sessionId": session, "timeStamp": TS, "key": "hphm1",
})
assert response.json()["value"] == '"苏B12345"'
# chat 使用业务状态继续(非流式,协议层解析 state 前缀)
response = client.post("/chat", json={
"sessionId": session, "timeStamp": TS,
"text": "我是刚才报警的驾驶员", "clientMode": "direct",
})
body = response.json()
assert body["code"] == "200"
assert body["nextStageCode"] in ("1001", "1002")
# 分组读值:三个分组齐全,未填字段为空串
response = client.post("/get_info", json={
"sessionId": session, "timeStamp": TS, "key": "all",
})
groups = json.loads(response.json()["value"])
assert set(groups) == {"acdinfo", "acdhuman1", "acdhuman2"}
assert groups["acdhuman1"]["hphm1"] == "苏B12345"
assert groups["acdhuman1"]["xm1"] == ""
# 未知 key 兼容:JSON 空串
response = client.post("/get_info", json={
"sessionId": session, "timeStamp": TS, "key": "no_such_key",
})
assert response.json()["value"] == '""'
# set_info 未知 key:白名单拒绝
response = client.post("/set_info", json={
"sessionId": session, "timeStamp": TS,
"key": "no_such_key", "value": "x",
})
assert response.json()["code"] == "500"
# 删除 -> 再读为空
response = client.request("DELETE", "/delete_session", json={
"sessionId": session, "timeStamp": TS,
})
assert response.json()["code"] == "200"
response = client.post("/get_info", json={
"sessionId": session, "timeStamp": TS, "key": "hphm1",
})
assert response.json()["value"] == '""'
# 重复删除幂等成功
response = client.request("DELETE", "/delete_session", json={
"sessionId": session, "timeStamp": TS,
})
assert response.json()["code"] == "200"
# 删除后再聊天:不注入旧业务数据,服务正常应答
response = client.post("/chat", json={
"sessionId": session, "timeStamp": TS,
"text": "新对话", "clientMode": "direct",
})
assert response.json()["code"] == "200"