# 本地单元测试指南:Antigravity 账号验证 ## 概述 本指南帮助你在本地环境中,不通过 HTTP,直接调用服务器代码来测试 Antigravity 账号 ID 68 的连接性。 ## 当前测试状态 ✅ **基础验证已通过**: - 账号 ID: 68 - 平台: antigravity - 类型: oauth - 凭证完整性: ✓ - Token 有效期: ✓ (有效期至 2026-04-11 18:25:54) - Project ID: kinetic-sum-r3tp7 ## 运行基础测试 ```bash cd backend go test -v -run TestAntigravityCredentialsValidation ./internal/service ``` **预期输出**: ``` === RUN TestAntigravityCredentialsValidation ... --- PASS: TestAntigravityCredentialsValidation (0.00s) PASS ok github.com/Wei-Shaw/sub2api/internal/service 0.607s ``` ## 问题诊断:找出 "IT" 错误的来源 当前问题:HTTP 请求返回了 "IT" 错误,这不是一个有意义的错误消息。 ### 可能的原因 1. **错误消息被截断** - 原始错误可能是 "INTERNAL_ERROR" 或其他,但在某个地方被截断成 "IT" - 问题位置:`account_test_service.go` 中的 `sendErrorAndEnd` 或错误处理逻辑 2. **HTTP 响应体包含不完整的字符** - 上游 API 返回的错误响应可能被不完整地处理 - 问题位置:`antigravity_gateway_service.go` 中的 `TestConnection` 或 `antigravityRetryLoop` 3. **编码错误** - 错误消息在 SSE 流中被破坏 - 问题位置:`account_test_service.go` 中的 SSE 事件处理 ## 创建增强的诊断测试 创建文件:`backend/internal/service/antigravity_test_diagnostic_test.go` ```go package service import ( "context" "testing" "time" ) // TestAntigravityDiagnoseConnectionError 诊断性测试 // 直接调用 AntigravityGatewayService.TestConnection,捕获完整的错误信息 func TestAntigravityDiagnoseConnectionError(t *testing.T) { // 这个测试需要依赖注入: // - AccountRepository // - TokenProvider // - HTTPUpstream // - AntigravityGatewayService // // 由于本地测试无法访问真实数据库和配置, // 需要在集成测试环境中运行 t.Skip("Requires integration test environment with database access") // 伪代码:实际实现步骤 // 1. 从数据库获取账号 // account, err := accountRepo.GetByID(ctx, 68) // if err != nil { // t.Fatalf("Failed to load account: %v", err) // } // 2. 调用 TestConnection // result, err := gatewayService.TestConnection(ctx, account, "claude-opus-4-6") // // if err != nil { // // 完整的错误信息应该会显示在这里,而不是 "IT" // t.Logf("Error type: %T", err) // t.Logf("Error message: %s", err.Error()) // t.Logf("Error details: %#v", err) // // // 进行根因分析 // analyzeAntigravityError(t, err, account) // return // } // // t.Logf("✅ Test passed") // t.Logf("Response: %+v", result) } // analyzeAntigravityError 分析 Antigravity 错误的根本原因 func analyzeAntigravityError(t *testing.T, err error, account *Account) { t.Logf("📊 Error Analysis for Account %d:", account.ID) t.Logf(" Error type: %T", err) t.Logf(" Error message: %s", err.Error()) // 检查是否是 AccountSwitchError // if switchErr, ok := IsAntigravityAccountSwitchError(err); ok { // t.Logf(" ⚠️ Account Switch Error:") // t.Logf(" Original Account ID: %d", switchErr.OriginalAccountID) // t.Logf(" Rate Limited Model: %s", switchErr.RateLimitedModel) // return // } // 其他错误分析... } ``` ## 实际诊断步骤 ### 步骤 1:增加日志记录 编辑 `account_test_service.go` 的 `sendErrorAndEnd` 函数: ```go func (s *AccountTestService) sendErrorAndEnd(c *gin.Context, msg string) error { // ADD: 完整的错误日志 log.Printf("[DIAGNOSTIC] sendErrorAndEnd called with message: %q (len=%d)", msg, len(msg)) s.sendEvent(c, TestEvent{ Type: "test_error", Error: msg, Success: false, }) s.sendEvent(c, TestEvent{Type: "test_complete", Success: false}) return nil } ``` ### 步骤 2:追踪 routeAntigravityTest 的路径 编辑 `account_test_service.go` 的 `routeAntigravityTest` 函数: ```go func (s *AccountTestService) routeAntigravityTest(c *gin.Context, account *Account, modelID string, prompt string) error { log.Printf("[DIAGNOSTIC] routeAntigravityTest: account=%d, platform=%s, type=%s, modelID=%s", account.ID, account.Platform, account.Type, modelID) if account.Type == AccountTypeAPIKey { log.Printf("[DIAGNOSTIC] Using APIKey path") if strings.HasPrefix(modelID, "gemini-") { return s.testGeminiAccountConnection(c, account, modelID, prompt) } return s.testClaudeAccountConnection(c, account, modelID) } log.Printf("[DIAGNOSTIC] Using testAntigravityAccountConnection path") return s.testAntigravityAccountConnection(c, account, modelID) } ``` ### 步骤 3:在 TestConnection 中增加诊断日志 编辑 `antigravity_gateway_service.go` 的 `TestConnection` 函数: ```go func (s *AntigravityGatewayService) TestConnection(ctx context.Context, account *Account, modelID string) (*TestConnectionResult, error) { log.Printf("[DIAGNOSTIC] TestConnection start: account=%d, modelID=%s", account.ID, modelID) // ... 现有代码 ... accessToken, err := s.tokenProvider.GetAccessToken(ctx, account) if err != nil { errMsg := fmt.Sprintf("获取 access_token 失败: %w", err) log.Printf("[DIAGNOSTIC] GetAccessToken failed: %v", err) return nil, errors.New(errMsg) } log.Printf("[DIAGNOSTIC] Access token obtained successfully") // ... 继续现有代码 ... result, err := s.antigravityRetryLoop(p) if err != nil { log.Printf("[DIAGNOSTIC] antigravityRetryLoop failed with error type %T: %v", err, err) return nil, err } log.Printf("[DIAGNOSTIC] TestConnection completed successfully") return &TestConnectionResult{Text: text, MappedModel: mappedModel}, nil } ``` ## 在完整环境中运行诊断 ### 方法 A:使用现有的测试端点 使用你的 curl 命令,但启用详细日志: ```bash # 启用应用的详细日志记录 export LOGLEVEL=debug # 运行测试端点 curl -X POST 'https://temp365.top/api/v1/admin/accounts/68/test' \ -H 'Content-Type: application/json' \ -H 'authorization: Bearer YOUR_JWT_TOKEN' \ -d '{"model_id":"claude-opus-4-6","prompt":""}' \ -v ``` ### 方法 B:编写集成测试 创建 `backend/internal/service/antigravity_integration_test.go`: ```go // 这个文件需要: // 1. 数据库连接 // 2. 真实的 HTTP 客户端配置 // 3. 配置文件 // // 在完整的开发环境中运行 ``` ## 预期的完整错误消息示例 正确的错误消息应该类似于: ``` "Invalid access token" "Account not found" "Project ID not available" "Google API returned 401: Invalid credentials" "Network timeout connecting to upstream" "Request rate limit exceeded for model claude-opus-4-6" ``` 如果返回的是 "IT",说明: 1. ❌ 错误被截断(原文可能是 20+ 个字符,被截断成 2 个) 2. ❌ 字符编码问题(UTF-8/ASCII 混淆) 3. ❌ SSE 流中的损坏数据 ## 日志文件位置 在完整服务运行中,查看日志: ```bash # 应用日志 tail -f /var/log/sub2api/server.log | grep "DIAGNOSTIC" # Docker 日志 docker logs -f | grep "DIAGNOSTIC" ``` ## 下一步 1. ✅ **已完成**:本地基础验证 2. ⏭️ **待做**:增加诊断日志并重新测试 3. ⏭️ **待做**:分析完整的错误消息 4. ⏭️ **待做**:修复根本原因 ## 参考代码位置 - 账号测试服务:`backend/internal/service/account_test_service.go` - `TestAccountConnection()` - 第 162 行 - `testAntigravityAccountConnection()` - 第 629 行 - `routeAntigravityTest()` - 第 617 行 - `sendErrorAndEnd()` - 查找函数定义 - Antigravity 网关服务:`backend/internal/service/antigravity_gateway_service.go` - `TestConnection()` - 第 1114 行 - `antigravityRetryLoop()` - 查找函数定义 - HTTP 处理器:`backend/internal/handler/admin/account_handler.go` - `Test()` - 第 671 行(路由处理) --- **创建时间**: 2026-04-11 **测试版本**: v1 **状态**: 就绪 ✓