From 71d4dcc4412bf98615405c9a2fd84fe743959d20 Mon Sep 17 00:00:00 2001 From: long-agent Date: Thu, 9 Apr 2026 19:01:08 +0800 Subject: [PATCH] fix: resolve go vet warnings in webhook_handler_test.go - Replace raw http.DefaultClient.Do(req) with doRequestWithCheck helper - Helper function now handles errors via t.Fatalf - Content-Type only set when body is non-nil docs: update REAL_PROJECT_STATUS.md with 2026-04-09 verification Go vet: 0 warnings --- docs/status/REAL_PROJECT_STATUS.md | 55 +++++++++ internal/api/handler/webhook_handler_test.go | 118 +++++++------------ 2 files changed, 98 insertions(+), 75 deletions(-) diff --git a/docs/status/REAL_PROJECT_STATUS.md b/docs/status/REAL_PROJECT_STATUS.md index 6e11551..f3016de 100644 --- a/docs/status/REAL_PROJECT_STATUS.md +++ b/docs/status/REAL_PROJECT_STATUS.md @@ -1,5 +1,60 @@ # REAL PROJECT STATUS +## 2026-04-09 最低验证矩阵 & Service层测试增强 + +### 本轮验证结果 (2026-04-09) + +| 验证项 | 状态 | 说明 | +|--------|------|------| +| `go build ./cmd/server` | ✅ | 构建成功 | +| `go test ./internal/... -short` | ✅ | 全部38个packages通过 | +| `go vet ./internal/...` | ✅ | 无警告 | +| `npm run build` (frontend) | ✅ | 构建成功 | + +### 本轮修复内容 + +- **go vet 警告修复**: `webhook_handler_test.go` 中的 `resp` 错误检查问题 + - 添加 `doRequestWithCheck` 辅助函数统一错误处理 + - 所有 HTTP 请求现通过辅助函数执行,自动处理错误 + +- **Service层测试增强**: 新增6个测试文件 + - `webhook_service_test.go`: `isPrivateIP`, `isSafeURL`, `computeHMAC` 安全函数 + - `request_metadata_test.go`: Context元数据函数 + - `classified_error_test.go`: 错误类型测试 + - `config_defaults_test.go`: 配置默认值测试 + - `email_config_test.go`: 邮箱配置测试 + - `auth_runtime_test.go`: `isUserNotFoundError` 测试 + +### 覆盖率状态 + +| 模块 | 覆盖率 | +|------|--------| +| api/handler | 15.6% | +| api/middleware | 21.5% | +| auth | 28.1% | +| auth/providers | **80.6%** | +| cache | **77.3%** | +| config | **85.2%** | +| database | **74.1%** | +| repository | 47.2% | +| middleware (internal) | **65.4%** | +| service | 14.7% | + +### Govulncheck 漏洞状态 + +| 漏洞 | 影响 | 状态 | +|------|------|------| +| GO-2026-4866 (crypto/x509) | 需要 Go 1.26.2 修复 | ⚠️ 当前 Go 1.26.1 | +| GO-2026-4865 (html/template) | 需要 Go 1.26.2 修复 | ⚠️ 当前 Go 1.26.1 | + +**说明**: Go 1.26.2 下载失败(网络问题),待环境恢复后升级。 + +### 提交记录 + +- `a3e090e` - test: add service layer unit tests for webhook/metadata/error/config +- `a6a0e58` - test: add more UserHandler tests for RBAC coverage +- `3ffce94` - test: add WebhookHandler tests + ## 2026-04-02 E2E 测试扩展 ### E2E 测试场景扩展 diff --git a/internal/api/handler/webhook_handler_test.go b/internal/api/handler/webhook_handler_test.go index 77598c1..773acf6 100644 --- a/internal/api/handler/webhook_handler_test.go +++ b/internal/api/handler/webhook_handler_test.go @@ -28,6 +28,31 @@ import ( var webhookDbCounter int64 +// doRequestWithCheck 执行HTTP请求并在失败时t.Fatalf +func doRequestWithCheck(t *testing.T, method, url string, token string, body interface{}) *http.Response { + t.Helper() + var bodyReader io.Reader + if body != nil { + jsonBytes, _ := json.Marshal(body) + bodyReader = bytes.NewReader(jsonBytes) + } + req, err := http.NewRequest(method, url, bodyReader) + if err != nil { + t.Fatalf("create request failed: %v", err) + } + if token != "" { + req.Header.Set("Authorization", "Bearer "+token) + } + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("request failed: %v", err) + } + return resp +} + func setupWebhookTestServer(t *testing.T) (*httptest.Server, *gorm.DB, string, func()) { t.Helper() gin.SetMode(gin.TestMode) @@ -148,12 +173,7 @@ func TestWebhookHandler_CreateWebhook_Success(t *testing.T) { "url": "https://example.com/webhook", "events": []string{"user.created", "user.deleted"}, } - jsonBytes, _ := json.Marshal(reqBody) - req, _ := http.NewRequest("POST", server.URL+"/api/v1/webhooks", bytes.NewReader(jsonBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - - resp, _ := http.DefaultClient.Do(req) + resp := doRequestWithCheck(t, "POST", server.URL+"/api/v1/webhooks", token, reqBody) defer resp.Body.Close() if resp.StatusCode != http.StatusCreated { @@ -181,12 +201,8 @@ func TestWebhookHandler_CreateWebhook_InvalidURL(t *testing.T) { "url": "not-a-valid-url", "events": []string{"user.created"}, } - jsonBytes, _ := json.Marshal(reqBody) - req, _ := http.NewRequest("POST", server.URL+"/api/v1/webhooks", bytes.NewReader(jsonBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - resp, _ := http.DefaultClient.Do(req) + resp := doRequestWithCheck(t, "POST", server.URL+"/api/v1/webhooks", token, reqBody) defer resp.Body.Close() if resp.StatusCode != http.StatusBadRequest { @@ -202,12 +218,8 @@ func TestWebhookHandler_CreateWebhook_MissingName(t *testing.T) { "url": "https://example.com/webhook", "events": []string{"user.created"}, } - jsonBytes, _ := json.Marshal(reqBody) - req, _ := http.NewRequest("POST", server.URL+"/api/v1/webhooks", bytes.NewReader(jsonBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - resp, _ := http.DefaultClient.Do(req) + resp := doRequestWithCheck(t, "POST", server.URL+"/api/v1/webhooks", token, reqBody) defer resp.Body.Close() if resp.StatusCode != http.StatusBadRequest { @@ -225,17 +237,11 @@ func TestWebhookHandler_ListWebhooks_Success(t *testing.T) { "url": "https://example.com/webhook", "events": []string{"user.created"}, } - jsonBytes, _ := json.Marshal(reqBody) - req, _ := http.NewRequest("POST", server.URL+"/api/v1/webhooks", bytes.NewReader(jsonBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - http.DefaultClient.Do(req) + resp := doRequestWithCheck(t, "POST", server.URL+"/api/v1/webhooks", token, reqBody) + resp.Body.Close() // List webhooks - req, _ = http.NewRequest("GET", server.URL+"/api/v1/webhooks?page=1&page_size=10", nil) - req.Header.Set("Authorization", "Bearer "+token) - - resp, _ := http.DefaultClient.Do(req) + resp = doRequestWithCheck(t, "GET", server.URL+"/api/v1/webhooks?page=1&page_size=10", token, nil) defer resp.Body.Close() if resp.StatusCode != http.StatusOK { @@ -267,11 +273,7 @@ func TestWebhookHandler_UpdateWebhook_Success(t *testing.T) { "url": "https://example.com/webhook", "events": []string{"user.created"}, } - jsonBytes, _ := json.Marshal(createReq) - req, _ := http.NewRequest("POST", server.URL+"/api/v1/webhooks", bytes.NewReader(jsonBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - resp, _ := http.DefaultClient.Do(req) + resp := doRequestWithCheck(t, "POST", server.URL+"/api/v1/webhooks", token, createReq) var createResult map[string]interface{} json.NewDecoder(resp.Body).Decode(&createResult) resp.Body.Close() @@ -282,12 +284,8 @@ func TestWebhookHandler_UpdateWebhook_Success(t *testing.T) { updateReq := map[string]interface{}{ "name": "Updated Name", } - jsonBytes, _ = json.Marshal(updateReq) - req, _ = http.NewRequest("PUT", server.URL+fmt.Sprintf("/api/v1/webhooks/%.0f", webhookID), bytes.NewReader(jsonBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - resp, _ = http.DefaultClient.Do(req) + resp = doRequestWithCheck(t, "PUT", server.URL+fmt.Sprintf("/api/v1/webhooks/%.0f", webhookID), token, updateReq) defer resp.Body.Close() if resp.StatusCode != http.StatusOK { @@ -310,12 +308,8 @@ func TestWebhookHandler_UpdateWebhook_InvalidID(t *testing.T) { updateReq := map[string]interface{}{ "name": "Updated Name", } - jsonBytes, _ := json.Marshal(updateReq) - req, _ := http.NewRequest("PUT", server.URL+"/api/v1/webhooks/invalid", bytes.NewReader(jsonBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - resp, _ := http.DefaultClient.Do(req) + resp := doRequestWithCheck(t, "PUT", server.URL+"/api/v1/webhooks/invalid", token, updateReq) defer resp.Body.Close() if resp.StatusCode != http.StatusBadRequest { @@ -333,11 +327,7 @@ func TestWebhookHandler_DeleteWebhook_Success(t *testing.T) { "url": "https://example.com/webhook", "events": []string{"user.created"}, } - jsonBytes, _ := json.Marshal(createReq) - req, _ := http.NewRequest("POST", server.URL+"/api/v1/webhooks", bytes.NewReader(jsonBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - resp, _ := http.DefaultClient.Do(req) + resp := doRequestWithCheck(t, "POST", server.URL+"/api/v1/webhooks", token, createReq) var createResult map[string]interface{} json.NewDecoder(resp.Body).Decode(&createResult) resp.Body.Close() @@ -345,10 +335,7 @@ func TestWebhookHandler_DeleteWebhook_Success(t *testing.T) { webhookID := createResult["data"].(map[string]interface{})["id"].(float64) // Delete the webhook - req, _ = http.NewRequest("DELETE", server.URL+fmt.Sprintf("/api/v1/webhooks/%.0f", webhookID), nil) - req.Header.Set("Authorization", "Bearer "+token) - - resp, _ = http.DefaultClient.Do(req) + resp = doRequestWithCheck(t, "DELETE", server.URL+fmt.Sprintf("/api/v1/webhooks/%.0f", webhookID), token, nil) defer resp.Body.Close() if resp.StatusCode != http.StatusOK { @@ -368,10 +355,7 @@ func TestWebhookHandler_DeleteWebhook_NotFound(t *testing.T) { server, _, token, cleanup := setupWebhookTestServer(t) defer cleanup() - req, _ := http.NewRequest("DELETE", server.URL+"/api/v1/webhooks/99999", nil) - req.Header.Set("Authorization", "Bearer "+token) - - resp, _ := http.DefaultClient.Do(req) + resp := doRequestWithCheck(t, "DELETE", server.URL+"/api/v1/webhooks/99999", token, nil) defer resp.Body.Close() // Delete is idempotent - returns 200 even if not found @@ -390,11 +374,7 @@ func TestWebhookHandler_GetWebhookDeliveries_Success(t *testing.T) { "url": "https://example.com/webhook", "events": []string{"user.created"}, } - jsonBytes, _ := json.Marshal(createReq) - req, _ := http.NewRequest("POST", server.URL+"/api/v1/webhooks", bytes.NewReader(jsonBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - resp, _ := http.DefaultClient.Do(req) + resp := doRequestWithCheck(t, "POST", server.URL+"/api/v1/webhooks", token, createReq) var createResult map[string]interface{} json.NewDecoder(resp.Body).Decode(&createResult) resp.Body.Close() @@ -402,10 +382,7 @@ func TestWebhookHandler_GetWebhookDeliveries_Success(t *testing.T) { webhookID := createResult["data"].(map[string]interface{})["id"].(float64) // Get webhook deliveries - req, _ = http.NewRequest("GET", server.URL+fmt.Sprintf("/api/v1/webhooks/%.0f/deliveries?limit=20", webhookID), nil) - req.Header.Set("Authorization", "Bearer "+token) - - resp, _ = http.DefaultClient.Do(req) + resp = doRequestWithCheck(t, "GET", server.URL+fmt.Sprintf("/api/v1/webhooks/%.0f/deliveries?limit=20", webhookID), token, nil) defer resp.Body.Close() if resp.StatusCode != http.StatusOK { @@ -428,10 +405,7 @@ func TestWebhookHandler_GetWebhookDeliveries_InvalidID(t *testing.T) { server, _, token, cleanup := setupWebhookTestServer(t) defer cleanup() - req, _ := http.NewRequest("GET", server.URL+"/api/v1/webhooks/invalid/deliveries", nil) - req.Header.Set("Authorization", "Bearer "+token) - - resp, _ := http.DefaultClient.Do(req) + resp := doRequestWithCheck(t, "GET", server.URL+"/api/v1/webhooks/invalid/deliveries", token, nil) defer resp.Body.Close() if resp.StatusCode != http.StatusBadRequest { @@ -444,25 +418,19 @@ func TestWebhookHandler_ListWebhooks_Pagination(t *testing.T) { defer cleanup() // Create multiple webhooks + var resp *http.Response for i := 0; i < 3; i++ { reqBody := map[string]interface{}{ "name": fmt.Sprintf("Pagination Test Webhook %d", i), "url": "https://example.com/webhook", "events": []string{"user.created"}, } - jsonBytes, _ := json.Marshal(reqBody) - req, _ := http.NewRequest("POST", server.URL+"/api/v1/webhooks", bytes.NewReader(jsonBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - resp, _ := http.DefaultClient.Do(req) + resp = doRequestWithCheck(t, "POST", server.URL+"/api/v1/webhooks", token, reqBody) resp.Body.Close() } // Test pagination - req, _ := http.NewRequest("GET", server.URL+"/api/v1/webhooks?page=1&page_size=2", nil) - req.Header.Set("Authorization", "Bearer "+token) - - resp, _ := http.DefaultClient.Do(req) + resp = doRequestWithCheck(t, "GET", server.URL+"/api/v1/webhooks?page=1&page_size=2", token, nil) defer resp.Body.Close() var result map[string]interface{}