package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" "testing" "time" "git.x2erp.com/qdy/go-svc-code/internal/opencode" ) // TestSessionList 测试会话列表API func TestSessionList(t *testing.T) { // 获取svc-code服务地址 svcCodeURL := "http://localhost:8020" // 检查服务是否运行 if !checkServiceRunning(t, svcCodeURL) { t.Skipf("svc-code服务未运行在 %s,跳过测试", svcCodeURL) } // 1. 用户登录获取token token, err := loginAndGetTokenForSessionTest(t, svcCodeURL) if err != nil { t.Fatalf("登录失败: %v", err) } t.Logf("获取到Token: %s...", token[:minSessionInt(20, len(token))]) // 2. 先创建几个测试会话 sessionIDs := []string{} for i := 1; i <= 3; i++ { sessionID, err := createSessionWithTitle(t, svcCodeURL, token, fmt.Sprintf("测试会话 %d", i)) if err != nil { t.Logf("创建会话 %d 失败: %v (可能是OpenCode连接问题)", i, err) continue } sessionIDs = append(sessionIDs, sessionID) t.Logf("创建会话成功: %s", sessionID) } if len(sessionIDs) == 0 { t.Log("警告: 未能创建任何测试会话,继续测试会话列表API") } // 3. 获取会话列表 sessions, err := getSessionList(t, svcCodeURL, token) if err != nil { t.Fatalf("获取会话列表失败: %v", err) } // 4. 验证响应 t.Logf("获取到 %d 个会话", len(sessions)) if len(sessions) > 0 { for i, session := range sessions { t.Logf("会话[%d]: ID=%s, Title=%s", i+1, session.ID, session.Title) } } else { t.Log("会话列表为空(可能是OpenCode未返回会话)") } // 5. 验证API响应结构 if len(sessions) > 0 { // 检查会话对象是否包含必要字段 session := sessions[0] if session.ID == "" { t.Error("会话ID不能为空") } if session.Title == "" { t.Error("会话标题不能为空") } } } // createSessionWithTitle 创建带标题的会话 func createSessionWithTitle(t *testing.T, svcCodeURL, token, title string) (string, error) { url := svcCodeURL + "/api/session/create" sessionData := map[string]string{ "title": title, } jsonData, _ := json.Marshal(sessionData) req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { return "", fmt.Errorf("创建请求失败: %v", err) } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json") client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { return "", fmt.Errorf("HTTP请求失败: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { bodyBytes, _ := io.ReadAll(resp.Body) return "", fmt.Errorf("创建会话失败 (状态码 %d): %s", resp.StatusCode, string(bodyBytes)) } var result struct { Success bool `json:"success"` Data struct { ID string `json:"id"` } `json:"data"` Message string `json:"message"` } bodyBytes, _ := io.ReadAll(resp.Body) if err := json.Unmarshal(bodyBytes, &result); err != nil { return "", fmt.Errorf("解析响应失败: %v", err) } if !result.Success { return "", fmt.Errorf("创建会话失败: %s", result.Message) } return result.Data.ID, nil } // getSessionList 获取会话列表 func getSessionList(t *testing.T, svcCodeURL, token string) ([]opencode.Session, error) { url := svcCodeURL + "/api/session/list" req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("创建请求失败: %v", err) } req.Header.Set("Authorization", "Bearer "+token) client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("HTTP请求失败: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { bodyBytes, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("获取会话列表失败 (状态码 %d): %s", resp.StatusCode, string(bodyBytes)) } var result struct { Success bool `json:"success"` Data []opencode.Session `json:"data"` Message string `json:"message"` } bodyBytes, _ := io.ReadAll(resp.Body) if err := json.Unmarshal(bodyBytes, &result); err != nil { return nil, fmt.Errorf("解析响应失败: %v", err) } if !result.Success { return nil, fmt.Errorf("获取会话列表失败: %s", result.Message) } return result.Data, nil } // loginAndGetTokenForSessionTest 登录获取token(会话测试专用) func loginAndGetTokenForSessionTest(t *testing.T, svcCodeURL string) (string, error) { loginURL := svcCodeURL + "/api/auth/login" loginData := map[string]string{ "user_id": "test-user-001", "password": "password123", } jsonData, _ := json.Marshal(loginData) resp, err := http.Post(loginURL, "application/json", bytes.NewBuffer(jsonData)) if err != nil { return "", fmt.Errorf("登录请求失败: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { bodyBytes, _ := io.ReadAll(resp.Body) return "", fmt.Errorf("登录失败 (状态码 %d): %s", resp.StatusCode, string(bodyBytes)) } var result struct { Success bool `json:"success"` Data string `json:"data"` Message string `json:"message"` } bodyBytes, _ := io.ReadAll(resp.Body) if err := json.Unmarshal(bodyBytes, &result); err != nil { return "", fmt.Errorf("解析响应失败: %v", err) } if !result.Success { return "", fmt.Errorf("登录失败: %s", result.Message) } return result.Data, nil } // checkServiceRunning 检查服务是否运行(会话测试专用) func checkServiceRunning(t *testing.T, url string) bool { client := &http.Client{Timeout: 3 * time.Second} resp, err := client.Get(url + "/api/health") if err != nil { // 尝试其他端点 resp, err = client.Get(url) if err != nil { return false } } defer resp.Body.Close() return resp.StatusCode == http.StatusOK || resp.StatusCode == 404 } // minSessionInt 返回两个整数的最小值(会话测试专用) func minSessionInt(a, b int) int { if a < b { return a } return b }