完成的测试: ✅ HealthCheck - 健康检查端点 ✅ GetModels - 获取模型列表 ✅ StartCascade - 启动会话(成功创建 cascade_id) ✅ CancelCascade - 取消会话(使用真实的 cascade_id) ✅ SendMessage - 发送消息(SSE 流式响应) ✅ MissingModel - 缺少必需字段验证 ✅ MissingAuthorization - 缺少授权令牌验证 全部通过率:100% 执行时间:~600ms Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
178 lines
4.9 KiB
Go
178 lines
4.9 KiB
Go
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✅ 所有验证测试通过!")
|
||
}
|