package service import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" ) // TestHTTPResponseFlow 测试完整的 HTTP 请求-响应流,看客户端会收到什么 func TestHTTPResponseFlow(t *testing.T) { t.Log("🔥 模拟完整的 HTTP 请求-响应流...") t.Log("") // 创建一个模拟的服务 gin.SetMode(gin.TestMode) router := gin.New() // 模拟账号测试端点 router.POST("/api/v1/admin/accounts/:id/test", func(c *gin.Context) { // 模拟返回错误的情况 // 设置 SSE 头 c.Header("Content-Type", "text/event-stream") c.Header("Cache-Control", "no-cache") c.Header("Connection", "keep-alive") c.Header("X-Accel-Buffering", "no") c.Status(http.StatusOK) // 发送测试开始事件 event1 := map[string]interface{}{ "type": "test_start", "model": "claude-opus-4-6", } jsonData1, _ := json.Marshal(event1) c.Writer.WriteString("data: " + string(jsonData1) + "\n\n") c.Writer.Flush() // 模拟一个错误:比如 "INVALID_TOKEN" 或其他上游错误 // 这里我们故意测试不同的错误信息来看 curl 会显示什么 errorMessages := []string{ "INVALID_TOKEN", "INTERNAL_ERROR", "Invalid authentication credentials", "Th", // 测试短错误 "IT", // 直接测试 "IT" } selectedError := errorMessages[3] // 选择第 4 个:这应该显示为 "Th" 而不是 "IT" event2 := map[string]interface{}{ "type": "error", "error": selectedError, "success": false, } jsonData2, _ := json.Marshal(event2) c.Writer.WriteString("data: " + string(jsonData2) + "\n\n") c.Writer.Flush() // 发送完成事件 event3 := map[string]interface{}{ "type": "test_complete", "success": false, } jsonData3, _ := json.Marshal(event3) c.Writer.WriteString("data: " + string(jsonData3) + "\n\n") c.Writer.Flush() t.Logf("📤 服务器发送的错误: '%s'", selectedError) }) // 测试 1: 发送 HTTP 请求 t.Run("SendRequestAndCheckResponse", func(t *testing.T) { t.Log("步骤 1: 发送 HTTP 请求...") req := httptest.NewRequest("POST", "/api/v1/admin/accounts/68/test", bytes.NewReader([]byte(`{"model_id":"claude-opus-4-6"}`))) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) t.Log("✅ 请求已发送") t.Log("") // 步骤 2: 检查响应 t.Log("步骤 2: 分析 HTTP 响应...") t.Logf(" HTTP Status: %d", w.Code) t.Logf(" Content-Type: %s", w.Header().Get("Content-Type")) t.Log("") // 步骤 3: 读取 SSE 响应 t.Log("步骤 3: 读取 SSE 事件...") body := w.Body.String() t.Logf(" 响应总长度: %d 字节", len(body)) t.Log("") // 解析 SSE 事件 lines := bytes.Split([]byte(body), []byte("\n\n")) for i, line := range lines { if len(line) == 0 { continue } // 去掉 "data: " 前缀 if bytes.HasPrefix(line, []byte("data: ")) { data := bytes.TrimPrefix(line, []byte("data: ")) var event map[string]interface{} err := json.Unmarshal(data, &event) if err != nil { t.Logf(" 事件 %d: [解析失败] %v", i, err) continue } t.Logf(" 事件 %d:", i) t.Logf(" type: %v", event["type"]) if errMsg, ok := event["error"]; ok { t.Logf(" error: %v (长度: %d)", errMsg, len(errMsg.(string))) // 这就是 curl 会看到的错误信息 errStr := errMsg.(string) if errStr == "IT" { t.Logf(" ✓ 发现 'IT' 错误!") } else if errStr == "Th" { t.Logf(" ℹ️ 这是 'Th' 而不是 'IT'") } else { t.Logf(" ℹ️ 实际错误: '%s'", errStr) } } if model, ok := event["model"]; ok { t.Logf(" model: %v", model) } } } t.Log("") t.Log("📋 完整的原始响应:") t.Logf("%s", body) }) // 测试 2: 模拟真实的 curl 请求 t.Run("SimulateRealCurlRequest", func(t *testing.T) { t.Log("步骤: 模拟真实 curl 命令...") t.Log("") // 发送请求 req := httptest.NewRequest("POST", "/api/v1/admin/accounts/68/test", bytes.NewReader([]byte(`{"model_id":"claude-opus-4-6","prompt":""}`))) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer test-token") w := httptest.NewRecorder() router.ServeHTTP(w, req) // 模拟 curl 读取响应 body := w.Body.String() t.Log("curl 会看到:") t.Log("```") t.Log(body) t.Log("```") }) } // 辅助函数:提取 SSE 事件中的错误信息 func extractErrorFromSSE(sseBody string) string { lines := bytes.Split([]byte(sseBody), []byte("\n\n")) for _, line := range lines { if bytes.HasPrefix(line, []byte("data: ")) { data := bytes.TrimPrefix(line, []byte("data: ")) var event map[string]interface{} if err := json.Unmarshal(data, &event); err != nil { continue } if errMsg, ok := event["error"]; ok { return errMsg.(string) } } } return "" }