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
This commit is contained in:
@@ -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 测试场景扩展
|
||||
|
||||
@@ -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{}
|
||||
|
||||
Reference in New Issue
Block a user