封堵鉴权旁路与导出 IDOR,DocGov 改服务端身份;对话层 PendingAction 接真实 docgov/taskrun;修复流式租约假死与会话级任务流;加固配置/doc_id/NULL;补齐 slog 与测试;前端 ErrorBoundary 与受控文档重试。 Co-authored-by: Cursor <cursoragent@cursor.com>
141 lines
5.1 KiB
Go
141 lines
5.1 KiB
Go
package server_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"chat-agent-system/internal/bizdev"
|
|
"chat-agent-system/internal/config"
|
|
"chat-agent-system/internal/ctxmgr"
|
|
"chat-agent-system/internal/docgov"
|
|
"chat-agent-system/internal/server"
|
|
)
|
|
|
|
func TestServer_ChatHistoryAndCaseRecommendationAPIs(t *testing.T) {
|
|
cfg := &config.Config{
|
|
HTTPHost: "127.0.0.1",
|
|
HTTPPort: 8888,
|
|
DataDir: t.TempDir(),
|
|
}
|
|
svc := bizdev.NewService(cfg)
|
|
srv := server.NewServer(cfg, svc)
|
|
handler := srv.Routes()
|
|
|
|
ctx := context.Background()
|
|
sessID := "sess_api_history_001"
|
|
userID := int64(108)
|
|
|
|
// 1. 模拟写入对话消息
|
|
_ = svc.ContextManager().RecordUserMessage(ctx, sessID, userID, "user", "我们准备立项一款便携无线掌上超声,预算大约150万", "SUPPLEMENT_INFO", "EXPECTATION_HIT", nil)
|
|
_ = svc.ContextManager().RecordUserMessage(ctx, sessID, 0, "assistant", "已为您建立超声设备立项基准,预估研发周期6个月", "ADVANCE_STAGE", "EXPECTATION_HIT", nil)
|
|
|
|
// 2. 测试 GET /api/v1/chat/sessions
|
|
reqSessions := httptest.NewRequest("GET", "/api/v1/chat/sessions?user_id=108", nil)
|
|
rrSessions := httptest.NewRecorder()
|
|
handler.ServeHTTP(rrSessions, reqSessions)
|
|
|
|
if rrSessions.Code != http.StatusOK {
|
|
t.Fatalf("GET /api/v1/chat/sessions expected 200, got %d: %s", rrSessions.Code, rrSessions.Body.String())
|
|
}
|
|
|
|
var sessionList []ctxmgr.ChatSessionSummary
|
|
if err := json.Unmarshal(rrSessions.Body.Bytes(), &sessionList); err != nil {
|
|
t.Fatalf("unmarshal sessions failed: %v", err)
|
|
}
|
|
if len(sessionList) != 1 || sessionList[0].SessionID != sessID {
|
|
t.Fatalf("unexpected sessions result: %+v", sessionList)
|
|
}
|
|
t.Logf("✓ 成功查询到历史会话列表: 包含会话=%s, 消息数=%d", sessionList[0].SessionID, sessionList[0].MessageCount)
|
|
|
|
// 2b. 测试 POST /api/v1/chat/sessions
|
|
upsertBody, _ := json.Marshal(map[string]any{
|
|
"session_id": "sess_p42_u108",
|
|
"title": "便携超声立项",
|
|
"user_id": userID,
|
|
"project_id": 42,
|
|
})
|
|
reqUpsert := httptest.NewRequest("POST", "/api/v1/chat/sessions", bytes.NewReader(upsertBody))
|
|
reqUpsert.Header.Set("Content-Type", "application/json")
|
|
rrUpsert := httptest.NewRecorder()
|
|
handler.ServeHTTP(rrUpsert, reqUpsert)
|
|
if rrUpsert.Code != http.StatusOK {
|
|
t.Fatalf("POST /api/v1/chat/sessions expected 200, got %d: %s", rrUpsert.Code, rrUpsert.Body.String())
|
|
}
|
|
var upserted ctxmgr.ChatSessionSummary
|
|
if err := json.Unmarshal(rrUpsert.Body.Bytes(), &upserted); err != nil {
|
|
t.Fatalf("unmarshal upserted session failed: %v", err)
|
|
}
|
|
if upserted.SessionID != "sess_p42_u108" || upserted.Title != "便携超声立项" {
|
|
t.Fatalf("unexpected upserted session: %+v", upserted)
|
|
}
|
|
|
|
// 3. 测试 GET /api/v1/chat/messages
|
|
reqMsgs := httptest.NewRequest("GET", "/api/v1/chat/messages?session_id="+sessID, nil)
|
|
rrMsgs := httptest.NewRecorder()
|
|
handler.ServeHTTP(rrMsgs, reqMsgs)
|
|
|
|
if rrMsgs.Code != http.StatusOK {
|
|
t.Fatalf("GET /api/v1/chat/messages expected 200, got %d: %s", rrMsgs.Code, rrMsgs.Body.String())
|
|
}
|
|
|
|
var msgList []ctxmgr.ChatMessageRecord
|
|
if err := json.Unmarshal(rrMsgs.Body.Bytes(), &msgList); err != nil {
|
|
t.Fatalf("unmarshal messages failed: %v", err)
|
|
}
|
|
if len(msgList) != 2 {
|
|
t.Fatalf("expected 2 messages, got %d", len(msgList))
|
|
}
|
|
t.Logf("✓ 成功分页获取会话消息历史: 轮次数=%d, 首条内容=%s", len(msgList), msgList[0].Content)
|
|
|
|
// 4. 预置一个受控文档终态并索引为标杆案例
|
|
now := time.Now()
|
|
doc := &docgov.DocumentRecord{
|
|
DocID: "doc_benchmark_api_01",
|
|
ProjectID: 501,
|
|
TaskKey: "feasibility_reg",
|
|
Title: "无线掌上超声探头可行性评估报告",
|
|
Stage: "V0.5_feasibility",
|
|
CurrentState: docgov.StateApproved,
|
|
IsLocked: true,
|
|
ApprovedAt: &now,
|
|
AuthorName: "王总工",
|
|
}
|
|
docBody := `# 无线掌上超声探头可行性评估报告
|
|
|
|
## 概述与器械分类
|
|
主要用于临床床旁急诊腹部与浅表超声扫查,属于二类或三类医疗器械。
|
|
|
|
## 关键性能与型检预估
|
|
符合 GB 9706.1-2020 和 YY 0644 标准要求,探头无线射频满足 SRRC 与无线电合规要求。`
|
|
|
|
_, err := svc.ContextManager().IndexApprovedDocument(ctx, doc, docBody)
|
|
if err != nil {
|
|
t.Fatalf("IndexApprovedDocument failed: %v", err)
|
|
}
|
|
|
|
// 5. 测试 GET /api/v1/context/cases/recommend
|
|
reqCase := httptest.NewRequest("GET", "/api/v1/context/cases/recommend?query=便携式无线掌上超声诊断系统&limit=3&min_score=0.10", nil)
|
|
rrCase := httptest.NewRecorder()
|
|
handler.ServeHTTP(rrCase, reqCase)
|
|
|
|
if rrCase.Code != http.StatusOK {
|
|
t.Fatalf("GET /api/v1/context/cases/recommend expected 200, got %d: %s", rrCase.Code, rrCase.Body.String())
|
|
}
|
|
|
|
var cases []ctxmgr.CaseRecommendationItem
|
|
if err := json.Unmarshal(rrCase.Body.Bytes(), &cases); err != nil {
|
|
t.Fatalf("unmarshal cases failed: %v", err)
|
|
}
|
|
if len(cases) == 0 {
|
|
t.Fatalf("expected recommended cases, got 0")
|
|
}
|
|
|
|
t.Logf("✓ 成功通过 API 检索到标杆案例推荐: 案例=%s, 匹配章节=%s, 得分=%.2f, 提要=%s",
|
|
cases[0].ArtifactTitle, cases[0].SectionTitle, cases[0].SimilarityScore, cases[0].SummarySnippet)
|
|
}
|