diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 81acf9b7..07e205c3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,4 @@ repos: - - repo: local - hooks: - - id: python-compile - name: python compile check - entry: tools/python_compile_check.sh - language: script - files: ".*\\.py$" - pass_filenames: false - always_run: true - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: diff --git a/backend/.golangci.yml b/backend/.golangci.yml index 27de4874..9f46e9d2 100644 --- a/backend/.golangci.yml +++ b/backend/.golangci.yml @@ -20,6 +20,9 @@ linters: enabled-tags: - diagnostic - performance + - style + - opinionated + - experimental disabled-checks: - whyNoLint - wrapperFunc diff --git a/backend/cmd/api/main.go b/backend/cmd/api/main.go index 64df9f1a..c12c02d8 100644 --- a/backend/cmd/api/main.go +++ b/backend/cmd/api/main.go @@ -23,10 +23,10 @@ import ( func main() { // Setup logging with rotation logDir := "/app/data/logs" - if err := os.MkdirAll(logDir, 0755); err != nil { + if err := os.MkdirAll(logDir, 0o755); err != nil { // Fallback to local directory if /app/data fails (e.g. local dev) logDir = "data/logs" - _ = os.MkdirAll(logDir, 0755) + _ = os.MkdirAll(logDir, 0o755) } logFile := filepath.Join(logDir, "charon.log") diff --git a/backend/internal/api/handlers/access_list_handler_coverage_test.go b/backend/internal/api/handlers/access_list_handler_coverage_test.go index c234b7b1..ad50fd9f 100644 --- a/backend/internal/api/handlers/access_list_handler_coverage_test.go +++ b/backend/internal/api/handlers/access_list_handler_coverage_test.go @@ -16,7 +16,7 @@ import ( func TestAccessListHandler_Get_InvalidID(t *testing.T) { router, _ := setupAccessListTestRouter(t) - req := httptest.NewRequest(http.MethodGet, "/access-lists/invalid", nil) + req := httptest.NewRequest(http.MethodGet, "/access-lists/invalid", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -53,7 +53,7 @@ func TestAccessListHandler_Update_InvalidJSON(t *testing.T) { func TestAccessListHandler_Delete_InvalidID(t *testing.T) { router, _ := setupAccessListTestRouter(t) - req := httptest.NewRequest(http.MethodDelete, "/access-lists/invalid", nil) + req := httptest.NewRequest(http.MethodDelete, "/access-lists/invalid", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -98,7 +98,7 @@ func TestAccessListHandler_List_DBError(t *testing.T) { handler := NewAccessListHandler(db) router.GET("/access-lists", handler.List) - req := httptest.NewRequest(http.MethodGet, "/access-lists", nil) + req := httptest.NewRequest(http.MethodGet, "/access-lists", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -115,7 +115,7 @@ func TestAccessListHandler_Get_DBError(t *testing.T) { handler := NewAccessListHandler(db) router.GET("/access-lists/:id", handler.Get) - req := httptest.NewRequest(http.MethodGet, "/access-lists/1", nil) + req := httptest.NewRequest(http.MethodGet, "/access-lists/1", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -138,7 +138,7 @@ func TestAccessListHandler_Delete_InternalError(t *testing.T) { acl := models.AccessList{UUID: "test-uuid", Name: "Test", Type: "whitelist"} db.Create(&acl) - req := httptest.NewRequest(http.MethodDelete, "/access-lists/1", nil) + req := httptest.NewRequest(http.MethodDelete, "/access-lists/1", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/access_list_handler_test.go b/backend/internal/api/handlers/access_list_handler_test.go index ad795183..51a84ea1 100644 --- a/backend/internal/api/handlers/access_list_handler_test.go +++ b/backend/internal/api/handlers/access_list_handler_test.go @@ -129,7 +129,7 @@ func TestAccessListHandler_List(t *testing.T) { db.Create(&acls[i]) } - req := httptest.NewRequest(http.MethodGet, "/access-lists", nil) + req := httptest.NewRequest(http.MethodGet, "/access-lists", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -173,7 +173,7 @@ func TestAccessListHandler_Get(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/access-lists/"+tt.id, nil) + req := httptest.NewRequest(http.MethodGet, "/access-lists/"+tt.id, http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -313,7 +313,7 @@ func TestAccessListHandler_Delete(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - req := httptest.NewRequest(http.MethodDelete, "/access-lists/"+tt.id, nil) + req := httptest.NewRequest(http.MethodDelete, "/access-lists/"+tt.id, http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -393,7 +393,7 @@ func TestAccessListHandler_TestIP(t *testing.T) { func TestAccessListHandler_GetTemplates(t *testing.T) { router, _ := setupAccessListTestRouter(t) - req := httptest.NewRequest(http.MethodGet, "/access-lists/templates", nil) + req := httptest.NewRequest(http.MethodGet, "/access-lists/templates", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/additional_coverage_test.go b/backend/internal/api/handlers/additional_coverage_test.go index 660b59c8..15aa1a5b 100644 --- a/backend/internal/api/handlers/additional_coverage_test.go +++ b/backend/internal/api/handlers/additional_coverage_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "mime/multipart" + "net/http" "net/http/httptest" "os" "path/filepath" @@ -143,7 +144,7 @@ func TestSecurityHandler_GetConfig_InternalError(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/security/config", nil) + c.Request = httptest.NewRequest("GET", "/security/config", http.NoBody) h.GetConfig(c) @@ -186,7 +187,7 @@ func TestSecurityHandler_GenerateBreakGlass_Error(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("POST", "/security/breakglass", nil) + c.Request = httptest.NewRequest("POST", "/security/breakglass", http.NoBody) h.GenerateBreakGlass(c) @@ -205,7 +206,7 @@ func TestSecurityHandler_ListDecisions_Error(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/security/decisions", nil) + c.Request = httptest.NewRequest("GET", "/security/decisions", http.NoBody) h.ListDecisions(c) @@ -224,7 +225,7 @@ func TestSecurityHandler_ListRuleSets_Error(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/security/rulesets", nil) + c.Request = httptest.NewRequest("GET", "/security/rulesets", http.NoBody) h.ListRuleSets(c) @@ -445,19 +446,19 @@ func TestImportHandler_UploadMulti_PathTraversal(t *testing.T) { // Logs Handler Download error coverage -func setupLogsDownloadTest(t *testing.T) (*LogsHandler, string) { +func setupLogsDownloadTest(t *testing.T) (h *LogsHandler, logsDir string) { t.Helper() tmpDir := t.TempDir() dataDir := filepath.Join(tmpDir, "data") os.MkdirAll(dataDir, 0o755) - logsDir := filepath.Join(dataDir, "logs") + logsDir = filepath.Join(dataDir, "logs") os.MkdirAll(logsDir, 0o755) dbPath := filepath.Join(dataDir, "charon.db") cfg := &config.Config{DatabasePath: dbPath} svc := services.NewLogService(cfg) - h := NewLogsHandler(svc) + h = NewLogsHandler(svc) return h, logsDir } @@ -469,7 +470,7 @@ func TestLogsHandler_Download_PathTraversal(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "filename", Value: "../../../etc/passwd"}} - c.Request = httptest.NewRequest("GET", "/logs/../../../etc/passwd/download", nil) + c.Request = httptest.NewRequest("GET", "/logs/../../../etc/passwd/download", http.NoBody) h.Download(c) @@ -484,7 +485,7 @@ func TestLogsHandler_Download_NotFound(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "filename", Value: "nonexistent.log"}} - c.Request = httptest.NewRequest("GET", "/logs/nonexistent.log/download", nil) + c.Request = httptest.NewRequest("GET", "/logs/nonexistent.log/download", http.NoBody) h.Download(c) @@ -502,7 +503,7 @@ func TestLogsHandler_Download_Success(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "filename", Value: "test.log"}} - c.Request = httptest.NewRequest("GET", "/logs/test.log/download", nil) + c.Request = httptest.NewRequest("GET", "/logs/test.log/download", http.NoBody) h.Download(c) @@ -574,7 +575,7 @@ func TestBackupHandler_List_ServiceError(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/backups", nil) + c.Request = httptest.NewRequest("GET", "/backups", http.NoBody) h.List(c) @@ -602,7 +603,7 @@ func TestBackupHandler_Delete_PathTraversal(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "filename", Value: "../../../etc/passwd"}} - c.Request = httptest.NewRequest("DELETE", "/backups/../../../etc/passwd", nil) + c.Request = httptest.NewRequest("DELETE", "/backups/../../../etc/passwd", http.NoBody) h.Delete(c) @@ -641,7 +642,7 @@ func TestBackupHandler_Delete_InternalError2(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "filename", Value: "test.zip"}} - c.Request = httptest.NewRequest("DELETE", "/backups/test.zip", nil) + c.Request = httptest.NewRequest("DELETE", "/backups/test.zip", http.NoBody) h.Delete(c) @@ -722,7 +723,7 @@ func TestHealthHandler_Basic(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/health", nil) + c.Request = httptest.NewRequest("GET", "/health", http.NoBody) HealthHandler(c) @@ -753,7 +754,7 @@ func TestBackupHandler_Create_Error(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("POST", "/backups", nil) + c.Request = httptest.NewRequest("POST", "/backups", http.NoBody) h.Create(c) @@ -782,7 +783,7 @@ func TestSettingsHandler_GetSettings_Error(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/settings", nil) + c.Request = httptest.NewRequest("GET", "/settings", http.NoBody) h.GetSettings(c) diff --git a/backend/internal/api/handlers/auth_handler_test.go b/backend/internal/api/handlers/auth_handler_test.go index 878821ba..77340c13 100644 --- a/backend/internal/api/handlers/auth_handler_test.go +++ b/backend/internal/api/handlers/auth_handler_test.go @@ -137,7 +137,7 @@ func TestAuthHandler_Logout(t *testing.T) { r := gin.New() r.POST("/logout", handler.Logout) - req := httptest.NewRequest("POST", "/logout", nil) + req := httptest.NewRequest("POST", "/logout", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -171,7 +171,7 @@ func TestAuthHandler_Me(t *testing.T) { }) r.GET("/me", handler.Me) - req := httptest.NewRequest("GET", "/me", nil) + req := httptest.NewRequest("GET", "/me", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -194,7 +194,7 @@ func TestAuthHandler_Me_NotFound(t *testing.T) { }) r.GET("/me", handler.Me) - req := httptest.NewRequest("GET", "/me", nil) + req := httptest.NewRequest("GET", "/me", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -319,7 +319,7 @@ func TestAuthHandler_Verify_NoCookie(t *testing.T) { r := gin.New() r.GET("/verify", handler.Verify) - req := httptest.NewRequest("GET", "/verify", nil) + req := httptest.NewRequest("GET", "/verify", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -333,7 +333,7 @@ func TestAuthHandler_Verify_InvalidToken(t *testing.T) { r := gin.New() r.GET("/verify", handler.Verify) - req := httptest.NewRequest("GET", "/verify", nil) + req := httptest.NewRequest("GET", "/verify", http.NoBody) req.AddCookie(&http.Cookie{Name: "auth_token", Value: "invalid-token"}) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -362,7 +362,7 @@ func TestAuthHandler_Verify_ValidToken(t *testing.T) { r := gin.New() r.GET("/verify", handler.Verify) - req := httptest.NewRequest("GET", "/verify", nil) + req := httptest.NewRequest("GET", "/verify", http.NoBody) req.AddCookie(&http.Cookie{Name: "auth_token", Value: token}) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -391,7 +391,7 @@ func TestAuthHandler_Verify_BearerToken(t *testing.T) { r := gin.New() r.GET("/verify", handler.Verify) - req := httptest.NewRequest("GET", "/verify", nil) + req := httptest.NewRequest("GET", "/verify", http.NoBody) req.Header.Set("Authorization", "Bearer "+token) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -420,7 +420,7 @@ func TestAuthHandler_Verify_DisabledUser(t *testing.T) { r := gin.New() r.GET("/verify", handler.Verify) - req := httptest.NewRequest("GET", "/verify", nil) + req := httptest.NewRequest("GET", "/verify", http.NoBody) req.AddCookie(&http.Cookie{Name: "auth_token", Value: token}) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -459,7 +459,7 @@ func TestAuthHandler_Verify_ForwardAuthDenied(t *testing.T) { r := gin.New() r.GET("/verify", handler.Verify) - req := httptest.NewRequest("GET", "/verify", nil) + req := httptest.NewRequest("GET", "/verify", http.NoBody) req.AddCookie(&http.Cookie{Name: "auth_token", Value: token}) req.Header.Set("X-Forwarded-Host", "app.example.com") w := httptest.NewRecorder() @@ -474,7 +474,7 @@ func TestAuthHandler_VerifyStatus_NotAuthenticated(t *testing.T) { r := gin.New() r.GET("/status", handler.VerifyStatus) - req := httptest.NewRequest("GET", "/status", nil) + req := httptest.NewRequest("GET", "/status", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -490,7 +490,7 @@ func TestAuthHandler_VerifyStatus_InvalidToken(t *testing.T) { r := gin.New() r.GET("/status", handler.VerifyStatus) - req := httptest.NewRequest("GET", "/status", nil) + req := httptest.NewRequest("GET", "/status", http.NoBody) req.AddCookie(&http.Cookie{Name: "auth_token", Value: "invalid"}) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -520,7 +520,7 @@ func TestAuthHandler_VerifyStatus_Authenticated(t *testing.T) { r := gin.New() r.GET("/status", handler.VerifyStatus) - req := httptest.NewRequest("GET", "/status", nil) + req := httptest.NewRequest("GET", "/status", http.NoBody) req.AddCookie(&http.Cookie{Name: "auth_token", Value: token}) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -553,7 +553,7 @@ func TestAuthHandler_VerifyStatus_DisabledUser(t *testing.T) { r := gin.New() r.GET("/status", handler.VerifyStatus) - req := httptest.NewRequest("GET", "/status", nil) + req := httptest.NewRequest("GET", "/status", http.NoBody) req.AddCookie(&http.Cookie{Name: "auth_token", Value: token}) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -570,7 +570,7 @@ func TestAuthHandler_GetAccessibleHosts_Unauthorized(t *testing.T) { r := gin.New() r.GET("/hosts", handler.GetAccessibleHosts) - req := httptest.NewRequest("GET", "/hosts", nil) + req := httptest.NewRequest("GET", "/hosts", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -604,7 +604,7 @@ func TestAuthHandler_GetAccessibleHosts_AllowAll(t *testing.T) { }) r.GET("/hosts", handler.GetAccessibleHosts) - req := httptest.NewRequest("GET", "/hosts", nil) + req := httptest.NewRequest("GET", "/hosts", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -640,7 +640,7 @@ func TestAuthHandler_GetAccessibleHosts_DenyAll(t *testing.T) { }) r.GET("/hosts", handler.GetAccessibleHosts) - req := httptest.NewRequest("GET", "/hosts", nil) + req := httptest.NewRequest("GET", "/hosts", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -679,7 +679,7 @@ func TestAuthHandler_GetAccessibleHosts_PermittedHosts(t *testing.T) { }) r.GET("/hosts", handler.GetAccessibleHosts) - req := httptest.NewRequest("GET", "/hosts", nil) + req := httptest.NewRequest("GET", "/hosts", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -701,7 +701,7 @@ func TestAuthHandler_GetAccessibleHosts_UserNotFound(t *testing.T) { }) r.GET("/hosts", handler.GetAccessibleHosts) - req := httptest.NewRequest("GET", "/hosts", nil) + req := httptest.NewRequest("GET", "/hosts", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -714,7 +714,7 @@ func TestAuthHandler_CheckHostAccess_Unauthorized(t *testing.T) { r := gin.New() r.GET("/hosts/:hostId/access", handler.CheckHostAccess) - req := httptest.NewRequest("GET", "/hosts/1/access", nil) + req := httptest.NewRequest("GET", "/hosts/1/access", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -735,7 +735,7 @@ func TestAuthHandler_CheckHostAccess_InvalidHostID(t *testing.T) { }) r.GET("/hosts/:hostId/access", handler.CheckHostAccess) - req := httptest.NewRequest("GET", "/hosts/invalid/access", nil) + req := httptest.NewRequest("GET", "/hosts/invalid/access", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -764,7 +764,7 @@ func TestAuthHandler_CheckHostAccess_Allowed(t *testing.T) { }) r.GET("/hosts/:hostId/access", handler.CheckHostAccess) - req := httptest.NewRequest("GET", "/hosts/1/access", nil) + req := httptest.NewRequest("GET", "/hosts/1/access", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -796,7 +796,7 @@ func TestAuthHandler_CheckHostAccess_Denied(t *testing.T) { }) r.GET("/hosts/:hostId/access", handler.CheckHostAccess) - req := httptest.NewRequest("GET", "/hosts/1/access", nil) + req := httptest.NewRequest("GET", "/hosts/1/access", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/backup_handler_sanitize_test.go b/backend/internal/api/handlers/backup_handler_sanitize_test.go index 0e772525..ecfb1fec 100644 --- a/backend/internal/api/handlers/backup_handler_sanitize_test.go +++ b/backend/internal/api/handlers/backup_handler_sanitize_test.go @@ -39,7 +39,7 @@ func TestBackupHandlerSanitizesFilename(t *testing.T) { // Create a malicious filename with newline and path components malicious := "../evil\nname" - c.Request = httptest.NewRequest(http.MethodGet, "/backups/"+strings.ReplaceAll(malicious, "\n", "%0A")+"/restore", nil) + c.Request = httptest.NewRequest(http.MethodGet, "/backups/"+strings.ReplaceAll(malicious, "\n", "%0A")+"/restore", http.NoBody) // Call handler directly with the test context h.Restore(c) diff --git a/backend/internal/api/handlers/backup_handler_test.go b/backend/internal/api/handlers/backup_handler_test.go index a13ba871..5daa4f37 100644 --- a/backend/internal/api/handlers/backup_handler_test.go +++ b/backend/internal/api/handlers/backup_handler_test.go @@ -31,12 +31,12 @@ func setupBackupTest(t *testing.T) (*gin.Engine, *services.BackupService, string // So if DatabasePath is /tmp/data/charon.db, DataDir is /tmp/data, BackupDir is /tmp/data/backups. dataDir := filepath.Join(tmpDir, "data") - err = os.MkdirAll(dataDir, 0755) + err = os.MkdirAll(dataDir, 0o755) require.NoError(t, err) dbPath := filepath.Join(dataDir, "charon.db") // Create a dummy DB file to back up - err = os.WriteFile(dbPath, []byte("dummy db content"), 0644) + err = os.WriteFile(dbPath, []byte("dummy db content"), 0o644) require.NoError(t, err) cfg := &config.Config{ @@ -72,7 +72,7 @@ func TestBackupLifecycle(t *testing.T) { defer os.RemoveAll(tmpDir) // 1. List backups (should be empty) - req := httptest.NewRequest(http.MethodGet, "/api/v1/backups", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/backups", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) @@ -80,7 +80,7 @@ func TestBackupLifecycle(t *testing.T) { // ... // 2. Create backup - req = httptest.NewRequest(http.MethodPost, "/api/v1/backups", nil) + req = httptest.NewRequest(http.MethodPost, "/api/v1/backups", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusCreated, resp.Code) @@ -92,20 +92,20 @@ func TestBackupLifecycle(t *testing.T) { require.NotEmpty(t, filename) // 3. List backups (should have 1) - req = httptest.NewRequest(http.MethodGet, "/api/v1/backups", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/backups", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) // Verify list contains filename // 4. Restore backup - req = httptest.NewRequest(http.MethodPost, "/api/v1/backups/"+filename+"/restore", nil) + req = httptest.NewRequest(http.MethodPost, "/api/v1/backups/"+filename+"/restore", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) // 5. Download backup - req = httptest.NewRequest(http.MethodGet, "/api/v1/backups/"+filename+"/download", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/backups/"+filename+"/download", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) @@ -113,13 +113,13 @@ func TestBackupLifecycle(t *testing.T) { // require.Equal(t, "application/zip", resp.Header().Get("Content-Type")) // 6. Delete backup - req = httptest.NewRequest(http.MethodDelete, "/api/v1/backups/"+filename, nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/backups/"+filename, http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) // 7. List backups (should be empty again) - req = httptest.NewRequest(http.MethodGet, "/api/v1/backups", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/backups", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) @@ -128,19 +128,19 @@ func TestBackupLifecycle(t *testing.T) { require.Empty(t, list) // 8. Delete non-existent backup - req = httptest.NewRequest(http.MethodDelete, "/api/v1/backups/missing.zip", nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/backups/missing.zip", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusNotFound, resp.Code) // 9. Restore non-existent backup - req = httptest.NewRequest(http.MethodPost, "/api/v1/backups/missing.zip/restore", nil) + req = httptest.NewRequest(http.MethodPost, "/api/v1/backups/missing.zip/restore", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusNotFound, resp.Code) // 10. Download non-existent backup - req = httptest.NewRequest(http.MethodGet, "/api/v1/backups/missing.zip/download", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/backups/missing.zip/download", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusNotFound, resp.Code) @@ -154,7 +154,7 @@ func TestBackupHandler_Errors(t *testing.T) { // Note: Service now handles missing dir gracefully by returning empty list os.RemoveAll(svc.BackupDir) - req := httptest.NewRequest(http.MethodGet, "/api/v1/backups", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/backups", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) @@ -163,7 +163,7 @@ func TestBackupHandler_Errors(t *testing.T) { require.Empty(t, list) // 4. Delete Error (Not Found) - req = httptest.NewRequest(http.MethodDelete, "/api/v1/backups/missing.zip", nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/backups/missing.zip", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusNotFound, resp.Code) @@ -174,13 +174,13 @@ func TestBackupHandler_List_Success(t *testing.T) { defer os.RemoveAll(tmpDir) // Create a backup first - req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusCreated, resp.Code) // Now list should return it - req = httptest.NewRequest(http.MethodGet, "/api/v1/backups", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/backups", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) @@ -196,7 +196,7 @@ func TestBackupHandler_Create_Success(t *testing.T) { router, _, tmpDir := setupBackupTest(t) defer os.RemoveAll(tmpDir) - req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusCreated, resp.Code) @@ -212,7 +212,7 @@ func TestBackupHandler_Download_Success(t *testing.T) { defer os.RemoveAll(tmpDir) // Create backup - req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusCreated, resp.Code) @@ -222,7 +222,7 @@ func TestBackupHandler_Download_Success(t *testing.T) { filename := result["filename"] // Download it - req = httptest.NewRequest(http.MethodGet, "/api/v1/backups/"+filename+"/download", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/backups/"+filename+"/download", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) @@ -234,19 +234,19 @@ func TestBackupHandler_PathTraversal(t *testing.T) { defer os.RemoveAll(tmpDir) // Try path traversal in Delete - req := httptest.NewRequest(http.MethodDelete, "/api/v1/backups/../../../etc/passwd", nil) + req := httptest.NewRequest(http.MethodDelete, "/api/v1/backups/../../../etc/passwd", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusNotFound, resp.Code) // Try path traversal in Download - req = httptest.NewRequest(http.MethodGet, "/api/v1/backups/../../../etc/passwd/download", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/backups/../../../etc/passwd/download", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Contains(t, []int{http.StatusBadRequest, http.StatusNotFound}, resp.Code) // Try path traversal in Restore - req = httptest.NewRequest(http.MethodPost, "/api/v1/backups/../../../etc/passwd/restore", nil) + req = httptest.NewRequest(http.MethodPost, "/api/v1/backups/../../../etc/passwd/restore", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusNotFound, resp.Code) @@ -257,7 +257,7 @@ func TestBackupHandler_Download_InvalidPath(t *testing.T) { defer os.RemoveAll(tmpDir) // Request with path traversal attempt - req := httptest.NewRequest(http.MethodGet, "/api/v1/backups/../invalid/download", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/backups/../invalid/download", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) // Should be BadRequest due to path validation failure @@ -269,10 +269,10 @@ func TestBackupHandler_Create_ServiceError(t *testing.T) { defer os.RemoveAll(tmpDir) // Remove write permissions on backup dir to force create error - os.Chmod(svc.BackupDir, 0444) - defer os.Chmod(svc.BackupDir, 0755) + os.Chmod(svc.BackupDir, 0o444) + defer os.Chmod(svc.BackupDir, 0o755) - req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) // Should fail with 500 due to permission error @@ -284,7 +284,7 @@ func TestBackupHandler_Delete_InternalError(t *testing.T) { defer os.RemoveAll(tmpDir) // Create a backup first - req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusCreated, resp.Code) @@ -294,10 +294,10 @@ func TestBackupHandler_Delete_InternalError(t *testing.T) { filename := result["filename"] // Make backup dir read-only to cause delete error (not NotExist) - os.Chmod(svc.BackupDir, 0444) - defer os.Chmod(svc.BackupDir, 0755) + os.Chmod(svc.BackupDir, 0o444) + defer os.Chmod(svc.BackupDir, 0o755) - req = httptest.NewRequest(http.MethodDelete, "/api/v1/backups/"+filename, nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/backups/"+filename, http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) // Should fail with 500 due to permission error (not 404) @@ -309,7 +309,7 @@ func TestBackupHandler_Restore_InternalError(t *testing.T) { defer os.RemoveAll(tmpDir) // Create a backup first - req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/backups", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusCreated, resp.Code) @@ -319,10 +319,10 @@ func TestBackupHandler_Restore_InternalError(t *testing.T) { filename := result["filename"] // Make data dir read-only to cause restore error - os.Chmod(svc.DataDir, 0444) - defer os.Chmod(svc.DataDir, 0755) + os.Chmod(svc.DataDir, 0o444) + defer os.Chmod(svc.DataDir, 0o755) - req = httptest.NewRequest(http.MethodPost, "/api/v1/backups/"+filename+"/restore", nil) + req = httptest.NewRequest(http.MethodPost, "/api/v1/backups/"+filename+"/restore", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) // Should fail with 500 due to permission error diff --git a/backend/internal/api/handlers/benchmark_test.go b/backend/internal/api/handlers/benchmark_test.go index 9b96c0ed..1bee57d8 100644 --- a/backend/internal/api/handlers/benchmark_test.go +++ b/backend/internal/api/handlers/benchmark_test.go @@ -70,7 +70,7 @@ func BenchmarkSecurityHandler_GetStatus(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { @@ -93,7 +93,7 @@ func BenchmarkSecurityHandler_GetStatus_NoSettings(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { @@ -126,7 +126,7 @@ func BenchmarkSecurityHandler_ListDecisions(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - req := httptest.NewRequest("GET", "/api/v1/security/decisions?limit=50", nil) + req := httptest.NewRequest("GET", "/api/v1/security/decisions?limit=50", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { @@ -159,7 +159,7 @@ func BenchmarkSecurityHandler_ListRuleSets(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - req := httptest.NewRequest("GET", "/api/v1/security/rulesets", nil) + req := httptest.NewRequest("GET", "/api/v1/security/rulesets", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { @@ -254,7 +254,7 @@ func BenchmarkSecurityHandler_GetConfig(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - req := httptest.NewRequest("GET", "/api/v1/security/config", nil) + req := httptest.NewRequest("GET", "/api/v1/security/config", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { @@ -323,7 +323,7 @@ func BenchmarkSecurityHandler_GetStatus_Parallel(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { @@ -366,7 +366,7 @@ func BenchmarkSecurityHandler_ListDecisions_Parallel(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { - req := httptest.NewRequest("GET", "/api/v1/security/decisions?limit=50", nil) + req := httptest.NewRequest("GET", "/api/v1/security/decisions?limit=50", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { @@ -453,7 +453,7 @@ func BenchmarkSecurityHandler_ManySettingsLookups(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { diff --git a/backend/internal/api/handlers/certificate_handler.go b/backend/internal/api/handlers/certificate_handler.go index 24c3ab67..08cb6bf7 100644 --- a/backend/internal/api/handlers/certificate_handler.go +++ b/backend/internal/api/handlers/certificate_handler.go @@ -86,14 +86,22 @@ func (h *CertificateHandler) Upload(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to open cert file"}) return } - defer func() { _ = certSrc.Close() }() + defer func() { + if err := certSrc.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close certificate file") + } + }() keySrc, err := keyFile.Open() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to open key file"}) return } - defer func() { _ = keySrc.Close() }() + defer func() { + if err := keySrc.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close key file") + } + }() // Read to string // Limit size to avoid DoS (e.g. 1MB) diff --git a/backend/internal/api/handlers/certificate_handler_coverage_test.go b/backend/internal/api/handlers/certificate_handler_coverage_test.go index f6a00be7..8151c588 100644 --- a/backend/internal/api/handlers/certificate_handler_coverage_test.go +++ b/backend/internal/api/handlers/certificate_handler_coverage_test.go @@ -26,7 +26,7 @@ func TestCertificateHandler_List_DBError(t *testing.T) { h := NewCertificateHandler(svc, nil, nil) r.GET("/api/certificates", h.List) - req := httptest.NewRequest(http.MethodGet, "/api/certificates", nil) + req := httptest.NewRequest(http.MethodGet, "/api/certificates", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -44,7 +44,7 @@ func TestCertificateHandler_Delete_InvalidID(t *testing.T) { h := NewCertificateHandler(svc, nil, nil) r.DELETE("/api/certificates/:id", h.Delete) - req := httptest.NewRequest(http.MethodDelete, "/api/certificates/invalid", nil) + req := httptest.NewRequest(http.MethodDelete, "/api/certificates/invalid", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -63,7 +63,7 @@ func TestCertificateHandler_Delete_NotFound(t *testing.T) { h := NewCertificateHandler(svc, nil, nil) r.DELETE("/api/certificates/:id", h.Delete) - req := httptest.NewRequest(http.MethodDelete, "/api/certificates/9999", nil) + req := httptest.NewRequest(http.MethodDelete, "/api/certificates/9999", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -99,7 +99,7 @@ func TestCertificateHandler_Delete_NoBackupService(t *testing.T) { h := NewCertificateHandler(svc, nil, nil) r.DELETE("/api/certificates/:id", h.Delete) - req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), nil) + req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -124,7 +124,7 @@ func TestCertificateHandler_Delete_CheckUsageDBError(t *testing.T) { h := NewCertificateHandler(svc, nil, nil) r.DELETE("/api/certificates/:id", h.Delete) - req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), nil) + req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -147,7 +147,7 @@ func TestCertificateHandler_List_WithCertificates(t *testing.T) { h := NewCertificateHandler(svc, nil, nil) r.GET("/api/certificates", h.List) - req := httptest.NewRequest(http.MethodGet, "/api/certificates", nil) + req := httptest.NewRequest(http.MethodGet, "/api/certificates", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/certificate_handler_security_test.go b/backend/internal/api/handlers/certificate_handler_security_test.go index 351098b8..275a5cfa 100644 --- a/backend/internal/api/handlers/certificate_handler_security_test.go +++ b/backend/internal/api/handlers/certificate_handler_security_test.go @@ -35,7 +35,7 @@ func TestCertificateHandler_Delete_RequiresAuth(t *testing.T) { h := NewCertificateHandler(svc, nil, nil) r.DELETE("/api/certificates/:id", h.Delete) - req := httptest.NewRequest(http.MethodDelete, "/api/certificates/1", nil) + req := httptest.NewRequest(http.MethodDelete, "/api/certificates/1", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -65,7 +65,7 @@ func TestCertificateHandler_List_RequiresAuth(t *testing.T) { h := NewCertificateHandler(svc, nil, nil) r.GET("/api/certificates", h.List) - req := httptest.NewRequest(http.MethodGet, "/api/certificates", nil) + req := httptest.NewRequest(http.MethodGet, "/api/certificates", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -95,7 +95,7 @@ func TestCertificateHandler_Upload_RequiresAuth(t *testing.T) { h := NewCertificateHandler(svc, nil, nil) r.POST("/api/certificates", h.Upload) - req := httptest.NewRequest(http.MethodPost, "/api/certificates", nil) + req := httptest.NewRequest(http.MethodPost, "/api/certificates", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -141,7 +141,7 @@ func TestCertificateHandler_Delete_DiskSpaceCheck(t *testing.T) { h := NewCertificateHandler(svc, mockBackup, nil) r.DELETE("/api/certificates/:id", h.Delete) - req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/certificates/%d", cert.ID), nil) + req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/certificates/%d", cert.ID), http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -186,7 +186,7 @@ func TestCertificateHandler_Delete_NotificationRateLimiting(t *testing.T) { r.DELETE("/api/certificates/:id", h.Delete) // Delete first cert - req1 := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/certificates/%d", cert1.ID), nil) + req1 := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/certificates/%d", cert1.ID), http.NoBody) w1 := httptest.NewRecorder() r.ServeHTTP(w1, req1) @@ -195,7 +195,7 @@ func TestCertificateHandler_Delete_NotificationRateLimiting(t *testing.T) { } // Delete second cert (different ID, should not be rate limited) - req2 := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/certificates/%d", cert2.ID), nil) + req2 := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/certificates/%d", cert2.ID), http.NoBody) w2 := httptest.NewRecorder() r.ServeHTTP(w2, req2) diff --git a/backend/internal/api/handlers/certificate_handler_test.go b/backend/internal/api/handlers/certificate_handler_test.go index a41fed6f..2559f5a9 100644 --- a/backend/internal/api/handlers/certificate_handler_test.go +++ b/backend/internal/api/handlers/certificate_handler_test.go @@ -70,7 +70,7 @@ func TestDeleteCertificate_InUse(t *testing.T) { r := setupCertTestRouter(t, db) - req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), nil) + req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -117,7 +117,7 @@ func TestDeleteCertificate_CreatesBackup(t *testing.T) { h := NewCertificateHandler(svc, mockBackupService, nil) r.DELETE("/api/certificates/:id", h.Delete) - req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), nil) + req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -169,7 +169,7 @@ func TestDeleteCertificate_BackupFailure(t *testing.T) { h := NewCertificateHandler(svc, mockBackupService, nil) r.DELETE("/api/certificates/:id", h.Delete) - req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), nil) + req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -225,7 +225,7 @@ func TestDeleteCertificate_InUse_NoBackup(t *testing.T) { h := NewCertificateHandler(svc, mockBackupService, nil) r.DELETE("/api/certificates/:id", h.Delete) - req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), nil) + req := httptest.NewRequest(http.MethodDelete, "/api/certificates/"+toStr(cert.ID), http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -294,7 +294,7 @@ func TestCertificateHandler_List(t *testing.T) { h := NewCertificateHandler(svc, nil, nil) r.GET("/api/certificates", h.List) - req := httptest.NewRequest(http.MethodGet, "/api/certificates", nil) + req := httptest.NewRequest(http.MethodGet, "/api/certificates", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -436,7 +436,7 @@ func TestCertificateHandler_Upload_Success(t *testing.T) { } } -func generateSelfSignedCertPEM() (string, string, error) { +func generateSelfSignedCertPEM() (certPEM, keyPEM string, err error) { // generate RSA key priv, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { @@ -458,11 +458,13 @@ func generateSelfSignedCertPEM() (string, string, error) { if err != nil { return "", "", err } - certPEM := new(bytes.Buffer) - pem.Encode(certPEM, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - keyPEM := new(bytes.Buffer) - pem.Encode(keyPEM, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) - return certPEM.String(), keyPEM.String(), nil + certBuf := new(bytes.Buffer) + pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + keyBuf := new(bytes.Buffer) + pem.Encode(keyBuf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) + certPEM = certBuf.String() + keyPEM = keyBuf.String() + return certPEM, keyPEM, nil } // Note: mockCertificateService removed — helper tests now use real service instances or testify mocks inlined where required. diff --git a/backend/internal/api/handlers/coverage_quick_test.go b/backend/internal/api/handlers/coverage_quick_test.go index d8d5cc35..9e067aa2 100644 --- a/backend/internal/api/handlers/coverage_quick_test.go +++ b/backend/internal/api/handlers/coverage_quick_test.go @@ -36,7 +36,7 @@ func TestBackupHandlerQuick(t *testing.T) { // List w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/backups", nil) + req := httptest.NewRequest(http.MethodGet, "/backups", http.NoBody) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d", w.Code) @@ -44,7 +44,7 @@ func TestBackupHandlerQuick(t *testing.T) { // Create (backup) w2 := httptest.NewRecorder() - req2 := httptest.NewRequest(http.MethodPost, "/backups", nil) + req2 := httptest.NewRequest(http.MethodPost, "/backups", http.NoBody) r.ServeHTTP(w2, req2) if w2.Code != http.StatusCreated { t.Fatalf("create expected 201 got %d", w2.Code) @@ -59,7 +59,7 @@ func TestBackupHandlerQuick(t *testing.T) { // Delete missing w3 := httptest.NewRecorder() - req3 := httptest.NewRequest(http.MethodDelete, "/backups/missing", nil) + req3 := httptest.NewRequest(http.MethodDelete, "/backups/missing", http.NoBody) r.ServeHTTP(w3, req3) if w3.Code != http.StatusNotFound { t.Fatalf("delete missing expected 404 got %d", w3.Code) @@ -67,7 +67,7 @@ func TestBackupHandlerQuick(t *testing.T) { // Download missing w4 := httptest.NewRecorder() - req4 := httptest.NewRequest(http.MethodGet, "/backups/missing", nil) + req4 := httptest.NewRequest(http.MethodGet, "/backups/missing", http.NoBody) r.ServeHTTP(w4, req4) if w4.Code != http.StatusNotFound { t.Fatalf("download missing expected 404 got %d", w4.Code) @@ -75,7 +75,7 @@ func TestBackupHandlerQuick(t *testing.T) { // Download present (use filename returned from create) w5 := httptest.NewRecorder() - req5 := httptest.NewRequest(http.MethodGet, "/backups/"+createResp.Filename, nil) + req5 := httptest.NewRequest(http.MethodGet, "/backups/"+createResp.Filename, http.NoBody) r.ServeHTTP(w5, req5) if w5.Code != http.StatusOK { t.Fatalf("download expected 200 got %d", w5.Code) @@ -83,7 +83,7 @@ func TestBackupHandlerQuick(t *testing.T) { // Restore missing w6 := httptest.NewRecorder() - req6 := httptest.NewRequest(http.MethodPost, "/backups/missing/restore", nil) + req6 := httptest.NewRequest(http.MethodPost, "/backups/missing/restore", http.NoBody) r.ServeHTTP(w6, req6) if w6.Code != http.StatusNotFound { t.Fatalf("restore missing expected 404 got %d", w6.Code) @@ -91,7 +91,7 @@ func TestBackupHandlerQuick(t *testing.T) { // Restore ok w7 := httptest.NewRecorder() - req7 := httptest.NewRequest(http.MethodPost, "/backups/"+createResp.Filename+"/restore", nil) + req7 := httptest.NewRequest(http.MethodPost, "/backups/"+createResp.Filename+"/restore", http.NoBody) r.ServeHTTP(w7, req7) if w7.Code != http.StatusOK { t.Fatalf("restore expected 200 got %d", w7.Code) diff --git a/backend/internal/api/handlers/crowdsec_decisions_test.go b/backend/internal/api/handlers/crowdsec_decisions_test.go index 3d8b48c7..26ba34bf 100644 --- a/backend/internal/api/handlers/crowdsec_decisions_test.go +++ b/backend/internal/api/handlers/crowdsec_decisions_test.go @@ -44,7 +44,7 @@ func TestListDecisions_Success(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/decisions", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/decisions", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -83,7 +83,7 @@ func TestListDecisions_EmptyList(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/decisions", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/decisions", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -114,7 +114,7 @@ func TestListDecisions_CscliError(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/decisions", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/decisions", http.NoBody) r.ServeHTTP(w, req) // Should return 200 with empty list and error message @@ -146,7 +146,7 @@ func TestListDecisions_InvalidJSON(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/decisions", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/decisions", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) @@ -339,7 +339,7 @@ func TestUnbanIP_Success(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodDelete, "/api/v1/admin/crowdsec/ban/192.168.1.100", nil) + req := httptest.NewRequest(http.MethodDelete, "/api/v1/admin/crowdsec/ban/192.168.1.100", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -373,7 +373,7 @@ func TestUnbanIP_CscliError(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodDelete, "/api/v1/admin/crowdsec/ban/192.168.1.100", nil) + req := httptest.NewRequest(http.MethodDelete, "/api/v1/admin/crowdsec/ban/192.168.1.100", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) @@ -401,7 +401,7 @@ func TestListDecisions_MultipleDecisions(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/decisions", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/decisions", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) diff --git a/backend/internal/api/handlers/crowdsec_exec.go b/backend/internal/api/handlers/crowdsec_exec.go index 5852018d..7214f418 100644 --- a/backend/internal/api/handlers/crowdsec_exec.go +++ b/backend/internal/api/handlers/crowdsec_exec.go @@ -2,6 +2,7 @@ package handlers import ( "context" + "errors" "fmt" "os" "os/exec" @@ -61,23 +62,33 @@ func (e *DefaultCrowdsecExecutor) Stop(ctx context.Context, configDir string) er return nil } -func (e *DefaultCrowdsecExecutor) Status(ctx context.Context, configDir string) (bool, int, error) { +func (e *DefaultCrowdsecExecutor) Status(ctx context.Context, configDir string) (running bool, pid int, err error) { b, err := os.ReadFile(e.pidFile(configDir)) if err != nil { + // Missing pid file is treated as not running return false, 0, nil } - pid, err := strconv.Atoi(string(b)) + + pid, err = strconv.Atoi(string(b)) if err != nil { + // Malformed pid file is treated as not running return false, 0, nil } - // Check process exists + proc, err := os.FindProcess(pid) if err != nil { + // Process lookup failures are treated as not running return false, pid, nil } + // Sending signal 0 is not portable on Windows, but OK for Linux containers - if err := proc.Signal(syscall.Signal(0)); err != nil { + if err = proc.Signal(syscall.Signal(0)); err != nil { + if errors.Is(err, os.ErrProcessDone) { + return false, pid, nil + } + // ESRCH or other errors mean process isn't running return false, pid, nil } + return true, pid, nil } diff --git a/backend/internal/api/handlers/crowdsec_handler.go b/backend/internal/api/handlers/crowdsec_handler.go index a3b98c80..7d17623e 100644 --- a/backend/internal/api/handlers/crowdsec_handler.go +++ b/backend/internal/api/handlers/crowdsec_handler.go @@ -20,19 +20,19 @@ import ( "gorm.io/gorm" ) -// Executor abstracts starting/stopping CrowdSec so tests can mock it. +// CrowdsecExecutor abstracts starting/stopping CrowdSec so tests can mock it. type CrowdsecExecutor interface { Start(ctx context.Context, binPath, configDir string) (int, error) Stop(ctx context.Context, configDir string) error Status(ctx context.Context, configDir string) (running bool, pid int, err error) } -// CommandExecutor abstracts command execution for testing +// CommandExecutor abstracts command execution for testing. type CommandExecutor interface { Execute(ctx context.Context, name string, args ...string) ([]byte, error) } -// RealCommandExecutor executes commands using os/exec +// RealCommandExecutor executes commands using os/exec. type RealCommandExecutor struct{} // Execute runs a command and returns its output @@ -50,10 +50,10 @@ type CrowdsecHandler struct { DataDir string } -func NewCrowdsecHandler(db *gorm.DB, exec CrowdsecExecutor, binPath, dataDir string) *CrowdsecHandler { +func NewCrowdsecHandler(db *gorm.DB, executor CrowdsecExecutor, binPath, dataDir string) *CrowdsecHandler { return &CrowdsecHandler{ DB: db, - Executor: exec, + Executor: executor, CmdExec: &RealCommandExecutor{}, BinPath: binPath, DataDir: dataDir, @@ -139,13 +139,21 @@ func (h *CrowdsecHandler) ImportConfig(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to open temp file"}) return } - defer in.Close() + defer func() { + if err := in.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close temp file") + } + }() out, err := os.Create(target) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create target file"}) return } - defer out.Close() + defer func() { + if err := out.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close target file") + } + }() if _, err := io.Copy(out, in); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to write config"}) return @@ -197,7 +205,11 @@ func (h *CrowdsecHandler) ExportConfig(c *gin.Context) { if err != nil { return err } - defer f.Close() + defer func() { + if err := f.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close file while archiving", "path", path) + } + }() hdr := &tar.Header{ Name: rel, diff --git a/backend/internal/api/handlers/crowdsec_handler_coverage_test.go b/backend/internal/api/handlers/crowdsec_handler_coverage_test.go index 9b3bacf4..c0235ff3 100644 --- a/backend/internal/api/handlers/crowdsec_handler_coverage_test.go +++ b/backend/internal/api/handlers/crowdsec_handler_coverage_test.go @@ -24,7 +24,7 @@ func (f *errorExec) Start(ctx context.Context, binPath, configDir string) (int, func (f *errorExec) Stop(ctx context.Context, configDir string) error { return errors.New("failed to stop crowdsec") } -func (f *errorExec) Status(ctx context.Context, configDir string) (bool, int, error) { +func (f *errorExec) Status(ctx context.Context, configDir string) (running bool, pid int, err error) { return false, 0, errors.New("failed to get status") } @@ -40,7 +40,7 @@ func TestCrowdsec_Start_Error(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/crowdsec/start", nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/crowdsec/start", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) @@ -59,7 +59,7 @@ func TestCrowdsec_Stop_Error(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/crowdsec/stop", nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/crowdsec/stop", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) @@ -78,7 +78,7 @@ func TestCrowdsec_Status_Error(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/status", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/status", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) @@ -98,7 +98,7 @@ func TestCrowdsec_ReadFile_MissingPath(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/file", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/file", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusBadRequest, w.Code) @@ -118,7 +118,7 @@ func TestCrowdsec_ReadFile_PathTraversal(t *testing.T) { // Attempt path traversal w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/file?path=../../../etc/passwd", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/file?path=../../../etc/passwd", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusBadRequest, w.Code) @@ -137,7 +137,7 @@ func TestCrowdsec_ReadFile_NotFound(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/file?path=nonexistent.conf", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/file?path=nonexistent.conf", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) @@ -227,7 +227,7 @@ func TestCrowdsec_ExportConfig_NotFound(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/export", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/export", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) @@ -247,7 +247,7 @@ func TestCrowdsec_ListFiles_EmptyDir(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/files", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/files", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -273,7 +273,7 @@ func TestCrowdsec_ListFiles_NonExistent(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/files", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/files", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -298,7 +298,7 @@ func TestCrowdsec_ImportConfig_NoFile(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/crowdsec/import", nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/crowdsec/import", http.NoBody) req.Header.Set("Content-Type", "multipart/form-data") r.ServeHTTP(w, req) @@ -323,7 +323,7 @@ func TestCrowdsec_ReadFile_NestedPath(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/file?path=subdir/test.conf", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/file?path=subdir/test.conf", http.NoBody) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) diff --git a/backend/internal/api/handlers/crowdsec_handler_test.go b/backend/internal/api/handlers/crowdsec_handler_test.go index df43b58d..d4e775e4 100644 --- a/backend/internal/api/handlers/crowdsec_handler_test.go +++ b/backend/internal/api/handlers/crowdsec_handler_test.go @@ -27,7 +27,7 @@ func (f *fakeExec) Stop(ctx context.Context, configDir string) error { f.started = false return nil } -func (f *fakeExec) Status(ctx context.Context, configDir string) (bool, int, error) { +func (f *fakeExec) Status(ctx context.Context, configDir string) (running bool, pid int, err error) { if f.started { return true, 12345, nil } @@ -53,7 +53,7 @@ func TestCrowdsecEndpoints(t *testing.T) { // Status (initially stopped) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/status", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/status", http.NoBody) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status expected 200 got %d", w.Code) @@ -61,7 +61,7 @@ func TestCrowdsecEndpoints(t *testing.T) { // Start w2 := httptest.NewRecorder() - req2 := httptest.NewRequest(http.MethodPost, "/api/v1/admin/crowdsec/start", nil) + req2 := httptest.NewRequest(http.MethodPost, "/api/v1/admin/crowdsec/start", http.NoBody) r.ServeHTTP(w2, req2) if w2.Code != http.StatusOK { t.Fatalf("start expected 200 got %d", w2.Code) @@ -69,7 +69,7 @@ func TestCrowdsecEndpoints(t *testing.T) { // Stop w3 := httptest.NewRecorder() - req3 := httptest.NewRequest(http.MethodPost, "/api/v1/admin/crowdsec/stop", nil) + req3 := httptest.NewRequest(http.MethodPost, "/api/v1/admin/crowdsec/stop", http.NoBody) r.ServeHTTP(w3, req3) if w3.Code != http.StatusOK { t.Fatalf("stop expected 200 got %d", w3.Code) @@ -151,7 +151,7 @@ func TestImportCreatesBackup(t *testing.T) { // fallback: check for any .backup.* in same parent dir entries, _ := os.ReadDir(filepath.Dir(tmpDir)) for _, e := range entries { - if e.IsDir() && filepath.Ext(e.Name()) == "" && (len(e.Name()) > 0) && (filepath.Base(e.Name()) != filepath.Base(tmpDir)) { + if e.IsDir() && filepath.Ext(e.Name()) == "" && e.Name() != "" && (filepath.Base(e.Name()) != filepath.Base(tmpDir)) { // best-effort assume backup present found = true break @@ -181,7 +181,7 @@ func TestExportConfig(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/export", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/export", http.NoBody) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("export expected 200 got %d body=%s", w.Code, w.Body.String()) @@ -211,14 +211,14 @@ func TestListAndReadFile(t *testing.T) { h.RegisterRoutes(g) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/files", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/files", http.NoBody) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("files expected 200 got %d body=%s", w.Code, w.Body.String()) } // read a single file w2 := httptest.NewRecorder() - req2 := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/file?path=conf.d/a.conf", nil) + req2 := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/file?path=conf.d/a.conf", http.NoBody) r.ServeHTTP(w2, req2) if w2.Code != http.StatusOK { t.Fatalf("file read expected 200 got %d body=%s", w2.Code, w2.Body.String()) diff --git a/backend/internal/api/handlers/docker_handler_test.go b/backend/internal/api/handlers/docker_handler_test.go index bab438db..0ac6c1cd 100644 --- a/backend/internal/api/handlers/docker_handler_test.go +++ b/backend/internal/api/handlers/docker_handler_test.go @@ -50,7 +50,7 @@ func TestDockerHandler_ListContainers(t *testing.T) { h := NewDockerHandler(svc, rsService) h.RegisterRoutes(r.Group("/")) - req, _ := http.NewRequest("GET", "/docker/containers", nil) + req, _ := http.NewRequest("GET", "/docker/containers", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -70,7 +70,7 @@ func TestDockerHandler_ListContainers_NonExistentServerID(t *testing.T) { h.RegisterRoutes(r.Group("/")) // Request with non-existent server_id - req, _ := http.NewRequest("GET", "/docker/containers?server_id=non-existent-uuid", nil) + req, _ := http.NewRequest("GET", "/docker/containers?server_id=non-existent-uuid", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -101,7 +101,7 @@ func TestDockerHandler_ListContainers_WithServerID(t *testing.T) { h.RegisterRoutes(r.Group("/")) // Request with valid server_id (will fail to connect, but shouldn't error on lookup) - req, _ := http.NewRequest("GET", "/docker/containers?server_id="+server.UUID, nil) + req, _ := http.NewRequest("GET", "/docker/containers?server_id="+server.UUID, http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -124,7 +124,7 @@ func TestDockerHandler_ListContainers_WithHostQuery(t *testing.T) { h.RegisterRoutes(r.Group("/")) // Request with custom host parameter - req, _ := http.NewRequest("GET", "/docker/containers?host=tcp://invalid-host:2375", nil) + req, _ := http.NewRequest("GET", "/docker/containers?host=tcp://invalid-host:2375", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/domain_handler_test.go b/backend/internal/api/handlers/domain_handler_test.go index 57a9f519..e4f94f11 100644 --- a/backend/internal/api/handlers/domain_handler_test.go +++ b/backend/internal/api/handlers/domain_handler_test.go @@ -54,7 +54,7 @@ func TestDomainLifecycle(t *testing.T) { require.NotEmpty(t, created.UUID) // 2. List Domains - req = httptest.NewRequest(http.MethodGet, "/api/v1/domains", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/domains", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) @@ -65,13 +65,13 @@ func TestDomainLifecycle(t *testing.T) { require.Equal(t, "example.com", list[0].Name) // 3. Delete Domain - req = httptest.NewRequest(http.MethodDelete, "/api/v1/domains/"+created.UUID, nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/domains/"+created.UUID, http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) // 4. Verify Deletion - req = httptest.NewRequest(http.MethodGet, "/api/v1/domains", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/domains", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) @@ -101,7 +101,7 @@ func TestDomainErrors(t *testing.T) { func TestDomainDelete_NotFound(t *testing.T) { router, _ := setupDomainTestRouter(t) - req := httptest.NewRequest(http.MethodDelete, "/api/v1/domains/nonexistent-uuid", nil) + req := httptest.NewRequest(http.MethodDelete, "/api/v1/domains/nonexistent-uuid", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) // Handler may return 200 with deleted=true even if not found (soft delete behavior) @@ -136,7 +136,7 @@ func TestDomainCreate_Duplicate(t *testing.T) { func TestDomainList_Empty(t *testing.T) { router, _ := setupDomainTestRouter(t) - req := httptest.NewRequest(http.MethodGet, "/api/v1/domains", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/domains", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) diff --git a/backend/internal/api/handlers/feature_flags_handler_coverage_test.go b/backend/internal/api/handlers/feature_flags_handler_coverage_test.go index 82468d59..5e84f978 100644 --- a/backend/internal/api/handlers/feature_flags_handler_coverage_test.go +++ b/backend/internal/api/handlers/feature_flags_handler_coverage_test.go @@ -33,7 +33,7 @@ func TestFeatureFlagsHandler_GetFlags_DBPrecedence(t *testing.T) { r := gin.New() r.GET("/api/v1/feature-flags", h.GetFlags) - req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -58,7 +58,7 @@ func TestFeatureFlagsHandler_GetFlags_EnvFallback(t *testing.T) { r := gin.New() r.GET("/api/v1/feature-flags", h.GetFlags) - req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -83,7 +83,7 @@ func TestFeatureFlagsHandler_GetFlags_EnvShortForm(t *testing.T) { r := gin.New() r.GET("/api/v1/feature-flags", h.GetFlags) - req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -108,7 +108,7 @@ func TestFeatureFlagsHandler_GetFlags_EnvNumeric(t *testing.T) { r := gin.New() r.GET("/api/v1/feature-flags", h.GetFlags) - req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -131,7 +131,7 @@ func TestFeatureFlagsHandler_GetFlags_DefaultTrue(t *testing.T) { r := gin.New() r.GET("/api/v1/feature-flags", h.GetFlags) - req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -154,7 +154,7 @@ func TestFeatureFlagsHandler_GetFlags_AllDefaultFlagsPresent(t *testing.T) { r := gin.New() r.GET("/api/v1/feature-flags", h.GetFlags) - req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -353,7 +353,7 @@ func TestFeatureFlagsHandler_GetFlags_DBValueVariants(t *testing.T) { r := gin.New() r.GET("/api/v1/feature-flags", h.GetFlags) - req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -396,7 +396,7 @@ func TestFeatureFlagsHandler_GetFlags_EnvValueVariants(t *testing.T) { r := gin.New() r.GET("/api/v1/feature-flags", h.GetFlags) - req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/feature_flags_handler_test.go b/backend/internal/api/handlers/feature_flags_handler_test.go index 4000a0b6..d994a8de 100644 --- a/backend/internal/api/handlers/feature_flags_handler_test.go +++ b/backend/internal/api/handlers/feature_flags_handler_test.go @@ -32,7 +32,7 @@ func TestFeatureFlags_GetAndUpdate(t *testing.T) { r.PUT("/api/v1/feature-flags", h.UpdateFlags) // 1) GET should return all default flags (as keys) - req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusOK { @@ -83,7 +83,7 @@ func TestFeatureFlags_EnvFallback(t *testing.T) { r := gin.New() r.GET("/api/v1/feature-flags", h.GetFlags) - req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/feature-flags", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusOK { diff --git a/backend/internal/api/handlers/handlers_test.go b/backend/internal/api/handlers/handlers_test.go index 9a568076..a27132ac 100644 --- a/backend/internal/api/handlers/handlers_test.go +++ b/backend/internal/api/handlers/handlers_test.go @@ -56,7 +56,7 @@ func TestRemoteServerHandler_List(t *testing.T) { // Test List w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/api/v1/remote-servers", nil) + req, _ := http.NewRequest("GET", "/api/v1/remote-servers", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -123,7 +123,7 @@ func TestRemoteServerHandler_TestConnection(t *testing.T) { // Test connection w := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/v1/remote-servers/"+server.UUID+"/test", nil) + req, _ := http.NewRequest("POST", "/api/v1/remote-servers/"+server.UUID+"/test", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -157,7 +157,7 @@ func TestRemoteServerHandler_Get(t *testing.T) { // Test Get w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/api/v1/remote-servers/"+server.UUID, nil) + req, _ := http.NewRequest("GET", "/api/v1/remote-servers/"+server.UUID, http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -235,14 +235,14 @@ func TestRemoteServerHandler_Delete(t *testing.T) { // Test Delete w := httptest.NewRecorder() - req, _ := http.NewRequest("DELETE", "/api/v1/remote-servers/"+server.UUID, nil) + req, _ := http.NewRequest("DELETE", "/api/v1/remote-servers/"+server.UUID, http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusNoContent, w.Code) // Verify Delete w2 := httptest.NewRecorder() - req2, _ := http.NewRequest("GET", "/api/v1/remote-servers/"+server.UUID, nil) + req2, _ := http.NewRequest("GET", "/api/v1/remote-servers/"+server.UUID, http.NoBody) router.ServeHTTP(w2, req2) assert.Equal(t, http.StatusNotFound, w2.Code) @@ -271,7 +271,7 @@ func TestProxyHostHandler_List(t *testing.T) { // Test List w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/api/v1/proxy-hosts", nil) + req, _ := http.NewRequest("GET", "/api/v1/proxy-hosts", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -362,7 +362,7 @@ func TestProxyHostHandler_PartialUpdate_DoesNotWipeFields(t *testing.T) { // Fetch via GET to ensure DB persisted state correctly w2 := httptest.NewRecorder() - req2, _ := http.NewRequest("GET", "/api/v1/proxy-hosts/"+original.UUID, nil) + req2, _ := http.NewRequest("GET", "/api/v1/proxy-hosts/"+original.UUID, http.NoBody) router.ServeHTTP(w2, req2) assert.Equal(t, http.StatusOK, w2.Code) @@ -382,7 +382,7 @@ func TestHealthHandler(t *testing.T) { router.GET("/health", handlers.HealthHandler) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/health", nil) + req, _ := http.NewRequest("GET", "/health", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -404,7 +404,7 @@ func TestRemoteServerHandler_Errors(t *testing.T) { // Get non-existent w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/api/v1/remote-servers/non-existent", nil) + req, _ := http.NewRequest("GET", "/api/v1/remote-servers/non-existent", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) @@ -417,7 +417,7 @@ func TestRemoteServerHandler_Errors(t *testing.T) { // Delete non-existent w = httptest.NewRecorder() - req, _ = http.NewRequest("DELETE", "/api/v1/remote-servers/non-existent", nil) + req, _ = http.NewRequest("DELETE", "/api/v1/remote-servers/non-existent", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) } diff --git a/backend/internal/api/handlers/health_handler_test.go b/backend/internal/api/handlers/health_handler_test.go index b890cb59..5448a42e 100644 --- a/backend/internal/api/handlers/health_handler_test.go +++ b/backend/internal/api/handlers/health_handler_test.go @@ -15,7 +15,7 @@ func TestHealthHandler(t *testing.T) { r := gin.New() r.GET("/health", HealthHandler) - req, _ := http.NewRequest("GET", "/health", nil) + req, _ := http.NewRequest("GET", "/health", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/import_handler.go b/backend/internal/api/handlers/import_handler.go index 4b8e1203..f8495f12 100644 --- a/backend/internal/api/handlers/import_handler.go +++ b/backend/internal/api/handlers/import_handler.go @@ -267,7 +267,7 @@ func (h *ImportHandler) Upload(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid import directory"}) return } - if err := os.MkdirAll(uploadsDir, 0755); err != nil { + if err := os.MkdirAll(uploadsDir, 0o755); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create uploads directory"}) return } @@ -276,7 +276,7 @@ func (h *ImportHandler) Upload(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid temp path"}) return } - if err := os.WriteFile(tempPath, []byte(req.Content), 0644); err != nil { + if err := os.WriteFile(tempPath, []byte(req.Content), 0o644); err != nil { middleware.GetRequestLogger(c).WithField("tempPath", util.SanitizeForLog(filepath.Base(tempPath))).WithError(err).Error("Import Upload: failed to write temp file") c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to write upload"}) return @@ -415,7 +415,7 @@ func (h *ImportHandler) UploadMulti(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid session directory"}) return } - if err := os.MkdirAll(sessionDir, 0755); err != nil { + if err := os.MkdirAll(sessionDir, 0o755); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create session directory"}) return } @@ -438,13 +438,13 @@ func (h *ImportHandler) UploadMulti(c *gin.Context) { // Create parent directory if file is in a subdirectory if dir := filepath.Dir(targetPath); dir != sessionDir { - if err := os.MkdirAll(dir, 0755); err != nil { + if err := os.MkdirAll(dir, 0o755); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to create directory for %s", f.Filename)}) return } } - if err := os.WriteFile(targetPath, []byte(f.Content), 0644); err != nil { + if err := os.WriteFile(targetPath, []byte(f.Content), 0o644); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to write file %s", f.Filename)}) return } @@ -510,12 +510,12 @@ func detectImportDirectives(content string) []string { for _, line := range lines { trimmed := strings.TrimSpace(line) if strings.HasPrefix(trimmed, "import ") { - path := strings.TrimSpace(strings.TrimPrefix(trimmed, "import")) + importPath := strings.TrimSpace(strings.TrimPrefix(trimmed, "import")) // Remove any trailing comments - if idx := strings.Index(path, "#"); idx != -1 { - path = strings.TrimSpace(path[:idx]) + if idx := strings.Index(importPath, "#"); idx != -1 { + importPath = strings.TrimSpace(importPath[:idx]) } - imports = append(imports, path) + imports = append(imports, importPath) } } return imports diff --git a/backend/internal/api/handlers/import_handler_sanitize_test.go b/backend/internal/api/handlers/import_handler_sanitize_test.go index f4a405b2..2140ca0b 100644 --- a/backend/internal/api/handlers/import_handler_sanitize_test.go +++ b/backend/internal/api/handlers/import_handler_sanitize_test.go @@ -23,7 +23,7 @@ func TestImportUploadSanitizesFilename(t *testing.T) { db := OpenTestDB(t) // Create a fake caddy executable to avoid dependency on system binary fakeCaddy := filepath.Join(tmpDir, "caddy") - os.WriteFile(fakeCaddy, []byte("#!/bin/sh\nexit 0"), 0755) + os.WriteFile(fakeCaddy, []byte("#!/bin/sh\nexit 0"), 0o755) svc := NewImportHandler(db, fakeCaddy, tmpDir, "") router := gin.New() diff --git a/backend/internal/api/handlers/import_handler_test.go b/backend/internal/api/handlers/import_handler_test.go index cac5b128..0ca1c3d6 100644 --- a/backend/internal/api/handlers/import_handler_test.go +++ b/backend/internal/api/handlers/import_handler_test.go @@ -40,7 +40,7 @@ func TestImportHandler_GetStatus(t *testing.T) { router.GET("/import/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/import/status", nil) + req, _ := http.NewRequest("GET", "/import/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -52,7 +52,7 @@ func TestImportHandler_GetStatus(t *testing.T) { // Case 2: No DB session but has mounted Caddyfile tmpDir := t.TempDir() mountPath := filepath.Join(tmpDir, "mounted.caddyfile") - os.WriteFile(mountPath, []byte("example.com"), 0644) + os.WriteFile(mountPath, []byte("example.com"), 0o644) handler2 := handlers.NewImportHandler(db, "echo", "/tmp", mountPath) router2 := gin.New() @@ -97,7 +97,7 @@ func TestImportHandler_GetPreview(t *testing.T) { // Case 1: No session w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/import/preview", nil) + req, _ := http.NewRequest("GET", "/import/preview", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) @@ -110,7 +110,7 @@ func TestImportHandler_GetPreview(t *testing.T) { db.Create(&session) w = httptest.NewRecorder() - req, _ = http.NewRequest("GET", "/import/preview", nil) + req, _ = http.NewRequest("GET", "/import/preview", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -141,7 +141,7 @@ func TestImportHandler_Cancel(t *testing.T) { db.Create(&session) w := httptest.NewRecorder() - req, _ := http.NewRequest("DELETE", "/import/cancel?session_uuid=test-uuid", nil) + req, _ := http.NewRequest("DELETE", "/import/cancel?session_uuid=test-uuid", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -198,7 +198,7 @@ func TestImportHandler_Upload(t *testing.T) { // Use fake caddy script cwd, _ := os.Getwd() fakeCaddy := filepath.Join(cwd, "testdata", "fake_caddy.sh") - os.Chmod(fakeCaddy, 0755) + os.Chmod(fakeCaddy, 0o755) tmpDir := t.TempDir() handler := handlers.NewImportHandler(db, fakeCaddy, tmpDir, "") @@ -231,7 +231,7 @@ func TestImportHandler_GetPreview_WithContent(t *testing.T) { // Case: Active session with source file content := "example.com {\n reverse_proxy localhost:8080\n}" sourceFile := filepath.Join(tmpDir, "source.caddyfile") - err := os.WriteFile(sourceFile, []byte(content), 0644) + err := os.WriteFile(sourceFile, []byte(content), 0o644) assert.NoError(t, err) // Case: Active session with source file @@ -244,7 +244,7 @@ func TestImportHandler_GetPreview_WithContent(t *testing.T) { db.Create(&session) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/import/preview", nil) + req, _ := http.NewRequest("GET", "/import/preview", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -307,7 +307,7 @@ func TestImportHandler_Cancel_Errors(t *testing.T) { // Case 1: Session not found w := httptest.NewRecorder() - req, _ := http.NewRequest("DELETE", "/import/cancel?session_uuid=non-existent", nil) + req, _ := http.NewRequest("DELETE", "/import/cancel?session_uuid=non-existent", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) } @@ -320,14 +320,14 @@ func TestCheckMountedImport(t *testing.T) { // Use fake caddy script cwd, _ := os.Getwd() fakeCaddy := filepath.Join(cwd, "testdata", "fake_caddy.sh") - os.Chmod(fakeCaddy, 0755) + os.Chmod(fakeCaddy, 0o755) // Case 1: File does not exist err := handlers.CheckMountedImport(db, mountPath, fakeCaddy, tmpDir) assert.NoError(t, err) // Case 2: File exists, not processed - err = os.WriteFile(mountPath, []byte("example.com"), 0644) + err = os.WriteFile(mountPath, []byte("example.com"), 0o644) assert.NoError(t, err) err = handlers.CheckMountedImport(db, mountPath, fakeCaddy, tmpDir) @@ -431,10 +431,10 @@ func TestImportHandler_GetPreview_BackupContent(t *testing.T) { // Create backup file backupDir := filepath.Join(tmpDir, "backups") - os.MkdirAll(backupDir, 0755) + os.MkdirAll(backupDir, 0o755) content := "backup content" backupFile := filepath.Join(backupDir, "source.caddyfile") - os.WriteFile(backupFile, []byte(content), 0644) + os.WriteFile(backupFile, []byte(content), 0o644) // Case: Active session with missing source file but existing backup session := models.ImportSession{ @@ -446,7 +446,7 @@ func TestImportHandler_GetPreview_BackupContent(t *testing.T) { db.Create(&session) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/import/preview", nil) + req, _ := http.NewRequest("GET", "/import/preview", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -465,7 +465,7 @@ func TestImportHandler_RegisterRoutes(t *testing.T) { // Verify routes exist by making requests w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/api/v1/import/status", nil) + req, _ := http.NewRequest("GET", "/api/v1/import/status", http.NoBody) router.ServeHTTP(w, req) assert.NotEqual(t, http.StatusNotFound, w.Code) } @@ -478,20 +478,20 @@ func TestImportHandler_GetPreview_TransientMount(t *testing.T) { // Create a mounted Caddyfile content := "example.com" - err := os.WriteFile(mountPath, []byte(content), 0644) + err := os.WriteFile(mountPath, []byte(content), 0o644) assert.NoError(t, err) // Use fake caddy script cwd, _ := os.Getwd() fakeCaddy := filepath.Join(cwd, "testdata", "fake_caddy_hosts.sh") - os.Chmod(fakeCaddy, 0755) + os.Chmod(fakeCaddy, 0o755) handler := handlers.NewImportHandler(db, fakeCaddy, tmpDir, mountPath) router := gin.New() router.GET("/import/preview", handler.GetPreview) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/import/preview", nil) + req, _ := http.NewRequest("GET", "/import/preview", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code, "Response body: %s", w.Body.String()) @@ -522,7 +522,7 @@ func TestImportHandler_Commit_TransientUpload(t *testing.T) { // Use fake caddy script cwd, _ := os.Getwd() fakeCaddy := filepath.Join(cwd, "testdata", "fake_caddy_hosts.sh") - os.Chmod(fakeCaddy, 0755) + os.Chmod(fakeCaddy, 0o755) handler := handlers.NewImportHandler(db, fakeCaddy, tmpDir, "") router := gin.New() @@ -580,13 +580,13 @@ func TestImportHandler_Commit_TransientMount(t *testing.T) { mountPath := filepath.Join(tmpDir, "mounted.caddyfile") // Create a mounted Caddyfile - err := os.WriteFile(mountPath, []byte("mounted.com"), 0644) + err := os.WriteFile(mountPath, []byte("mounted.com"), 0o644) assert.NoError(t, err) // Use fake caddy script cwd, _ := os.Getwd() fakeCaddy := filepath.Join(cwd, "testdata", "fake_caddy_hosts.sh") - os.Chmod(fakeCaddy, 0755) + os.Chmod(fakeCaddy, 0o755) handler := handlers.NewImportHandler(db, fakeCaddy, tmpDir, mountPath) router := gin.New() @@ -627,7 +627,7 @@ func TestImportHandler_Cancel_TransientUpload(t *testing.T) { // Use fake caddy script cwd, _ := os.Getwd() fakeCaddy := filepath.Join(cwd, "testdata", "fake_caddy_hosts.sh") - os.Chmod(fakeCaddy, 0755) + os.Chmod(fakeCaddy, 0o755) handler := handlers.NewImportHandler(db, fakeCaddy, tmpDir, "") router := gin.New() @@ -658,7 +658,7 @@ func TestImportHandler_Cancel_TransientUpload(t *testing.T) { // Cancel should delete the file w = httptest.NewRecorder() - req, _ = http.NewRequest("DELETE", "/import/cancel?session_uuid="+sessionID, nil) + req, _ = http.NewRequest("DELETE", "/import/cancel?session_uuid="+sessionID, http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -704,7 +704,7 @@ func TestImportHandler_Errors(t *testing.T) { // Cancel - Session Not Found w = httptest.NewRecorder() - req, _ = http.NewRequest("DELETE", "/import/cancel?session_uuid=non-existent", nil) + req, _ = http.NewRequest("DELETE", "/import/cancel?session_uuid=non-existent", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) } @@ -794,7 +794,7 @@ func TestImportHandler_UploadMulti(t *testing.T) { // Use fake caddy script cwd, _ := os.Getwd() fakeCaddy := filepath.Join(cwd, "testdata", "fake_caddy_hosts.sh") - os.Chmod(fakeCaddy, 0755) + os.Chmod(fakeCaddy, 0o755) handler := handlers.NewImportHandler(db, fakeCaddy, tmpDir, "") router := gin.New() diff --git a/backend/internal/api/handlers/logs_handler.go b/backend/internal/api/handlers/logs_handler.go index 66994212..0e39828a 100644 --- a/backend/internal/api/handlers/logs_handler.go +++ b/backend/internal/api/handlers/logs_handler.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" + "github.com/Wikid82/charon/backend/internal/logger" "github.com/Wikid82/charon/backend/internal/models" "github.com/Wikid82/charon/backend/internal/services" "github.com/gin-gonic/gin" @@ -84,22 +85,36 @@ func (h *LogsHandler) Download(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp file"}) return } - defer os.Remove(tmpFile.Name()) + defer func() { + if err := os.Remove(tmpFile.Name()); err != nil { + logger.Log().WithError(err).Warn("failed to remove temp file") + } + }() srcFile, err := os.Open(path) if err != nil { - _ = tmpFile.Close() + if err := tmpFile.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close temp file") + } c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open log file"}) return } - defer func() { _ = srcFile.Close() }() + defer func() { + if err := srcFile.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close source log file") + } + }() if _, err := io.Copy(tmpFile, srcFile); err != nil { - _ = tmpFile.Close() + if err := tmpFile.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close temp file after copy error") + } c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to copy log file"}) return } - _ = tmpFile.Close() + if err := tmpFile.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close temp file after copy") + } c.Header("Content-Disposition", "attachment; filename="+filename) c.File(tmpFile.Name()) diff --git a/backend/internal/api/handlers/logs_handler_coverage_test.go b/backend/internal/api/handlers/logs_handler_coverage_test.go index 96bf452f..d8c6b91f 100644 --- a/backend/internal/api/handlers/logs_handler_coverage_test.go +++ b/backend/internal/api/handlers/logs_handler_coverage_test.go @@ -1,6 +1,7 @@ package handlers import ( + "net/http" "net/http/httptest" "os" "path/filepath" @@ -38,7 +39,7 @@ func TestLogsHandler_Read_FilterBySearch(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "filename", Value: "access.log"}} - c.Request = httptest.NewRequest("GET", "/logs/access.log?search=error", nil) + c.Request = httptest.NewRequest("GET", "/logs/access.log?search=error", http.NoBody) h.Read(c) @@ -69,7 +70,7 @@ func TestLogsHandler_Read_FilterByHost(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "filename", Value: "access.log"}} - c.Request = httptest.NewRequest("GET", "/logs/access.log?host=example.com", nil) + c.Request = httptest.NewRequest("GET", "/logs/access.log?host=example.com", http.NoBody) h.Read(c) @@ -99,7 +100,7 @@ func TestLogsHandler_Read_FilterByLevel(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "filename", Value: "access.log"}} - c.Request = httptest.NewRequest("GET", "/logs/access.log?level=error", nil) + c.Request = httptest.NewRequest("GET", "/logs/access.log?level=error", http.NoBody) h.Read(c) @@ -129,7 +130,7 @@ func TestLogsHandler_Read_FilterByStatus(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "filename", Value: "access.log"}} - c.Request = httptest.NewRequest("GET", "/logs/access.log?status=500", nil) + c.Request = httptest.NewRequest("GET", "/logs/access.log?status=500", http.NoBody) h.Read(c) @@ -159,7 +160,7 @@ func TestLogsHandler_Read_SortAsc(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "filename", Value: "access.log"}} - c.Request = httptest.NewRequest("GET", "/logs/access.log?sort=asc", nil) + c.Request = httptest.NewRequest("GET", "/logs/access.log?sort=asc", http.NoBody) h.Read(c) @@ -185,7 +186,7 @@ func TestLogsHandler_List_DirectoryIsFile(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/logs", nil) + c.Request = httptest.NewRequest("GET", "/logs", http.NoBody) h.List(c) diff --git a/backend/internal/api/handlers/logs_handler_test.go b/backend/internal/api/handlers/logs_handler_test.go index b88dea78..8ebf8d53 100644 --- a/backend/internal/api/handlers/logs_handler_test.go +++ b/backend/internal/api/handlers/logs_handler_test.go @@ -26,24 +26,24 @@ func setupLogsTest(t *testing.T) (*gin.Engine, *services.LogService, string) { // It derives it from cfg.DatabasePath dataDir := filepath.Join(tmpDir, "data") - err = os.MkdirAll(dataDir, 0755) + err = os.MkdirAll(dataDir, 0o755) require.NoError(t, err) dbPath := filepath.Join(dataDir, "charon.db") // Create logs dir logsDir := filepath.Join(dataDir, "logs") - err = os.MkdirAll(logsDir, 0755) + err = os.MkdirAll(logsDir, 0o755) require.NoError(t, err) // Create dummy log files with JSON content log1 := `{"level":"info","ts":1600000000,"msg":"request handled","request":{"method":"GET","host":"example.com","uri":"/","remote_ip":"1.2.3.4"},"status":200}` log2 := `{"level":"error","ts":1600000060,"msg":"error handled","request":{"method":"POST","host":"api.example.com","uri":"/submit","remote_ip":"5.6.7.8"},"status":500}` - err = os.WriteFile(filepath.Join(logsDir, "access.log"), []byte(log1+"\n"+log2+"\n"), 0644) + err = os.WriteFile(filepath.Join(logsDir, "access.log"), []byte(log1+"\n"+log2+"\n"), 0o644) require.NoError(t, err) // Write a charon.log and create a cpmp.log symlink to it for backward compatibility (cpmp is legacy) - err = os.WriteFile(filepath.Join(logsDir, "charon.log"), []byte("app log line 1\napp log line 2"), 0644) + err = os.WriteFile(filepath.Join(logsDir, "charon.log"), []byte("app log line 1\napp log line 2"), 0o644) require.NoError(t, err) // Create legacy cpmp log symlink (cpmp is a legacy name for Charon) _ = os.Symlink(filepath.Join(logsDir, "charon.log"), filepath.Join(logsDir, "cpmp.log")) @@ -72,7 +72,7 @@ func TestLogsLifecycle(t *testing.T) { defer os.RemoveAll(tmpDir) // 1. List logs - req := httptest.NewRequest(http.MethodGet, "/api/v1/logs", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/logs", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) @@ -93,7 +93,7 @@ func TestLogsLifecycle(t *testing.T) { require.True(t, found) // 2. Read log - req = httptest.NewRequest(http.MethodGet, "/api/v1/logs/access.log?limit=2", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/logs/access.log?limit=2", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) @@ -108,27 +108,27 @@ func TestLogsLifecycle(t *testing.T) { require.Len(t, content.Logs, 2) // 3. Download log - req = httptest.NewRequest(http.MethodGet, "/api/v1/logs/access.log/download", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/logs/access.log/download", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) require.Contains(t, resp.Body.String(), "request handled") // 4. Read non-existent log - req = httptest.NewRequest(http.MethodGet, "/api/v1/logs/missing.log", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/logs/missing.log", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusNotFound, resp.Code) // 5. Download non-existent log - req = httptest.NewRequest(http.MethodGet, "/api/v1/logs/missing.log/download", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/logs/missing.log/download", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusNotFound, resp.Code) // 6. List logs error (delete directory) os.RemoveAll(filepath.Join(tmpDir, "data", "logs")) - req = httptest.NewRequest(http.MethodGet, "/api/v1/logs", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/logs", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) // ListLogs returns empty list if dir doesn't exist, so it should be 200 OK with empty list diff --git a/backend/internal/api/handlers/misc_coverage_test.go b/backend/internal/api/handlers/misc_coverage_test.go index a515712b..a9684ba8 100644 --- a/backend/internal/api/handlers/misc_coverage_test.go +++ b/backend/internal/api/handlers/misc_coverage_test.go @@ -3,6 +3,7 @@ package handlers import ( "bytes" "encoding/json" + "net/http" "net/http/httptest" "testing" @@ -112,7 +113,7 @@ func TestRemoteServerHandler_List_Error(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/remote-servers", nil) + c.Request = httptest.NewRequest("GET", "/remote-servers", http.NoBody) h.List(c) @@ -131,7 +132,7 @@ func TestRemoteServerHandler_List_EnabledOnly(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/remote-servers?enabled=true", nil) + c.Request = httptest.NewRequest("GET", "/remote-servers?enabled=true", http.NoBody) h.List(c) @@ -267,7 +268,7 @@ func TestUptimeHandler_GetHistory_Error(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Params = gin.Params{{Key: "id", Value: "test-id"}} - c.Request = httptest.NewRequest("GET", "/uptime/test-id/history", nil) + c.Request = httptest.NewRequest("GET", "/uptime/test-id/history", http.NoBody) h.GetHistory(c) diff --git a/backend/internal/api/handlers/notification_coverage_test.go b/backend/internal/api/handlers/notification_coverage_test.go index 2c26b5b8..8c6d2e03 100644 --- a/backend/internal/api/handlers/notification_coverage_test.go +++ b/backend/internal/api/handlers/notification_coverage_test.go @@ -3,6 +3,7 @@ package handlers import ( "bytes" "encoding/json" + "net/http" "net/http/httptest" "testing" @@ -35,7 +36,7 @@ func TestNotificationHandler_List_Error(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/notifications", nil) + c.Request = httptest.NewRequest("GET", "/notifications", http.NoBody) h.List(c) @@ -55,7 +56,7 @@ func TestNotificationHandler_List_UnreadOnly(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest("GET", "/notifications?unread=true", nil) + c.Request = httptest.NewRequest("GET", "/notifications?unread=true", http.NoBody) h.List(c) diff --git a/backend/internal/api/handlers/notification_handler_test.go b/backend/internal/api/handlers/notification_handler_test.go index 3d78996a..0d602d25 100644 --- a/backend/internal/api/handlers/notification_handler_test.go +++ b/backend/internal/api/handlers/notification_handler_test.go @@ -44,7 +44,7 @@ func TestNotificationHandler_List(t *testing.T) { // Test List All w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/notifications", nil) + req, _ := http.NewRequest("GET", "/notifications", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -55,7 +55,7 @@ func TestNotificationHandler_List(t *testing.T) { // Test List Unread w = httptest.NewRecorder() - req, _ = http.NewRequest("GET", "/notifications?unread=true", nil) + req, _ = http.NewRequest("GET", "/notifications?unread=true", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -79,7 +79,7 @@ func TestNotificationHandler_MarkAsRead(t *testing.T) { router.POST("/notifications/:id/read", handler.MarkAsRead) w := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/notifications/"+notif.ID+"/read", nil) + req, _ := http.NewRequest("POST", "/notifications/"+notif.ID+"/read", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -103,7 +103,7 @@ func TestNotificationHandler_MarkAllAsRead(t *testing.T) { router.POST("/notifications/read-all", handler.MarkAllAsRead) w := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/notifications/read-all", nil) + req, _ := http.NewRequest("POST", "/notifications/read-all", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -126,7 +126,7 @@ func TestNotificationHandler_MarkAllAsRead_Error(t *testing.T) { sqlDB, _ := db.DB() sqlDB.Close() - req, _ := http.NewRequest("POST", "/notifications/read-all", nil) + req, _ := http.NewRequest("POST", "/notifications/read-all", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) @@ -145,7 +145,7 @@ func TestNotificationHandler_DBError(t *testing.T) { sqlDB, _ := db.DB() sqlDB.Close() - req, _ := http.NewRequest("POST", "/notifications/1/read", nil) + req, _ := http.NewRequest("POST", "/notifications/1/read", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) diff --git a/backend/internal/api/handlers/notification_provider_handler_test.go b/backend/internal/api/handlers/notification_provider_handler_test.go index 8961de0d..30a6bcc8 100644 --- a/backend/internal/api/handlers/notification_provider_handler_test.go +++ b/backend/internal/api/handlers/notification_provider_handler_test.go @@ -61,7 +61,7 @@ func TestNotificationProviderHandler_CRUD(t *testing.T) { assert.NotEmpty(t, created.ID) // 2. List - req, _ = http.NewRequest("GET", "/api/v1/notifications/providers", nil) + req, _ = http.NewRequest("GET", "/api/v1/notifications/providers", http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -88,7 +88,7 @@ func TestNotificationProviderHandler_CRUD(t *testing.T) { assert.Equal(t, "Updated Discord", dbProvider.Name) // 4. Delete - req, _ = http.NewRequest("DELETE", "/api/v1/notifications/providers/"+created.ID, nil) + req, _ = http.NewRequest("DELETE", "/api/v1/notifications/providers/"+created.ID, http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -102,7 +102,7 @@ func TestNotificationProviderHandler_CRUD(t *testing.T) { func TestNotificationProviderHandler_Templates(t *testing.T) { r, _ := setupNotificationProviderTest(t) - req, _ := http.NewRequest("GET", "/api/v1/notifications/templates", nil) + req, _ := http.NewRequest("GET", "/api/v1/notifications/templates", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/notification_template_handler_test.go b/backend/internal/api/handlers/notification_template_handler_test.go index 13b51e0e..5a0adfd1 100644 --- a/backend/internal/api/handlers/notification_template_handler_test.go +++ b/backend/internal/api/handlers/notification_template_handler_test.go @@ -45,7 +45,7 @@ func TestNotificationTemplateHandler_CRUDAndPreview(t *testing.T) { require.NotEmpty(t, created.ID) // List - req = httptest.NewRequest(http.MethodGet, "/api/v1/notifications/templates", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/notifications/templates", http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) @@ -76,7 +76,7 @@ func TestNotificationTemplateHandler_CRUDAndPreview(t *testing.T) { require.NotEmpty(t, previewResp["rendered"]) // Delete - req = httptest.NewRequest(http.MethodDelete, "/api/v1/notifications/templates/"+created.ID, nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/notifications/templates/"+created.ID, http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) diff --git a/backend/internal/api/handlers/perf_assert_test.go b/backend/internal/api/handlers/perf_assert_test.go index 120ca31e..678f34e5 100644 --- a/backend/internal/api/handlers/perf_assert_test.go +++ b/backend/internal/api/handlers/perf_assert_test.go @@ -53,7 +53,7 @@ func gatherStats(t *testing.T, req *http.Request, router http.Handler, counts in } // computePercentiles returns avg, p50, p95, p99, max -func computePercentiles(samples []float64) (avg, p50, p95, p99, max float64) { +func computePercentiles(samples []float64) (avg, p50, p95, p99, maxVal float64) { sort.Float64s(samples) var sum float64 for _, s := range samples { @@ -73,7 +73,7 @@ func computePercentiles(samples []float64) (avg, p50, p95, p99, max float64) { p50 = p(0.50) p95 = p(0.95) p99 = p(0.99) - max = samples[len(samples)-1] + maxVal = samples[len(samples)-1] return } @@ -93,9 +93,9 @@ func TestPerf_GetStatus_AssertThreshold(t *testing.T) { router.GET("/api/v1/security/status", h.GetStatus) counts := 500 - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) samples := gatherStats(t, req, router, counts) - avg, _, p95, _, max := computePercentiles(samples) + avg, _, p95, _, maxVal := computePercentiles(samples) // default thresholds ms thresholdP95 := 2.0 // 2ms per request if env := os.Getenv("PERF_MAX_MS_GETSTATUS_P95"); env != "" { @@ -104,7 +104,7 @@ func TestPerf_GetStatus_AssertThreshold(t *testing.T) { } } // fail if p95 exceeds threshold - t.Logf("GetStatus avg=%.3fms p95=%.3fms max=%.3fms", avg, p95, max) + t.Logf("GetStatus avg=%.3fms p95=%.3fms max=%.3fms", avg, p95, maxVal) if p95 > thresholdP95 { t.Fatalf("GetStatus P95 (%.3fms) exceeds threshold %.3fms", p95, thresholdP95) } @@ -123,7 +123,7 @@ func TestPerf_GetStatus_Parallel_AssertThreshold(t *testing.T) { samples := make(chan float64, n) var worker = func() { for i := 0; i < n; i++ { - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) w := httptest.NewRecorder() s := time.Now() router.ServeHTTP(w, req) @@ -140,14 +140,14 @@ func TestPerf_GetStatus_Parallel_AssertThreshold(t *testing.T) { for i := 0; i < n*4; i++ { collected = append(collected, <-samples) } - avg, _, p95, _, max := computePercentiles(collected) + avg, _, p95, _, maxVal := computePercentiles(collected) thresholdP95 := 5.0 // 5ms default if env := os.Getenv("PERF_MAX_MS_GETSTATUS_P95_PARALLEL"); env != "" { if parsed, err := time.ParseDuration(env); err == nil { thresholdP95 = ms(parsed) } } - t.Logf("GetStatus Parallel avg=%.3fms p95=%.3fms max=%.3fms", avg, p95, max) + t.Logf("GetStatus Parallel avg=%.3fms p95=%.3fms max=%.3fms", avg, p95, maxVal) if p95 > thresholdP95 { t.Fatalf("GetStatus Parallel P95 (%.3fms) exceeds threshold %.3fms", p95, thresholdP95) } @@ -167,16 +167,16 @@ func TestPerf_ListDecisions_AssertThreshold(t *testing.T) { router.GET("/api/v1/security/decisions", h.ListDecisions) counts := 200 - req := httptest.NewRequest("GET", "/api/v1/security/decisions?limit=50", nil) + req := httptest.NewRequest("GET", "/api/v1/security/decisions?limit=50", http.NoBody) samples := gatherStats(t, req, router, counts) - avg, _, p95, _, max := computePercentiles(samples) + avg, _, p95, _, maxVal := computePercentiles(samples) thresholdP95 := 30.0 // 30ms default if env := os.Getenv("PERF_MAX_MS_LISTDECISIONS_P95"); env != "" { if parsed, err := time.ParseDuration(env); err == nil { thresholdP95 = ms(parsed) } } - t.Logf("ListDecisions avg=%.3fms p95=%.3fms max=%.3fms", avg, p95, max) + t.Logf("ListDecisions avg=%.3fms p95=%.3fms max=%.3fms", avg, p95, maxVal) if p95 > thresholdP95 { t.Fatalf("ListDecisions P95 (%.3fms) exceeds threshold %.3fms", p95, thresholdP95) } diff --git a/backend/internal/api/handlers/proxy_host_handler.go b/backend/internal/api/handlers/proxy_host_handler.go index 9807c820..10c949ef 100644 --- a/backend/internal/api/handlers/proxy_host_handler.go +++ b/backend/internal/api/handlers/proxy_host_handler.go @@ -125,9 +125,9 @@ func (h *ProxyHostHandler) Create(c *gin.Context) { // Get retrieves a proxy host by UUID. func (h *ProxyHostHandler) Get(c *gin.Context) { - uuid := c.Param("uuid") + uuidStr := c.Param("uuid") - host, err := h.service.GetByUUID(uuid) + host, err := h.service.GetByUUID(uuidStr) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "proxy host not found"}) return @@ -310,9 +310,9 @@ func (h *ProxyHostHandler) Update(c *gin.Context) { // Delete removes a proxy host. func (h *ProxyHostHandler) Delete(c *gin.Context) { - uuid := c.Param("uuid") + uuidStr := c.Param("uuid") - host, err := h.service.GetByUUID(uuid) + host, err := h.service.GetByUUID(uuidStr) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "proxy host not found"}) return @@ -399,11 +399,11 @@ func (h *ProxyHostHandler) BulkUpdateACL(c *gin.Context) { updated := 0 errors := []map[string]string{} - for _, uuid := range req.HostUUIDs { - host, err := h.service.GetByUUID(uuid) + for _, hostUUID := range req.HostUUIDs { + host, err := h.service.GetByUUID(hostUUID) if err != nil { errors = append(errors, map[string]string{ - "uuid": uuid, + "uuid": hostUUID, "error": "proxy host not found", }) continue @@ -412,7 +412,7 @@ func (h *ProxyHostHandler) BulkUpdateACL(c *gin.Context) { host.AccessListID = req.AccessListID if err := h.service.Update(host); err != nil { errors = append(errors, map[string]string{ - "uuid": uuid, + "uuid": hostUUID, "error": err.Error(), }) continue diff --git a/backend/internal/api/handlers/proxy_host_handler_test.go b/backend/internal/api/handlers/proxy_host_handler_test.go index a5510d52..dc0ddc97 100644 --- a/backend/internal/api/handlers/proxy_host_handler_test.go +++ b/backend/internal/api/handlers/proxy_host_handler_test.go @@ -59,7 +59,7 @@ func TestProxyHostLifecycle(t *testing.T) { require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &created)) require.Equal(t, "media.example.com", created.DomainNames) - listReq := httptest.NewRequest(http.MethodGet, "/api/v1/proxy-hosts", nil) + listReq := httptest.NewRequest(http.MethodGet, "/api/v1/proxy-hosts", http.NoBody) listResp := httptest.NewRecorder() router.ServeHTTP(listResp, listReq) require.Equal(t, http.StatusOK, listResp.Code) @@ -69,7 +69,7 @@ func TestProxyHostLifecycle(t *testing.T) { require.Len(t, hosts, 1) // Get by ID - getReq := httptest.NewRequest(http.MethodGet, "/api/v1/proxy-hosts/"+created.UUID, nil) + getReq := httptest.NewRequest(http.MethodGet, "/api/v1/proxy-hosts/"+created.UUID, http.NoBody) getResp := httptest.NewRecorder() router.ServeHTTP(getResp, getReq) require.Equal(t, http.StatusOK, getResp.Code) @@ -92,13 +92,13 @@ func TestProxyHostLifecycle(t *testing.T) { require.False(t, updated.Enabled) // Delete - delReq := httptest.NewRequest(http.MethodDelete, "/api/v1/proxy-hosts/"+created.UUID, nil) + delReq := httptest.NewRequest(http.MethodDelete, "/api/v1/proxy-hosts/"+created.UUID, http.NoBody) delResp := httptest.NewRecorder() router.ServeHTTP(delResp, delReq) require.Equal(t, http.StatusOK, delResp.Code) // Verify Delete - getReq2 := httptest.NewRequest(http.MethodGet, "/api/v1/proxy-hosts/"+created.UUID, nil) + getReq2 := httptest.NewRequest(http.MethodGet, "/api/v1/proxy-hosts/"+created.UUID, http.NoBody) getResp2 := httptest.NewRecorder() router.ServeHTTP(getResp2, getReq2) require.Equal(t, http.StatusNotFound, getResp2.Code) @@ -131,7 +131,7 @@ func TestProxyHostDelete_WithUptimeCleanup(t *testing.T) { require.Equal(t, int64(1), count) // Delete host with delete_uptime=true - req := httptest.NewRequest(http.MethodDelete, "/api/v1/proxy-hosts/"+host.UUID+"?delete_uptime=true", nil) + req := httptest.NewRequest(http.MethodDelete, "/api/v1/proxy-hosts/"+host.UUID+"?delete_uptime=true", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) @@ -198,7 +198,7 @@ func TestProxyHostErrors(t *testing.T) { db.Create(&host) // Test Get - Not Found - req = httptest.NewRequest(http.MethodGet, "/api/v1/proxy-hosts/non-existent-uuid", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/proxy-hosts/non-existent-uuid", http.NoBody) resp = httptest.NewRecorder() r.ServeHTTP(resp, req) require.Equal(t, http.StatusNotFound, resp.Code) @@ -226,13 +226,13 @@ func TestProxyHostErrors(t *testing.T) { require.Equal(t, http.StatusInternalServerError, resp.Code) // Test Delete - Not Found - req = httptest.NewRequest(http.MethodDelete, "/api/v1/proxy-hosts/non-existent-uuid", nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/proxy-hosts/non-existent-uuid", http.NoBody) resp = httptest.NewRecorder() r.ServeHTTP(resp, req) require.Equal(t, http.StatusNotFound, resp.Code) // Test Delete - Apply Config Error - req = httptest.NewRequest(http.MethodDelete, "/api/v1/proxy-hosts/"+host.UUID, nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/proxy-hosts/"+host.UUID, http.NoBody) resp = httptest.NewRecorder() r.ServeHTTP(resp, req) require.Equal(t, http.StatusInternalServerError, resp.Code) @@ -408,7 +408,7 @@ func TestProxyHostHandler_List_Error(t *testing.T) { sqlDB, _ := db.DB() sqlDB.Close() - req := httptest.NewRequest(http.MethodGet, "/api/v1/proxy-hosts", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/proxy-hosts", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusInternalServerError, resp.Code) @@ -465,7 +465,7 @@ func TestProxyHostWithCaddyIntegration(t *testing.T) { require.Equal(t, http.StatusOK, resp.Code) // Test Delete with Caddy Sync - req = httptest.NewRequest(http.MethodDelete, "/api/v1/proxy-hosts/"+createdHost.UUID, nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/proxy-hosts/"+createdHost.UUID, http.NoBody) resp = httptest.NewRecorder() r.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) diff --git a/backend/internal/api/handlers/remote_server_handler.go b/backend/internal/api/handlers/remote_server_handler.go index 2518504f..b1831500 100644 --- a/backend/internal/api/handlers/remote_server_handler.go +++ b/backend/internal/api/handlers/remote_server_handler.go @@ -9,6 +9,7 @@ import ( "github.com/gin-gonic/gin" "github.com/google/uuid" + "github.com/Wikid82/charon/backend/internal/logger" "github.com/Wikid82/charon/backend/internal/models" "github.com/Wikid82/charon/backend/internal/services" "github.com/Wikid82/charon/backend/internal/util" @@ -87,9 +88,9 @@ func (h *RemoteServerHandler) Create(c *gin.Context) { // Get retrieves a remote server by UUID. func (h *RemoteServerHandler) Get(c *gin.Context) { - uuid := c.Param("uuid") + uuidStr := c.Param("uuid") - server, err := h.service.GetByUUID(uuid) + server, err := h.service.GetByUUID(uuidStr) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "server not found"}) return @@ -100,9 +101,9 @@ func (h *RemoteServerHandler) Get(c *gin.Context) { // Update updates an existing remote server. func (h *RemoteServerHandler) Update(c *gin.Context) { - uuid := c.Param("uuid") + uuidStr := c.Param("uuid") - server, err := h.service.GetByUUID(uuid) + server, err := h.service.GetByUUID(uuidStr) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "server not found"}) return @@ -123,9 +124,9 @@ func (h *RemoteServerHandler) Update(c *gin.Context) { // Delete removes a remote server. func (h *RemoteServerHandler) Delete(c *gin.Context) { - uuid := c.Param("uuid") + uuidStr := c.Param("uuid") - server, err := h.service.GetByUUID(uuid) + server, err := h.service.GetByUUID(uuidStr) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "server not found"}) return @@ -154,9 +155,9 @@ func (h *RemoteServerHandler) Delete(c *gin.Context) { // TestConnection tests the TCP connection to a remote server. func (h *RemoteServerHandler) TestConnection(c *gin.Context) { - uuid := c.Param("uuid") + uuidStr := c.Param("uuid") - server, err := h.service.GetByUUID(uuid) + server, err := h.service.GetByUUID(uuidStr) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "server not found"}) return @@ -185,7 +186,11 @@ func (h *RemoteServerHandler) TestConnection(c *gin.Context) { c.JSON(http.StatusOK, result) return } - defer func() { _ = conn.Close() }() + defer func() { + if err := conn.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close tcp connection") + } + }() // Connection successful result["reachable"] = true @@ -228,7 +233,11 @@ func (h *RemoteServerHandler) TestConnectionCustom(c *gin.Context) { c.JSON(http.StatusOK, result) return } - defer func() { _ = conn.Close() }() + defer func() { + if err := conn.Close(); err != nil { + logger.Log().WithError(err).Warn("failed to close tcp connection") + } + }() // Connection successful result["reachable"] = true diff --git a/backend/internal/api/handlers/remote_server_handler_test.go b/backend/internal/api/handlers/remote_server_handler_test.go index e2250987..5cf501dc 100644 --- a/backend/internal/api/handlers/remote_server_handler_test.go +++ b/backend/internal/api/handlers/remote_server_handler_test.go @@ -84,13 +84,13 @@ func TestRemoteServerHandler_FullCRUD(t *testing.T) { assert.NotEmpty(t, created.UUID) // List - req, _ = http.NewRequest("GET", "/api/v1/remote-servers", nil) + req, _ = http.NewRequest("GET", "/api/v1/remote-servers", http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) // Get - req, _ = http.NewRequest("GET", "/api/v1/remote-servers/"+created.UUID, nil) + req, _ = http.NewRequest("GET", "/api/v1/remote-servers/"+created.UUID, http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -104,7 +104,7 @@ func TestRemoteServerHandler_FullCRUD(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) // Delete - req, _ = http.NewRequest("DELETE", "/api/v1/remote-servers/"+created.UUID, nil) + req, _ = http.NewRequest("DELETE", "/api/v1/remote-servers/"+created.UUID, http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusNoContent, w.Code) @@ -122,7 +122,7 @@ func TestRemoteServerHandler_FullCRUD(t *testing.T) { assert.Equal(t, http.StatusNotFound, w.Code) // Delete - Not Found - req, _ = http.NewRequest("DELETE", "/api/v1/remote-servers/non-existent-uuid", nil) + req, _ = http.NewRequest("DELETE", "/api/v1/remote-servers/non-existent-uuid", http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) diff --git a/backend/internal/api/handlers/security_handler_additional_test.go b/backend/internal/api/handlers/security_handler_additional_test.go index a9fae5c6..92d195f2 100644 --- a/backend/internal/api/handlers/security_handler_additional_test.go +++ b/backend/internal/api/handlers/security_handler_additional_test.go @@ -29,7 +29,7 @@ func TestSecurityHandler_GetConfigAndUpdateConfig(t *testing.T) { // Create a gin test context for GetConfig when no config exists w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest("GET", "/security/config", nil) + req := httptest.NewRequest("GET", "/security/config", http.NoBody) c.Request = req h.GetConfig(c) require.Equal(t, http.StatusOK, w.Code) @@ -53,7 +53,7 @@ func TestSecurityHandler_GetConfigAndUpdateConfig(t *testing.T) { // Now call GetConfig again and ensure config is returned w = httptest.NewRecorder() c, _ = gin.CreateTestContext(w) - req = httptest.NewRequest("GET", "/security/config", nil) + req = httptest.NewRequest("GET", "/security/config", http.NoBody) c.Request = req h.GetConfig(c) require.Equal(t, http.StatusOK, w.Code) diff --git a/backend/internal/api/handlers/security_handler_audit_test.go b/backend/internal/api/handlers/security_handler_audit_test.go index cd0896d7..b969cc86 100644 --- a/backend/internal/api/handlers/security_handler_audit_test.go +++ b/backend/internal/api/handlers/security_handler_audit_test.go @@ -65,7 +65,7 @@ func TestSecurityHandler_GetStatus_SQLInjection(t *testing.T) { router := gin.New() router.GET("/api/v1/security/status", h.GetStatus) - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -248,7 +248,7 @@ func TestSecurityHandler_GetStatus_SettingsOverride(t *testing.T) { router := gin.New() router.GET("/api/v1/security/status", h.GetStatus) - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -293,7 +293,7 @@ func TestSecurityHandler_GetStatus_DisabledViaSettings(t *testing.T) { router := gin.New() router.GET("/api/v1/security/status", h.GetStatus) - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -342,7 +342,7 @@ func TestSecurityAudit_DeleteRuleSet_InvalidID(t *testing.T) { if tc.id == "" { url = "/api/v1/security/rulesets/" } - req := httptest.NewRequest("DELETE", url, nil) + req := httptest.NewRequest("DELETE", url, http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -383,7 +383,7 @@ func TestSecurityHandler_UpsertRuleSet_XSSInContent(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code) // Verify it's stored and returned as JSON (not rendered as HTML) - req2 := httptest.NewRequest("GET", "/api/v1/security/rulesets", nil) + req2 := httptest.NewRequest("GET", "/api/v1/security/rulesets", http.NoBody) w2 := httptest.NewRecorder() router.ServeHTTP(w2, req2) @@ -468,7 +468,7 @@ func TestSecurityHandler_GetStatus_NilDB(t *testing.T) { router := gin.New() router.GET("/api/v1/security/status", h.GetStatus) - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) w := httptest.NewRecorder() // Should not panic @@ -498,7 +498,7 @@ func TestSecurityHandler_Enable_WithoutWhitelist(t *testing.T) { router.POST("/api/v1/security/enable", h.Enable) // Try to enable without token or whitelist - req := httptest.NewRequest("POST", "/api/v1/security/enable", nil) + req := httptest.NewRequest("POST", "/api/v1/security/enable", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -525,7 +525,7 @@ func TestSecurityHandler_Disable_RequiresToken(t *testing.T) { router.POST("/api/v1/security/disable", h.Disable) // Try to disable from non-localhost without token - req := httptest.NewRequest("POST", "/api/v1/security/disable", nil) + req := httptest.NewRequest("POST", "/api/v1/security/disable", http.NoBody) req.RemoteAddr = "10.0.0.5:12345" w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -560,7 +560,7 @@ func TestSecurityHandler_GetStatus_CrowdSecModeValidation(t *testing.T) { router := gin.New() router.GET("/api/v1/security/status", h.GetStatus) - req := httptest.NewRequest("GET", "/api/v1/security/status", nil) + req := httptest.NewRequest("GET", "/api/v1/security/status", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/security_handler_clean_test.go b/backend/internal/api/handlers/security_handler_clean_test.go index 760bb800..e494884a 100644 --- a/backend/internal/api/handlers/security_handler_clean_test.go +++ b/backend/internal/api/handlers/security_handler_clean_test.go @@ -46,7 +46,7 @@ func TestSecurityHandler_GetStatus_Clean(t *testing.T) { router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -72,7 +72,7 @@ func TestSecurityHandler_Cerberus_DBOverride(t *testing.T) { router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -108,7 +108,7 @@ func TestSecurityHandler_ACL_DBOverride(t *testing.T) { router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -127,7 +127,7 @@ func TestSecurityHandler_GenerateBreakGlass_ReturnsToken(t *testing.T) { router.POST("/security/breakglass/generate", handler.GenerateBreakGlass) w := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/security/breakglass/generate", nil) + req, _ := http.NewRequest("POST", "/security/breakglass/generate", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) var resp map[string]interface{} @@ -156,7 +156,7 @@ func TestSecurityHandler_ACL_DisabledWhenCerberusOff(t *testing.T) { router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -185,7 +185,7 @@ func TestSecurityHandler_CrowdSec_Mode_DBOverride(t *testing.T) { router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -209,7 +209,7 @@ func TestSecurityHandler_CrowdSec_ExternalMappedToDisabled_DBOverride(t *testing router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) var response map[string]interface{} @@ -233,7 +233,7 @@ func TestSecurityHandler_ExternalModeMappedToDisabled(t *testing.T) { router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) var response map[string]interface{} @@ -279,7 +279,7 @@ func TestSecurityHandler_Enable_Disable_WithAdminWhitelistAndToken(t *testing.T) assert.Equal(t, http.StatusOK, resp.Code) // Generate break-glass token - req = httptest.NewRequest("POST", "/api/v1/security/breakglass/generate", nil) + req = httptest.NewRequest("POST", "/api/v1/security/breakglass/generate", http.NoBody) resp = httptest.NewRecorder() router.ServeHTTP(resp, req) assert.Equal(t, http.StatusOK, resp.Code) diff --git a/backend/internal/api/handlers/security_handler_coverage_test.go b/backend/internal/api/handlers/security_handler_coverage_test.go index 613c07be..7959599a 100644 --- a/backend/internal/api/handlers/security_handler_coverage_test.go +++ b/backend/internal/api/handlers/security_handler_coverage_test.go @@ -102,7 +102,7 @@ func TestSecurityHandler_GetConfig_Success(t *testing.T) { router.GET("/security/config", handler.GetConfig) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/config", nil) + req, _ := http.NewRequest("GET", "/security/config", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -122,7 +122,7 @@ func TestSecurityHandler_GetConfig_NotFound(t *testing.T) { router.GET("/security/config", handler.GetConfig) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/config", nil) + req, _ := http.NewRequest("GET", "/security/config", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -147,7 +147,7 @@ func TestSecurityHandler_ListDecisions_Success(t *testing.T) { router.GET("/security/decisions", handler.ListDecisions) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/decisions", nil) + req, _ := http.NewRequest("GET", "/security/decisions", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -173,7 +173,7 @@ func TestSecurityHandler_ListDecisions_WithLimit(t *testing.T) { router.GET("/security/decisions", handler.ListDecisions) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/decisions?limit=2", nil) + req, _ := http.NewRequest("GET", "/security/decisions?limit=2", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -286,7 +286,7 @@ func TestSecurityHandler_ListRuleSets_Success(t *testing.T) { router.GET("/security/rulesets", handler.ListRuleSets) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/rulesets", nil) + req, _ := http.NewRequest("GET", "/security/rulesets", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -377,7 +377,7 @@ func TestSecurityHandler_DeleteRuleSet_Success(t *testing.T) { router.DELETE("/security/rulesets/:id", handler.DeleteRuleSet) w := httptest.NewRecorder() - req, _ := http.NewRequest("DELETE", "/security/rulesets/1", nil) + req, _ := http.NewRequest("DELETE", "/security/rulesets/1", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -397,7 +397,7 @@ func TestSecurityHandler_DeleteRuleSet_NotFound(t *testing.T) { router.DELETE("/security/rulesets/:id", handler.DeleteRuleSet) w := httptest.NewRecorder() - req, _ := http.NewRequest("DELETE", "/security/rulesets/999", nil) + req, _ := http.NewRequest("DELETE", "/security/rulesets/999", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) @@ -413,7 +413,7 @@ func TestSecurityHandler_DeleteRuleSet_InvalidID(t *testing.T) { router.DELETE("/security/rulesets/:id", handler.DeleteRuleSet) w := httptest.NewRecorder() - req, _ := http.NewRequest("DELETE", "/security/rulesets/invalid", nil) + req, _ := http.NewRequest("DELETE", "/security/rulesets/invalid", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusBadRequest, w.Code) @@ -431,7 +431,7 @@ func TestSecurityHandler_DeleteRuleSet_EmptyID(t *testing.T) { // This should hit the "id is required" check if we bypass routing w := httptest.NewRecorder() - req, _ := http.NewRequest("DELETE", "/security/rulesets/", nil) + req, _ := http.NewRequest("DELETE", "/security/rulesets/", http.NoBody) router.ServeHTTP(w, req) // Router won't match this path, so 404 @@ -517,7 +517,7 @@ func TestSecurityHandler_Enable_WithValidBreakGlassToken(t *testing.T) { // Generate a break-glass token w := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/security/breakglass/generate", nil) + req, _ := http.NewRequest("POST", "/security/breakglass/generate", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -609,7 +609,7 @@ func TestSecurityHandler_Disable_FromRemoteWithToken(t *testing.T) { // Generate token w := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/security/breakglass/generate", nil) + req, _ := http.NewRequest("POST", "/security/breakglass/generate", http.NoBody) router.ServeHTTP(w, req) var tokenResp map[string]string json.Unmarshal(w.Body.Bytes(), &tokenResp) @@ -689,7 +689,7 @@ func TestSecurityHandler_GenerateBreakGlass_NoConfig(t *testing.T) { router.POST("/security/breakglass/generate", handler.GenerateBreakGlass) w := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/security/breakglass/generate", nil) + req, _ := http.NewRequest("POST", "/security/breakglass/generate", http.NoBody) router.ServeHTTP(w, req) // Should succeed and create a new config with the token diff --git a/backend/internal/api/handlers/security_handler_rules_decisions_test.go b/backend/internal/api/handlers/security_handler_rules_decisions_test.go index 3953891c..0c46954d 100644 --- a/backend/internal/api/handlers/security_handler_rules_decisions_test.go +++ b/backend/internal/api/handlers/security_handler_rules_decisions_test.go @@ -57,7 +57,7 @@ func TestSecurityHandler_CreateAndListDecisionAndRulesets(t *testing.T) { require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &decisionResp)) require.NotNil(t, decisionResp["decision"]) - req = httptest.NewRequest(http.MethodGet, "/api/v1/security/decisions?limit=10", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/security/decisions?limit=10", http.NoBody) resp = httptest.NewRecorder() r.ServeHTTP(resp, req) if resp.Code != http.StatusOK { @@ -80,7 +80,7 @@ func TestSecurityHandler_CreateAndListDecisionAndRulesets(t *testing.T) { require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &rsResp)) require.NotNil(t, rsResp["ruleset"]) - req = httptest.NewRequest(http.MethodGet, "/api/v1/security/rulesets", nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/security/rulesets", http.NoBody) resp = httptest.NewRecorder() r.ServeHTTP(resp, req) if resp.Code != http.StatusOK { @@ -94,7 +94,7 @@ func TestSecurityHandler_CreateAndListDecisionAndRulesets(t *testing.T) { idFloat, ok := listRsResp["rulesets"][0]["id"].(float64) require.True(t, ok) id := int(idFloat) - req = httptest.NewRequest(http.MethodDelete, "/api/v1/security/rulesets/"+strconv.Itoa(id), nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/security/rulesets/"+strconv.Itoa(id), http.NoBody) resp = httptest.NewRecorder() r.ServeHTTP(resp, req) assert.Equal(t, http.StatusOK, resp.Code) @@ -159,7 +159,7 @@ func TestSecurityHandler_UpsertDeleteTriggersApplyConfig(t *testing.T) { // Read ID from DB var rs models.SecurityRuleSet assert.NoError(t, db.First(&rs).Error) - req = httptest.NewRequest(http.MethodDelete, "/api/v1/security/rulesets/"+strconv.Itoa(int(rs.ID)), nil) + req = httptest.NewRequest(http.MethodDelete, "/api/v1/security/rulesets/"+strconv.Itoa(int(rs.ID)), http.NoBody) resp = httptest.NewRecorder() r.ServeHTTP(resp, req) assert.Equal(t, http.StatusOK, resp.Code) diff --git a/backend/internal/api/handlers/security_handler_settings_test.go b/backend/internal/api/handlers/security_handler_settings_test.go index 013f5670..e48f7ff2 100644 --- a/backend/internal/api/handlers/security_handler_settings_test.go +++ b/backend/internal/api/handlers/security_handler_settings_test.go @@ -131,7 +131,7 @@ func TestSecurityHandler_GetStatus_RespectsSettingsTable(t *testing.T) { router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -173,7 +173,7 @@ func TestSecurityHandler_GetStatus_WAFModeFromSettings(t *testing.T) { router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -205,7 +205,7 @@ func TestSecurityHandler_GetStatus_RateLimitModeFromSettings(t *testing.T) { router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) diff --git a/backend/internal/api/handlers/security_handler_test_fixed.go b/backend/internal/api/handlers/security_handler_test_fixed.go index aaf1694a..23bf1efb 100644 --- a/backend/internal/api/handlers/security_handler_test_fixed.go +++ b/backend/internal/api/handlers/security_handler_test_fixed.go @@ -90,7 +90,7 @@ func TestSecurityHandler_GetStatus_Fixed(t *testing.T) { router.GET("/security/status", handler.GetStatus) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) + req, _ := http.NewRequest("GET", "/security/status", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, tt.expectedStatus, w.Code) diff --git a/backend/internal/api/handlers/settings_handler_test.go b/backend/internal/api/handlers/settings_handler_test.go index ba55faf7..e2bf788d 100644 --- a/backend/internal/api/handlers/settings_handler_test.go +++ b/backend/internal/api/handlers/settings_handler_test.go @@ -38,7 +38,7 @@ func TestSettingsHandler_GetSettings(t *testing.T) { router.GET("/settings", handler.GetSettings) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/settings", nil) + req, _ := http.NewRequest("GET", "/settings", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -148,7 +148,7 @@ func TestSettingsHandler_GetSMTPConfig(t *testing.T) { router.GET("/settings/smtp", handler.GetSMTPConfig) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/settings/smtp", nil) + req, _ := http.NewRequest("GET", "/settings/smtp", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -169,7 +169,7 @@ func TestSettingsHandler_GetSMTPConfig_Empty(t *testing.T) { router.GET("/settings/smtp", handler.GetSMTPConfig) w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/settings/smtp", nil) + req, _ := http.NewRequest("GET", "/settings/smtp", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -303,7 +303,7 @@ func TestSettingsHandler_TestSMTPConfig_NonAdmin(t *testing.T) { }) router.POST("/settings/smtp/test", handler.TestSMTPConfig) - req, _ := http.NewRequest("POST", "/settings/smtp/test", nil) + req, _ := http.NewRequest("POST", "/settings/smtp/test", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -321,7 +321,7 @@ func TestSettingsHandler_TestSMTPConfig_NotConfigured(t *testing.T) { }) router.POST("/settings/smtp/test", handler.TestSMTPConfig) - req, _ := http.NewRequest("POST", "/settings/smtp/test", nil) + req, _ := http.NewRequest("POST", "/settings/smtp/test", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/system_handler_test.go b/backend/internal/api/handlers/system_handler_test.go index 10647bdb..a7a69f3e 100644 --- a/backend/internal/api/handlers/system_handler_test.go +++ b/backend/internal/api/handlers/system_handler_test.go @@ -10,7 +10,7 @@ import ( func TestGetClientIPHeadersAndRemoteAddr(t *testing.T) { // Cloudflare header should win - req := httptest.NewRequest(http.MethodGet, "/", nil) + req := httptest.NewRequest(http.MethodGet, "/", http.NoBody) req.Header.Set("CF-Connecting-IP", "5.6.7.8") ip := getClientIP(req) if ip != "5.6.7.8" { @@ -18,7 +18,7 @@ func TestGetClientIPHeadersAndRemoteAddr(t *testing.T) { } // X-Real-IP should be preferred over RemoteAddr - req2 := httptest.NewRequest(http.MethodGet, "/", nil) + req2 := httptest.NewRequest(http.MethodGet, "/", http.NoBody) req2.Header.Set("X-Real-IP", "10.0.0.4") req2.RemoteAddr = "1.2.3.4:5678" ip2 := getClientIP(req2) @@ -27,7 +27,7 @@ func TestGetClientIPHeadersAndRemoteAddr(t *testing.T) { } // X-Forwarded-For returns first in list - req3 := httptest.NewRequest(http.MethodGet, "/", nil) + req3 := httptest.NewRequest(http.MethodGet, "/", http.NoBody) req3.Header.Set("X-Forwarded-For", "192.168.0.1, 192.168.0.2") ip3 := getClientIP(req3) if ip3 != "192.168.0.1" { @@ -35,7 +35,7 @@ func TestGetClientIPHeadersAndRemoteAddr(t *testing.T) { } // Fallback to remote addr port trimmed - req4 := httptest.NewRequest(http.MethodGet, "/", nil) + req4 := httptest.NewRequest(http.MethodGet, "/", http.NoBody) req4.RemoteAddr = "7.7.7.7:8888" ip4 := getClientIP(req4) if ip4 != "7.7.7.7" { @@ -51,7 +51,7 @@ func TestGetMyIPHandler(t *testing.T) { // With CF header w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/myip", nil) + req := httptest.NewRequest(http.MethodGet, "/myip", http.NoBody) req.Header.Set("CF-Connecting-IP", "5.6.7.8") r.ServeHTTP(w, req) if w.Code != http.StatusOK { diff --git a/backend/internal/api/handlers/testdb.go b/backend/internal/api/handlers/testdb.go index 44f87654..3b5799ac 100644 --- a/backend/internal/api/handlers/testdb.go +++ b/backend/internal/api/handlers/testdb.go @@ -12,7 +12,7 @@ import ( "gorm.io/gorm" ) -// openTestDB creates a SQLite in-memory DB unique per test and applies +// OpenTestDB creates a SQLite in-memory DB unique per test and applies // a busy timeout and WAL journal mode to reduce SQLITE locking during parallel tests. func OpenTestDB(t *testing.T) *gorm.DB { t.Helper() diff --git a/backend/internal/api/handlers/update_handler_test.go b/backend/internal/api/handlers/update_handler_test.go index 1405a231..5c50f730 100644 --- a/backend/internal/api/handlers/update_handler_test.go +++ b/backend/internal/api/handlers/update_handler_test.go @@ -37,7 +37,7 @@ func TestUpdateHandler_Check(t *testing.T) { r.GET("/api/v1/update", h.Check) // Test Request - req := httptest.NewRequest(http.MethodGet, "/api/v1/update", nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/update", http.NoBody) resp := httptest.NewRecorder() r.ServeHTTP(resp, req) @@ -62,7 +62,7 @@ func TestUpdateHandler_Check(t *testing.T) { rError := gin.New() rError.GET("/api/v1/update", hError.Check) - reqError := httptest.NewRequest(http.MethodGet, "/api/v1/update", nil) + reqError := httptest.NewRequest(http.MethodGet, "/api/v1/update", http.NoBody) respError := httptest.NewRecorder() rError.ServeHTTP(respError, reqError) @@ -80,7 +80,7 @@ func TestUpdateHandler_Check(t *testing.T) { rClientError := gin.New() rClientError.GET("/api/v1/update", hClientError.Check) - reqClientError := httptest.NewRequest(http.MethodGet, "/api/v1/update", nil) + reqClientError := httptest.NewRequest(http.MethodGet, "/api/v1/update", http.NoBody) respClientError := httptest.NewRecorder() rClientError.ServeHTTP(respClientError, reqClientError) diff --git a/backend/internal/api/handlers/uptime_handler_test.go b/backend/internal/api/handlers/uptime_handler_test.go index c840e3c3..11bb8c2d 100644 --- a/backend/internal/api/handlers/uptime_handler_test.go +++ b/backend/internal/api/handlers/uptime_handler_test.go @@ -52,7 +52,7 @@ func TestUptimeHandler_List(t *testing.T) { } db.Create(&monitor) - req, _ := http.NewRequest("GET", "/api/v1/uptime", nil) + req, _ := http.NewRequest("GET", "/api/v1/uptime", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -88,7 +88,7 @@ func TestUptimeHandler_GetHistory(t *testing.T) { CreatedAt: time.Now(), }) - req, _ := http.NewRequest("GET", "/api/v1/uptime/"+monitorID+"/history", nil) + req, _ := http.NewRequest("GET", "/api/v1/uptime/"+monitorID+"/history", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -108,7 +108,7 @@ func TestUptimeHandler_CheckMonitor(t *testing.T) { monitor := models.UptimeMonitor{ID: "check-mon-1", Name: "Check Monitor", Type: "http", URL: "http://example.com"} db.Create(&monitor) - req, _ := http.NewRequest("POST", "/api/v1/uptime/check-mon-1/check", nil) + req, _ := http.NewRequest("POST", "/api/v1/uptime/check-mon-1/check", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -118,7 +118,7 @@ func TestUptimeHandler_CheckMonitor(t *testing.T) { func TestUptimeHandler_CheckMonitor_NotFound(t *testing.T) { r, _ := setupUptimeHandlerTest(t) - req, _ := http.NewRequest("POST", "/api/v1/uptime/nonexistent/check", nil) + req, _ := http.NewRequest("POST", "/api/v1/uptime/nonexistent/check", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -193,7 +193,7 @@ func TestUptimeHandler_DeleteAndSync(t *testing.T) { monitor := models.UptimeMonitor{ID: "mon-delete", Name: "ToDelete", Type: "http", URL: "http://example.com"} db.Create(&monitor) - req, _ := http.NewRequest("DELETE", "/api/v1/uptime/mon-delete", nil) + req, _ := http.NewRequest("DELETE", "/api/v1/uptime/mon-delete", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -209,7 +209,7 @@ func TestUptimeHandler_DeleteAndSync(t *testing.T) { host := models.ProxyHost{UUID: "ph-up-1", Name: "Test Host", DomainNames: "sync.example.com", ForwardHost: "127.0.0.1", ForwardPort: 80, Enabled: true} db.Create(&host) - req, _ := http.NewRequest("POST", "/api/v1/uptime/sync", nil) + req, _ := http.NewRequest("POST", "/api/v1/uptime/sync", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -244,7 +244,7 @@ func TestUptimeHandler_DeleteAndSync(t *testing.T) { func TestUptimeHandler_Sync_Success(t *testing.T) { r, _ := setupUptimeHandlerTest(t) - req, _ := http.NewRequest("POST", "/api/v1/uptime/sync", nil) + req, _ := http.NewRequest("POST", "/api/v1/uptime/sync", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -259,7 +259,7 @@ func TestUptimeHandler_Delete_Error(t *testing.T) { r, db := setupUptimeHandlerTest(t) db.Exec("DROP TABLE IF EXISTS uptime_monitors") - req, _ := http.NewRequest("DELETE", "/api/v1/uptime/nonexistent", nil) + req, _ := http.NewRequest("DELETE", "/api/v1/uptime/nonexistent", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -270,7 +270,7 @@ func TestUptimeHandler_List_Error(t *testing.T) { r, db := setupUptimeHandlerTest(t) db.Exec("DROP TABLE IF EXISTS uptime_monitors") - req, _ := http.NewRequest("GET", "/api/v1/uptime", nil) + req, _ := http.NewRequest("GET", "/api/v1/uptime", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -281,7 +281,7 @@ func TestUptimeHandler_GetHistory_Error(t *testing.T) { r, db := setupUptimeHandlerTest(t) db.Exec("DROP TABLE IF EXISTS uptime_heartbeats") - req, _ := http.NewRequest("GET", "/api/v1/uptime/monitor-1/history", nil) + req, _ := http.NewRequest("GET", "/api/v1/uptime/monitor-1/history", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) diff --git a/backend/internal/api/handlers/user_handler_test.go b/backend/internal/api/handlers/user_handler_test.go index 52e2a404..0c870feb 100644 --- a/backend/internal/api/handlers/user_handler_test.go +++ b/backend/internal/api/handlers/user_handler_test.go @@ -34,7 +34,7 @@ func TestUserHandler_GetSetupStatus(t *testing.T) { r.GET("/setup", handler.GetSetupStatus) // No users -> setup required - req, _ := http.NewRequest("GET", "/setup", nil) + req, _ := http.NewRequest("GET", "/setup", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -110,7 +110,7 @@ func TestUserHandler_RegenerateAPIKey(t *testing.T) { }) r.POST("/api-key", handler.RegenerateAPIKey) - req, _ := http.NewRequest("POST", "/api-key", nil) + req, _ := http.NewRequest("POST", "/api-key", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -143,7 +143,7 @@ func TestUserHandler_GetProfile(t *testing.T) { }) r.GET("/profile", handler.GetProfile) - req, _ := http.NewRequest("GET", "/profile", nil) + req, _ := http.NewRequest("GET", "/profile", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -206,18 +206,18 @@ func TestUserHandler_Errors(t *testing.T) { }) // Test Unauthorized - req, _ := http.NewRequest("GET", "/profile-no-auth", nil) + req, _ := http.NewRequest("GET", "/profile-no-auth", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) - req, _ = http.NewRequest("POST", "/api-key-no-auth", nil) + req, _ = http.NewRequest("POST", "/api-key-no-auth", http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) // Test Not Found (GetProfile) - req, _ = http.NewRequest("GET", "/profile-not-found", nil) + req, _ = http.NewRequest("GET", "/profile-not-found", http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) @@ -229,7 +229,7 @@ func TestUserHandler_Errors(t *testing.T) { // However, let's see if we can force an error by closing DB? No, shared DB. // We can drop the table? db.Migrator().DropTable(&models.User{}) - req, _ = http.NewRequest("POST", "/api-key-not-found", nil) + req, _ = http.NewRequest("POST", "/api-key-not-found", http.NoBody) w = httptest.NewRecorder() r.ServeHTTP(w, req) // If table missing, Update should fail @@ -360,7 +360,7 @@ func TestUserHandler_UpdateProfile_Errors(t *testing.T) { // 1. Unauthorized (no userID) r.PUT("/profile-no-auth", handler.UpdateProfile) - req, _ := http.NewRequest("PUT", "/profile-no-auth", nil) + req, _ := http.NewRequest("PUT", "/profile-no-auth", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) @@ -409,7 +409,7 @@ func TestUserHandler_ListUsers_NonAdmin(t *testing.T) { }) r.GET("/users", handler.ListUsers) - req := httptest.NewRequest("GET", "/users", nil) + req := httptest.NewRequest("GET", "/users", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -433,7 +433,7 @@ func TestUserHandler_ListUsers_Admin(t *testing.T) { }) r.GET("/users", handler.ListUsers) - req := httptest.NewRequest("GET", "/users", nil) + req := httptest.NewRequest("GET", "/users", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -577,7 +577,7 @@ func TestUserHandler_GetUser_NonAdmin(t *testing.T) { }) r.GET("/users/:id", handler.GetUser) - req := httptest.NewRequest("GET", "/users/1", nil) + req := httptest.NewRequest("GET", "/users/1", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -594,7 +594,7 @@ func TestUserHandler_GetUser_InvalidID(t *testing.T) { }) r.GET("/users/:id", handler.GetUser) - req := httptest.NewRequest("GET", "/users/invalid", nil) + req := httptest.NewRequest("GET", "/users/invalid", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -611,7 +611,7 @@ func TestUserHandler_GetUser_NotFound(t *testing.T) { }) r.GET("/users/:id", handler.GetUser) - req := httptest.NewRequest("GET", "/users/999", nil) + req := httptest.NewRequest("GET", "/users/999", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -632,7 +632,7 @@ func TestUserHandler_GetUser_Success(t *testing.T) { }) r.GET("/users/:id", handler.GetUser) - req := httptest.NewRequest("GET", "/users/1", nil) + req := httptest.NewRequest("GET", "/users/1", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -759,7 +759,7 @@ func TestUserHandler_DeleteUser_NonAdmin(t *testing.T) { }) r.DELETE("/users/:id", handler.DeleteUser) - req := httptest.NewRequest("DELETE", "/users/1", nil) + req := httptest.NewRequest("DELETE", "/users/1", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -776,7 +776,7 @@ func TestUserHandler_DeleteUser_InvalidID(t *testing.T) { }) r.DELETE("/users/:id", handler.DeleteUser) - req := httptest.NewRequest("DELETE", "/users/invalid", nil) + req := httptest.NewRequest("DELETE", "/users/invalid", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -794,7 +794,7 @@ func TestUserHandler_DeleteUser_NotFound(t *testing.T) { }) r.DELETE("/users/:id", handler.DeleteUser) - req := httptest.NewRequest("DELETE", "/users/999", nil) + req := httptest.NewRequest("DELETE", "/users/999", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -816,7 +816,7 @@ func TestUserHandler_DeleteUser_Success(t *testing.T) { }) r.DELETE("/users/:id", handler.DeleteUser) - req := httptest.NewRequest("DELETE", "/users/1", nil) + req := httptest.NewRequest("DELETE", "/users/1", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -838,7 +838,7 @@ func TestUserHandler_DeleteUser_CannotDeleteSelf(t *testing.T) { }) r.DELETE("/users/:id", handler.DeleteUser) - req := httptest.NewRequest("DELETE", "/users/1", nil) + req := httptest.NewRequest("DELETE", "/users/1", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -976,7 +976,7 @@ func TestUserHandler_ValidateInvite_MissingToken(t *testing.T) { r := gin.New() r.GET("/invite/validate", handler.ValidateInvite) - req := httptest.NewRequest("GET", "/invite/validate", nil) + req := httptest.NewRequest("GET", "/invite/validate", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -989,7 +989,7 @@ func TestUserHandler_ValidateInvite_InvalidToken(t *testing.T) { r := gin.New() r.GET("/invite/validate", handler.ValidateInvite) - req := httptest.NewRequest("GET", "/invite/validate?token=invalidtoken", nil) + req := httptest.NewRequest("GET", "/invite/validate?token=invalidtoken", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -1014,7 +1014,7 @@ func TestUserHandler_ValidateInvite_ExpiredToken(t *testing.T) { r := gin.New() r.GET("/invite/validate", handler.ValidateInvite) - req := httptest.NewRequest("GET", "/invite/validate?token=expiredtoken123", nil) + req := httptest.NewRequest("GET", "/invite/validate?token=expiredtoken123", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -1039,7 +1039,7 @@ func TestUserHandler_ValidateInvite_AlreadyAccepted(t *testing.T) { r := gin.New() r.GET("/invite/validate", handler.ValidateInvite) - req := httptest.NewRequest("GET", "/invite/validate?token=acceptedtoken123", nil) + req := httptest.NewRequest("GET", "/invite/validate?token=acceptedtoken123", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -1064,7 +1064,7 @@ func TestUserHandler_ValidateInvite_Success(t *testing.T) { r := gin.New() r.GET("/invite/validate", handler.ValidateInvite) - req := httptest.NewRequest("GET", "/invite/validate?token=validtoken123", nil) + req := httptest.NewRequest("GET", "/invite/validate?token=validtoken123", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -1333,7 +1333,7 @@ func TestGetBaseURL(t *testing.T) { c.String(200, url) }) - req := httptest.NewRequest("GET", "/test", nil) + req := httptest.NewRequest("GET", "/test", http.NoBody) req.Host = "example.com" req.Header.Set("X-Forwarded-Proto", "https") w := httptest.NewRecorder() diff --git a/backend/internal/api/middleware/auth_test.go b/backend/internal/api/middleware/auth_test.go index 7dc3edcb..7fb4e077 100644 --- a/backend/internal/api/middleware/auth_test.go +++ b/backend/internal/api/middleware/auth_test.go @@ -33,7 +33,7 @@ func TestAuthMiddleware_MissingHeader(t *testing.T) { c.Status(http.StatusOK) }) - req, _ := http.NewRequest("GET", "/test", nil) + req, _ := http.NewRequest("GET", "/test", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -53,7 +53,7 @@ func TestRequireRole_Success(t *testing.T) { c.Status(http.StatusOK) }) - req, _ := http.NewRequest("GET", "/test", nil) + req, _ := http.NewRequest("GET", "/test", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -72,7 +72,7 @@ func TestRequireRole_Forbidden(t *testing.T) { c.Status(http.StatusOK) }) - req, _ := http.NewRequest("GET", "/test", nil) + req, _ := http.NewRequest("GET", "/test", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -95,7 +95,7 @@ func TestAuthMiddleware_Cookie(t *testing.T) { c.Status(http.StatusOK) }) - req, _ := http.NewRequest("GET", "/test", nil) + req, _ := http.NewRequest("GET", "/test", http.NoBody) req.AddCookie(&http.Cookie{Name: "auth_token", Value: token}) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -119,7 +119,7 @@ func TestAuthMiddleware_ValidToken(t *testing.T) { c.Status(http.StatusOK) }) - req, _ := http.NewRequest("GET", "/test", nil) + req, _ := http.NewRequest("GET", "/test", http.NoBody) req.Header.Set("Authorization", "Bearer "+token) w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -137,7 +137,7 @@ func TestAuthMiddleware_InvalidToken(t *testing.T) { c.Status(http.StatusOK) }) - req, _ := http.NewRequest("GET", "/test", nil) + req, _ := http.NewRequest("GET", "/test", http.NoBody) req.Header.Set("Authorization", "Bearer invalid-token") w := httptest.NewRecorder() r.ServeHTTP(w, req) @@ -155,7 +155,7 @@ func TestRequireRole_MissingRoleInContext(t *testing.T) { c.Status(http.StatusOK) }) - req, _ := http.NewRequest("GET", "/test", nil) + req, _ := http.NewRequest("GET", "/test", http.NoBody) w := httptest.NewRecorder() r.ServeHTTP(w, req) diff --git a/backend/internal/api/middleware/recovery_test.go b/backend/internal/api/middleware/recovery_test.go index fbe12240..64675fdd 100644 --- a/backend/internal/api/middleware/recovery_test.go +++ b/backend/internal/api/middleware/recovery_test.go @@ -27,7 +27,7 @@ func TestRecoveryLogsStacktraceVerbose(t *testing.T) { panic("test panic") }) - req := httptest.NewRequest(http.MethodGet, "/panic", nil) + req := httptest.NewRequest(http.MethodGet, "/panic", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -62,7 +62,7 @@ func TestRecoveryLogsBriefWhenNotVerbose(t *testing.T) { panic("brief panic") }) - req := httptest.NewRequest(http.MethodGet, "/panic", nil) + req := httptest.NewRequest(http.MethodGet, "/panic", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -95,7 +95,7 @@ func TestRecoverySanitizesHeadersAndPath(t *testing.T) { panic("sensitive panic") }) - req := httptest.NewRequest(http.MethodGet, "/panic", nil) + req := httptest.NewRequest(http.MethodGet, "/panic", http.NoBody) // Add sensitive header that should be redacted req.Header.Set("Authorization", "Bearer secret-token") w := httptest.NewRecorder() diff --git a/backend/internal/api/middleware/request_id_test.go b/backend/internal/api/middleware/request_id_test.go index 69598b13..816c4f09 100644 --- a/backend/internal/api/middleware/request_id_test.go +++ b/backend/internal/api/middleware/request_id_test.go @@ -24,7 +24,7 @@ func TestRequestIDAddsHeaderAndLogger(t *testing.T) { c.String(200, "ok") }) - req := httptest.NewRequest(http.MethodGet, "/test", nil) + req := httptest.NewRequest(http.MethodGet, "/test", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) diff --git a/backend/internal/api/middleware/request_logger_test.go b/backend/internal/api/middleware/request_logger_test.go index 8282c81e..8ff8a494 100644 --- a/backend/internal/api/middleware/request_logger_test.go +++ b/backend/internal/api/middleware/request_logger_test.go @@ -23,7 +23,7 @@ func TestRequestLoggerSanitizesPath(t *testing.T) { router.Use(RequestLogger()) router.GET(longPath, func(c *gin.Context) { c.Status(http.StatusOK) }) - req := httptest.NewRequest(http.MethodGet, longPath, nil) + req := httptest.NewRequest(http.MethodGet, longPath, http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -56,7 +56,7 @@ func TestRequestLoggerIncludesRequestID(t *testing.T) { router.Use(RequestLogger()) router.GET("/ok", func(c *gin.Context) { c.String(200, "ok") }) - req := httptest.NewRequest(http.MethodGet, "/ok", nil) + req := httptest.NewRequest(http.MethodGet, "/ok", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { diff --git a/backend/internal/api/middleware/security_test.go b/backend/internal/api/middleware/security_test.go index d83cf7bf..99d5f6de 100644 --- a/backend/internal/api/middleware/security_test.go +++ b/backend/internal/api/middleware/security_test.go @@ -117,7 +117,7 @@ func TestSecurityHeaders(t *testing.T) { c.String(http.StatusOK, "OK") }) - req := httptest.NewRequest(http.MethodGet, "/test", nil) + req := httptest.NewRequest(http.MethodGet, "/test", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) @@ -141,7 +141,7 @@ func TestSecurityHeadersCustomCSP(t *testing.T) { c.String(http.StatusOK, "OK") }) - req := httptest.NewRequest(http.MethodGet, "/test", nil) + req := httptest.NewRequest(http.MethodGet, "/test", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) diff --git a/backend/internal/api/tests/integration_test.go b/backend/internal/api/tests/integration_test.go index 71574633..5bb0224f 100644 --- a/backend/internal/api/tests/integration_test.go +++ b/backend/internal/api/tests/integration_test.go @@ -38,7 +38,7 @@ func TestIntegration_WAF_BlockAndMonitor(t *testing.T) { // Block mode should reject suspicious payload on an API route covered by middleware rBlock, _ := newServer("block") - req := httptest.NewRequest(http.MethodGet, "/api/v1/remote-servers?test=