package server_test import ( "bytes" "context" "encoding/json" "net/http" "net/http/httptest" "strconv" "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.MustNewService(t, cfg) srv := server.NewServer(cfg, svc) handler := srv.Routes() token := loginTestAdmin(t, handler, cfg) adminID := fetchAuthUserID(t, handler, token) ctx := context.Background() sessID := "sess_api_history_001" userID := adminID if _, err := bizdev.Snapshot(svc).Ctx.EnsureChatSession(ctx, sessID, userID, 0, "历史会话"); err != nil { t.Fatalf("ensure session: %v", err) } // 1. 模拟写入对话消息 _ = bizdev.Snapshot(svc).Ctx.RecordUserMessage(ctx, sessID, userID, "user", "我们准备立项一款便携无线掌上超声,预算大约150万", "SUPPLEMENT_INFO", "EXPECTATION_HIT", nil) _ = bizdev.Snapshot(svc).Ctx.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="+strconv.FormatInt(userID, 10), nil) withBearerAuth(reqSessions, token) 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") withBearerAuth(reqUpsert, token) 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) withBearerAuth(reqMsgs, token) 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 := bizdev.Snapshot(svc).Ctx.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) }