package routes import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" "github.com/Wei-Shaw/sub2api/internal/service" "log/slog" ) func TestAntigravityHTTPRoutes(t *testing.T) { gin.SetMode(gin.TestMode) // 创建模拟的 LanguageServerService mockService := service.NewLanguageServerService(slog.Default(), nil) // 创建路由 r := gin.New() v1 := r.Group("/api/v1") // 注册 Antigravity 路由 RegisterAntigravityHTTPRoutes(v1, mockService) // 测试 1: GET /health t.Run("HealthCheck", func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/api/v1/health", nil) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("Expected 200, got %d", w.Code) } var result map[string]string json.Unmarshal(w.Body.Bytes(), &result) if result["status"] != "healthy" { t.Fatalf("Expected status=healthy, got %v", result) } t.Log("✅ 健康检查端点") }) // 测试 2: GET /models t.Run("GetModels", func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/api/v1/models", nil) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("Expected 200, got %d", w.Code) } var result map[string]interface{} json.Unmarshal(w.Body.Bytes(), &result) if result["default_model"] != "claude-opus-4-6" { t.Fatalf("Expected default_model, got %v", result) } t.Log("✅ 获取模型列表") }) // 测试 3: POST /cascade/start var cascadeID string t.Run("StartCascade", func(t *testing.T) { body, _ := json.Marshal(map[string]string{ "model": "claude-opus-4-6", }) w := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/api/v1/cascade/start", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer test-token") r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("Expected 200, got %d", w.Code) } var result map[string]string json.Unmarshal(w.Body.Bytes(), &result) cascadeID = result["cascade_id"] if cascadeID == "" { t.Fatalf("Expected cascade_id, got empty") } t.Logf("✅ 启动会话 (cascade_id=%s)", cascadeID) }) // 测试 4: POST /cascade/cancel(使用从第3个测试获取的真实会话ID) t.Run("CancelCascade", func(t *testing.T) { body, _ := json.Marshal(map[string]string{ "cascade_id": cascadeID, }) w := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/api/v1/cascade/cancel", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("Expected 200, got %d", w.Code) } var result map[string]string json.Unmarshal(w.Body.Bytes(), &result) if result["message"] != "cascade cancelled" { t.Fatalf("Expected cascade cancelled message, got %v", result) } t.Log("✅ 取消会话") }) // 测试 5: POST /cascade/message (SSE) - 验证响应头格式 t.Run("SendMessage", func(t *testing.T) { body, _ := json.Marshal(map[string]string{ "cascade_id": cascadeID, "message": "Hello, world!", }) w := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/api/v1/cascade/message", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer test-token") r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("Expected 200, got %d", w.Code) } contentType := w.Header().Get("Content-Type") if contentType != "text/event-stream" { t.Fatalf("Expected text/event-stream, got %s", contentType) } t.Log("✅ 发送消息(SSE流式响应)") }) t.Log("\n✅ 所有 Antigravity HTTP API 路由测试通过!") } func TestStartCascadeValidation(t *testing.T) { gin.SetMode(gin.TestMode) mockService := service.NewLanguageServerService(slog.Default(), nil) r := gin.New() v1 := r.Group("/api/v1") RegisterAntigravityHTTPRoutes(v1, mockService) t.Run("MissingModel", func(t *testing.T) { w := httptest.NewRecorder() body := []byte(`{"system_prompt":"test"}`) req, _ := http.NewRequest("POST", "/api/v1/cascade/start", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer test-token") r.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("Expected 400 for invalid request, got %d", w.Code) } t.Log("✅ 缺少必需字段验证") }) t.Run("MissingAuthorization", func(t *testing.T) { w := httptest.NewRecorder() body := []byte(`{"model":"claude-opus-4-6"}`) req, _ := http.NewRequest("POST", "/api/v1/cascade/start", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") // 不设置 Authorization 头 r.ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("Expected 401 for missing auth, got %d", w.Code) } t.Log("✅ 缺少授权令牌验证") }) t.Log("\n✅ 所有验证测试通过!") }