Files
ai-contract-platform/backend/tests/test_chat_api.py
T

155 lines
6.3 KiB
Python

import json
import unittest
from fastapi import HTTPException
from fastapi.testclient import TestClient
from app.api.chat import get_shared_ai_platform_client
from app.api.conversations import get_shared_ai_platform_client as get_conversations_shared_ai_platform_client
from app.api.reviews import require_current_user
from app.main import app
class StubChatService:
def __init__(self):
self.stream_calls = []
def stream_answer(self, current_user, conversation_id, message, review_context=None):
self.stream_calls.append((current_user, conversation_id, message, review_context))
if not message.strip():
yield 'data: {"type":"error","message":"message 不能为空"}\n\n'
return
active_id = conversation_id or "conv-123"
yield 'data: {"type":"text","content":"第一段"}\n\n'
yield 'data: {"type":"text","content":"重点摘要:\\n- 关注违约金条款"}\n\n'
yield 'data: {"type":"sources","sources":[{"title":"民法典第585条","content":"...","score":1.2}]}\n\n'
yield f'data: {{"type":"end","conversation_id":"{active_id}"}}\n\n'
def list_conversations(self, current_user, page, page_size):
return {
"items": [
{
"id": "conv-123",
"title": "违约金条款咨询",
"message_count": 2,
"last_message_at": "2026-04-15T11:00:00+00:00",
}
],
"page": page,
"page_size": page_size,
"total": 1,
}
def get_conversation_detail(self, current_user, conversation_id):
if conversation_id == "missing":
raise HTTPException(status_code=404, detail="conversation not found")
return {
"id": conversation_id,
"title": "违约金条款咨询",
"messages": [
{
"id": "msg-1",
"role": "user",
"content": "这个违约金条款有没有问题?",
"sources": [],
"created_at": "2026-04-15T11:00:00+00:00",
},
{
"id": "msg-2",
"role": "assistant",
"content": "存在偏高风险。",
"sources": [{"title": "民法典第585条", "content": "..."}],
"created_at": "2026-04-15T11:00:03+00:00",
},
],
}
class ChatApiTestCase(unittest.TestCase):
def setUp(self):
self.chat_service = StubChatService()
app.dependency_overrides[get_shared_ai_platform_client] = lambda: self.chat_service
app.dependency_overrides[get_conversations_shared_ai_platform_client] = lambda: self.chat_service
self.client = TestClient(app)
def tearDown(self):
app.dependency_overrides.clear()
def test_stream_chat_requires_authentication(self):
response = self.client.post("/api/v1/chat/stream", json={"message": "违约金条款有没有问题"})
self.assertEqual(response.status_code, 401)
def test_stream_chat_returns_sse_events(self):
app.dependency_overrides[require_current_user] = lambda: {"id": "user-1", "tenant_id": "tenant-1"}
response = self.client.post("/api/v1/chat/stream", json={"message": "违约金条款有没有问题"})
self.assertEqual(response.status_code, 200)
self.assertIn("text/event-stream", response.headers["content-type"])
events = [line.removeprefix("data: ") for line in response.text.split("\n\n") if line.startswith("data: ")]
payloads = [json.loads(item) for item in events]
self.assertEqual(payloads[0]["type"], "text")
self.assertEqual(payloads[1]["type"], "text")
self.assertEqual(payloads[2]["type"], "sources")
self.assertEqual(payloads[2]["sources"][0]["score"], 1.2)
self.assertEqual(payloads[3]["type"], "end")
self.assertEqual(payloads[3]["conversation_id"], "conv-123")
self.assertEqual(self.chat_service.stream_calls[0][0]["id"], "user-1")
def test_stream_chat_accepts_review_context(self):
app.dependency_overrides[require_current_user] = lambda: {"id": "user-1", "tenant_id": "tenant-1"}
response = self.client.post(
"/api/v1/chat/stream",
json={
"message": "请继续解释这个风险项",
"review_context": {
"reviewId": "review-123",
"metadata": {
"reviewTitle": "采购合同审查",
"focusLabel": "自动续约",
"actionLabel": "解释风险",
},
},
},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(self.chat_service.stream_calls[0][3]["reviewId"], "review-123")
self.assertEqual(self.chat_service.stream_calls[0][3]["metadata"]["focusLabel"], "自动续约")
def test_list_conversations_returns_paginated_payload(self):
app.dependency_overrides[require_current_user] = lambda: {"id": "user-1", "tenant_id": "tenant-1"}
response = self.client.get("/api/v1/conversations?page=1&page_size=20")
self.assertEqual(response.status_code, 200)
payload = response.json()["data"]
self.assertEqual(payload["total"], 1)
self.assertEqual(payload["items"][0]["id"], "conv-123")
def test_get_conversation_detail_returns_messages(self):
app.dependency_overrides[require_current_user] = lambda: {"id": "user-1", "tenant_id": "tenant-1"}
response = self.client.get("/api/v1/conversations/conv-123")
self.assertEqual(response.status_code, 200)
payload = response.json()["data"]
self.assertEqual(payload["id"], "conv-123")
self.assertEqual(len(payload["messages"]), 2)
self.assertEqual(payload["messages"][1]["sources"][0]["title"], "民法典第585条")
def test_get_conversation_detail_returns_404_when_missing(self):
app.dependency_overrides[require_current_user] = lambda: {"id": "user-1", "tenant_id": "tenant-1"}
response = self.client.get("/api/v1/conversations/missing")
self.assertEqual(response.status_code, 404)
if __name__ == "__main__":
unittest.main()