Boot memory IAM only via explicit test env opt-in; strict auth cases disable AllowInMemory after startup. Co-authored-by: Cursor <cursoragent@cursor.com>
182 lines
6.1 KiB
Go
182 lines
6.1 KiB
Go
package server_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"chat-agent-system/internal/bizdev"
|
|
"chat-agent-system/internal/config"
|
|
"chat-agent-system/internal/ctxmgr"
|
|
"chat-agent-system/internal/server"
|
|
)
|
|
|
|
func TestServer_ChatWindowAndDayAPIs(t *testing.T) {
|
|
cfg := &config.Config{
|
|
Env: "dev",
|
|
HTTPHost: "127.0.0.1",
|
|
HTTPPort: 8889,
|
|
DataDir: t.TempDir(),
|
|
}
|
|
svc := bizdev.MustNewService(t, cfg)
|
|
srv := server.NewServer(cfg, svc)
|
|
handler := srv.Routes()
|
|
|
|
sessID := "sess_api_window_001"
|
|
userID := int64(1) // 与 AllowInMemory legacy principal 一致
|
|
|
|
// 1. 测试 POST /api/v1/chat/messages/import (批量历史回填)
|
|
importBody := []map[string]any{
|
|
{
|
|
"session_id": sessID,
|
|
"user_id": userID,
|
|
"role": "user",
|
|
"content": "9月8日初次需求沟通:规划一款神经外科无线超声",
|
|
"intent": "SUPPLEMENT_INFO",
|
|
"branch": "",
|
|
"day_key": "2026-09-08",
|
|
"created_at": "2026-09-08T09:00:00+08:00",
|
|
},
|
|
{
|
|
"session_id": sessID,
|
|
"user_id": 0,
|
|
"role": "assistant",
|
|
"content": "已记录需求,正在检索 NMPA 神经外科超声相关审评要点",
|
|
"intent": "ADVISOR_BRIEF",
|
|
"branch": "",
|
|
"day_key": "2026-09-08",
|
|
"created_at": "2026-09-08T09:00:15+08:00",
|
|
},
|
|
{
|
|
"session_id": sessID,
|
|
"user_id": userID,
|
|
"role": "user",
|
|
"content": "9月10日最新进展:请生成技术方案初步草案",
|
|
"intent": "TRIGGER_TASK",
|
|
"branch": "",
|
|
"day_key": "2026-09-10",
|
|
"created_at": "2026-09-10T10:00:00+08:00",
|
|
},
|
|
}
|
|
bodyBytes, _ := json.Marshal(importBody)
|
|
|
|
reqImport := httptest.NewRequest("POST", "/api/v1/chat/messages/import", bytes.NewReader(bodyBytes))
|
|
rrImport := httptest.NewRecorder()
|
|
handler.ServeHTTP(rrImport, reqImport)
|
|
|
|
if rrImport.Code != http.StatusOK {
|
|
t.Fatalf("POST /api/v1/chat/messages/import expected 200, got %d: %s", rrImport.Code, rrImport.Body.String())
|
|
}
|
|
var importResp map[string]any
|
|
_ = json.Unmarshal(rrImport.Body.Bytes(), &importResp)
|
|
if int(importResp["imported_count"].(float64)) != 3 {
|
|
t.Fatalf("expected 3 messages imported, got %v", importResp["imported_count"])
|
|
}
|
|
|
|
// 2. 测试 GET /api/v1/chat/messages/window
|
|
reqWindow := httptest.NewRequest("GET", "/api/v1/chat/messages/window?session_id="+sessID+"&days=7", nil)
|
|
rrWindow := httptest.NewRecorder()
|
|
handler.ServeHTTP(rrWindow, reqWindow)
|
|
|
|
if rrWindow.Code != http.StatusOK {
|
|
t.Fatalf("GET /api/v1/chat/messages/window expected 200, got %d: %s", rrWindow.Code, rrWindow.Body.String())
|
|
}
|
|
|
|
var winResult ctxmgr.ChatWindowResult
|
|
if err := json.Unmarshal(rrWindow.Body.Bytes(), &winResult); err != nil {
|
|
t.Fatalf("unmarshal ChatWindowResult failed: %v", err)
|
|
}
|
|
|
|
if winResult.SessionID != sessID {
|
|
t.Errorf("expected session_id %s, got %s", sessID, winResult.SessionID)
|
|
}
|
|
if len(winResult.Days) != 2 {
|
|
t.Fatalf("expected 2 days in window, got %d", len(winResult.Days))
|
|
}
|
|
if winResult.Days[0].DayKey != "2026-09-08" || winResult.Days[1].DayKey != "2026-09-10" {
|
|
t.Errorf("expected days [2026-09-08, 2026-09-10], got [%s, %s]", winResult.Days[0].DayKey, winResult.Days[1].DayKey)
|
|
}
|
|
if len(winResult.Days[0].Messages) != 2 || len(winResult.Days[1].Messages) != 1 {
|
|
t.Errorf("unexpected day message counts: day0=%d, day1=%d", len(winResult.Days[0].Messages), len(winResult.Days[1].Messages))
|
|
}
|
|
|
|
// 3. 测试 GET /api/v1/chat/dates
|
|
reqDates := httptest.NewRequest("GET", "/api/v1/chat/dates?session_id="+sessID, nil)
|
|
rrDates := httptest.NewRecorder()
|
|
handler.ServeHTTP(rrDates, reqDates)
|
|
|
|
if rrDates.Code != http.StatusOK {
|
|
t.Fatalf("GET /api/v1/chat/dates expected 200, got %d: %s", rrDates.Code, rrDates.Body.String())
|
|
}
|
|
var dateStats []ctxmgr.ChatDateStat
|
|
if err := json.Unmarshal(rrDates.Body.Bytes(), &dateStats); err != nil {
|
|
t.Fatalf("unmarshal ChatDateStat failed: %v", err)
|
|
}
|
|
if len(dateStats) != 2 {
|
|
t.Fatalf("expected 2 dates, got %d", len(dateStats))
|
|
}
|
|
if dateStats[0].Count != 2 || dateStats[1].Count != 1 {
|
|
t.Errorf("unexpected date message counts: %v", dateStats)
|
|
}
|
|
|
|
// 4. 测试 GET /api/v1/chat/messages/day
|
|
reqDay := httptest.NewRequest("GET", "/api/v1/chat/messages/day?session_id="+sessID+"&date=2026-09-08", nil)
|
|
rrDay := httptest.NewRecorder()
|
|
handler.ServeHTTP(rrDay, reqDay)
|
|
|
|
if rrDay.Code != http.StatusOK {
|
|
t.Fatalf("GET /api/v1/chat/messages/day expected 200, got %d: %s", rrDay.Code, rrDay.Body.String())
|
|
}
|
|
var dayRes ctxmgr.ChatDayMessagesResult
|
|
if err := json.Unmarshal(rrDay.Body.Bytes(), &dayRes); err != nil {
|
|
t.Fatalf("unmarshal ChatDayMessagesResult failed: %v", err)
|
|
}
|
|
if dayRes.DayKey != "2026-09-08" || dayRes.TotalCount != 2 || len(dayRes.Messages) != 2 {
|
|
t.Fatalf("unexpected day messages result: %+v", dayRes)
|
|
}
|
|
|
|
// 5. 测试 GET /api/v1/chat/categories
|
|
reqCat := httptest.NewRequest("GET", "/api/v1/chat/categories?session_id="+sessID, nil)
|
|
rrCat := httptest.NewRecorder()
|
|
handler.ServeHTTP(rrCat, reqCat)
|
|
|
|
if rrCat.Code != http.StatusOK {
|
|
t.Fatalf("GET /api/v1/chat/categories expected 200, got %d: %s", rrCat.Code, rrCat.Body.String())
|
|
}
|
|
var catStats []ctxmgr.ChatCategoryStat
|
|
if err := json.Unmarshal(rrCat.Body.Bytes(), &catStats); err != nil {
|
|
t.Fatalf("unmarshal ChatCategoryStat failed: %v", err)
|
|
}
|
|
if len(catStats) < 2 {
|
|
t.Fatalf("expected at least 2 categories, got %d", len(catStats))
|
|
}
|
|
}
|
|
|
|
func TestServer_ChatWindow_ImportGuard(t *testing.T) {
|
|
// 验证在生产环境中 import 接口被正确拦截
|
|
cfg := &config.Config{
|
|
Env: "test",
|
|
HTTPHost: "127.0.0.1",
|
|
HTTPPort: 8889,
|
|
DataDir: t.TempDir(),
|
|
AllowInMemory: true,
|
|
AgentBackend: "stub",
|
|
}
|
|
svc := bizdev.MustNewService(t, cfg)
|
|
srv := server.NewServer(cfg, svc)
|
|
cfg.Env = "production"
|
|
handler := srv.Routes()
|
|
|
|
reqImport := httptest.NewRequest("POST", "/api/v1/chat/messages/import", bytes.NewReader([]byte("[]")))
|
|
reqImport.Header.Set("Content-Type", "application/json")
|
|
withBearerAuth(reqImport, loginTestAdmin(t, handler, cfg))
|
|
rrImport := httptest.NewRecorder()
|
|
handler.ServeHTTP(rrImport, reqImport)
|
|
|
|
if rrImport.Code != http.StatusForbidden {
|
|
t.Fatalf("expected 403 Forbidden in production env, got %d", rrImport.Code)
|
|
}
|
|
}
|