diff --git a/backend/internal/api/handlers/security_handler_test.go b/backend/internal/api/handlers/security_handler_test.go deleted file mode 100644 index 12cbe2d9..00000000 --- a/backend/internal/api/handlers/security_handler_test.go +++ /dev/null @@ -1,1182 +0,0 @@ -//go:build ignore - -package handlers - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "gorm.io/driver/sqlite" - "gorm.io/gorm" - - "github.com/Wikid82/charon/backend/internal/config" - "github.com/Wikid82/charon/backend/internal/models" -) - -// Intentionally ignored by build to avoid duplicate test artifacts during initial scaffolding -// Use security_handler_clean_test.go for canonical tests. - -func setupSecurityTestRouter(t *testing.T) (*gin.Engine, *gorm.DB) { - db, err := gorm.Open(sqlite.Open("file::memory:?mode=memory&cache=shared"), &gorm.Config{}) - require.NoError(t, err) - require.NoError(t, db.AutoMigrate(&models.SecurityConfig{})) - - r := gin.New() - api := r.Group("/api/v1") - cfg := config.SecurityConfig{} - h := NewSecurityHandler(cfg, db, nil) - api.GET("/security/status", h.GetStatus) - api.GET("/security/config", h.GetConfig) - api.POST("/security/config", h.UpdateConfig) - api.POST("/security/enable", h.Enable) - api.POST("/security/disable", h.Disable) - api.POST("/security/breakglass/generate", h.GenerateBreakGlass) - return r, db -} - -func TestSecurityHandler_ConfigUpsertAndBreakGlass(t *testing.T) { - r, _ := setupSecurityTestRouter(t) - - body := `{"name":"default","admin_whitelist":"invalid-cidr"}` - req := httptest.NewRequest(http.MethodPost, "/api/v1/security/config", strings.NewReader(body)) - req.Header.Set("Content-Type", "application/json") - resp := httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusBadRequest, resp.Code) - - body = `{"name":"default","admin_whitelist":"127.0.0.1/32"}` - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/config", strings.NewReader(body)) - req.Header.Set("Content-Type", "application/json") - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusOK, resp.Code) - - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/breakglass/generate", nil) - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusOK, resp.Code) - var tokenResp map[string]string - require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &tokenResp)) - require.NotEmpty(t, tokenResp["token"]) -} - -func TestSecurityHandler_GetStatus(t *testing.T) { - handler := NewSecurityHandler(config.SecurityConfig{CrowdSecMode: "disabled", WAFMode: "disabled", RateLimitMode: "disabled", ACLMode: "disabled"}, nil, nil) - router := gin.New() - router.GET("/security/status", handler.GetStatus) - - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) - router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Code) -} -package handlers - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "gorm.io/driver/sqlite" - "gorm.io/gorm" - - "github.com/Wikid82/charon/backend/internal/config" - "github.com/Wikid82/charon/backend/internal/models" -) - -func setupSecurityTestRouter(t *testing.T) (*gin.Engine, *gorm.DB) { - t.Helper() - db, err := gorm.Open(sqlite.Open("file::memory:?mode=memory&cache=shared"), &gorm.Config{}) - require.NoError(t, err) - require.NoError(t, db.AutoMigrate(&models.SecurityConfig{})) - - r := gin.New() - api := r.Group("/api/v1") - cfg := config.SecurityConfig{} - h := NewSecurityHandler(cfg, db, nil) - api.GET("/security/status", h.GetStatus) - api.GET("/security/config", h.GetConfig) - api.POST("/security/config", h.UpdateConfig) - api.POST("/security/enable", h.Enable) - api.POST("/security/disable", h.Disable) - api.POST("/security/breakglass/generate", h.GenerateBreakGlass) - return r, db -} - -func TestSecurityHandler_ConfigAndBreakGlassLifecycle(t *testing.T) { - r, _ := setupSecurityTestRouter(t) - - // Invalid admin whitelist - body := `{"name":"default","admin_whitelist":"invalid-cidr"}` - req := httptest.NewRequest(http.MethodPost, "/api/v1/security/config", strings.NewReader(body)) - req.Header.Set("Content-Type", "application/json") - resp := httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusBadRequest, resp.Code) - - // Now update config with a valid admin whitelist - body = `{"name":"default","admin_whitelist":"127.0.0.1/32"}` - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/config", strings.NewReader(body)) - req.Header.Set("Content-Type", "application/json") - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusOK, resp.Code) - - // Generate break-glass token - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/breakglass/generate", nil) - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusOK, resp.Code) - var tokenResp map[string]string - require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &tokenResp)) - token := tokenResp["token"] - require.NotEmpty(t, token) - - // Enable using admin whitelist (X-Forwarded-For) - this should succeed - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/enable", strings.NewReader(`{}`)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-Forwarded-For", "127.0.0.1") - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusOK, resp.Code) - - // Disable using break glass token - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/disable", strings.NewReader(`{"break_glass_token":"`+token+`"}`)) - req.Header.Set("Content-Type", "application/json") - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusOK, resp.Code) -} - -func TestSecurityHandler_GetStatus(t *testing.T) { - gin.SetMode(gin.TestMode) - - tests := []struct { - name string - cfg config.SecurityConfig - expectedStatus int - expectedBody map[string]interface{} - }{ - { - name: "All Disabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "disabled", - WAFMode: "disabled", - RateLimitMode: "disabled", - ACLMode: "disabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": false}, - "crowdsec": map[string]interface{}{ - "mode": "disabled", - "api_url": "", - "enabled": false, - }, - "waf": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "rate_limit": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "acl": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - }, - }, - { - name: "All Enabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "local", - WAFMode: "enabled", - RateLimitMode: "enabled", - ACLMode: "enabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": true}, - "crowdsec": map[string]interface{}{ - "mode": "local", - "api_url": "", - "enabled": true, - }, - "waf": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "rate_limit": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "acl": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := NewSecurityHandler(tt.cfg, nil, nil) - router := gin.New() - router.GET("/security/status", handler.GetStatus) - - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) - router.ServeHTTP(w, req) - - assert.Equal(t, tt.expectedStatus, w.Code) - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - - expectedJSON, _ := json.Marshal(tt.expectedBody) - var expectedNormalized map[string]interface{} - json.Unmarshal(expectedJSON, &expectedNormalized) - - assert.Equal(t, expectedNormalized, response) - }) - } -} -package handlers - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "gorm.io/driver/sqlite" - "gorm.io/gorm" - - "github.com/Wikid82/charon/backend/internal/config" - "github.com/Wikid82/charon/backend/internal/models" -) - -func setupSecurityTestRouter(t *testing.T) (*gin.Engine, *gorm.DB) { - t.Helper() - db, err := gorm.Open(sqlite.Open("file::memory:?mode=memory&cache=shared"), &gorm.Config{}) - require.NoError(t, err) - require.NoError(t, db.AutoMigrate(&models.SecurityConfig{})) - - r := gin.New() - api := r.Group("/api/v1") - cfg := config.SecurityConfig{} - h := NewSecurityHandler(cfg, db, nil) - api.GET("/security/status", h.GetStatus) - api.GET("/security/config", h.GetConfig) - api.POST("/security/config", h.UpdateConfig) - api.POST("/security/enable", h.Enable) - api.POST("/security/disable", h.Disable) - api.POST("/security/breakglass/generate", h.GenerateBreakGlass) - return r, db -} - -func TestSecurityHandler_ConfigAndBreakGlassLifecycle(t *testing.T) { - r, _ := setupSecurityTestRouter(t) - - // Invalid admin whitelist - body := `{"name":"default","admin_whitelist":"invalid-cidr"}` - req := httptest.NewRequest(http.MethodPost, "/api/v1/security/config", strings.NewReader(body)) - req.Header.Set("Content-Type", "application/json") - resp := httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusBadRequest, resp.Code) - - // Now update config with a valid admin whitelist - body = `{"name":"default","admin_whitelist":"127.0.0.1/32"}` - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/config", strings.NewReader(body)) - req.Header.Set("Content-Type", "application/json") - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusOK, resp.Code) - - // Generate break-glass token - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/breakglass/generate", nil) - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusOK, resp.Code) - var tokenResp map[string]string - require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &tokenResp)) - token := tokenResp["token"] - require.NotEmpty(t, token) - - // Enable using admin whitelist (X-Forwarded-For) - this should succeed - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/enable", strings.NewReader(`{}`)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-Forwarded-For", "127.0.0.1") - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusOK, resp.Code) - - // Disable using break glass token - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/disable", strings.NewReader(`{"break_glass_token":"`+token+`"}`)) - req.Header.Set("Content-Type", "application/json") - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - assert.Equal(t, http.StatusOK, resp.Code) -} - -func TestSecurityHandler_GetStatus(t *testing.T) { - gin.SetMode(gin.TestMode) - - tests := []struct { - name string - cfg config.SecurityConfig - expectedStatus int - expectedBody map[string]interface{} - }{ - { - name: "All Disabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "disabled", - WAFMode: "disabled", - RateLimitMode: "disabled", - ACLMode: "disabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": false}, - "crowdsec": map[string]interface{}{ - "mode": "disabled", - "api_url": "", - "enabled": false, - }, - "waf": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "rate_limit": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "acl": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - }, - }, - { - name: "All Enabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "local", - WAFMode: "enabled", - RateLimitMode: "enabled", - ACLMode: "enabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": true}, - "crowdsec": map[string]interface{}{ - "mode": "local", - "api_url": "", - "enabled": true, - }, - "waf": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "rate_limit": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "acl": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := NewSecurityHandler(tt.cfg, nil, nil) - router := gin.New() - router.GET("/security/status", handler.GetStatus) - - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) - router.ServeHTTP(w, req) - - assert.Equal(t, tt.expectedStatus, w.Code) - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - - expectedJSON, _ := json.Marshal(tt.expectedBody) - var expectedNormalized map[string]interface{} - json.Unmarshal(expectedJSON, &expectedNormalized) - - assert.Equal(t, expectedNormalized, response) - }) - } -} -package handlers - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/require" - "gorm.io/driver/sqlite" - "gorm.io/gorm" - - "github.com/Wikid82/charon/backend/internal/models" - "github.com/Wikid82/charon/backend/internal/config" -) - -func setupSecurityTestRouter(t *testing.T) (*gin.Engine, *gorm.DB) { - t.Helper() - db, err := gorm.Open(sqlite.Open("file::memory:?mode=memory&cache=shared"), &gorm.Config{}) - require.NoError(t, err) - require.NoError(t, db.AutoMigrate(&models.SecurityConfig{})) - - r := gin.New() - api := r.Group("/api/v1") - cfg := config.SecurityConfig{} - h := NewSecurityHandler(cfg, db, nil) - // The NewSecurityHandler above matches pattern; here we'll use the real handler - // Register the routes manually - api.GET("/security/status", h.GetStatus) - api.GET("/security/config", h.GetConfig) - api.POST("/security/config", h.UpdateConfig) - api.POST("/security/enable", h.Enable) - api.POST("/security/disable", h.Disable) - api.POST("/security/breakglass/generate", h.GenerateBreakGlass) - return r, db -} - -func TestSecurityHandler_ConfigAndBreakGlassLifecycle(t *testing.T) { - r, _ := setupSecurityTestRouter(t) - - // Invalid admin whitelist JSON - missing because we accept comma-separated CIDRs - body := `{"name":"default","admin_whitelist":"invalid-cidr"}` - req := httptest.NewRequest(http.MethodPost, "/api/v1/security/config", strings.NewReader(body)) - req.Header.Set("Content-Type", "application/json") - resp := httptest.NewRecorder() - r.ServeHTTP(resp, req) - require.Equal(t, http.StatusBadRequest, resp.Code) - - // Now update config with a valid admin whitelist - body = `{"name":"default","admin_whitelist":"127.0.0.1/32"}` - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/config", strings.NewReader(body)) - req.Header.Set("Content-Type", "application/json") - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - require.Equal(t, http.StatusOK, resp.Code) - - // Generate break-glass token - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/breakglass/generate", nil) - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - require.Equal(t, http.StatusOK, resp.Code) - var tokenResp map[string]string - require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &tokenResp)) - token := tokenResp["token"] - require.NotEmpty(t, token) - - // Enable using admin whitelist (X-Forwarded-For) - this should succeed - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/enable", strings.NewReader(`{}`)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-Forwarded-For", "127.0.0.1") - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - require.Equal(t, http.StatusOK, resp.Code) - - // Disable using break glass token - req = httptest.NewRequest(http.MethodPost, "/api/v1/security/disable", strings.NewReader(`{"break_glass_token":"`+token+`"}`)) - req.Header.Set("Content-Type", "application/json") - resp = httptest.NewRecorder() - r.ServeHTTP(resp, req) - require.Equal(t, http.StatusOK, resp.Code) -} -package handlers - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - - "github.com/Wikid82/charon/backend/internal/config" -) - -func TestSecurityHandler_GetStatus(t *testing.T) { - gin.SetMode(gin.TestMode) - - tests := []struct { - name string - cfg config.SecurityConfig - expectedStatus int - expectedBody map[string]interface{} - }{ - { - name: "All Disabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "disabled", - WAFMode: "disabled", - RateLimitMode: "disabled", - ACLMode: "disabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": false}, - "crowdsec": map[string]interface{}{ - "mode": "disabled", - "api_url": "", - "enabled": false, - }, - "waf": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "rate_limit": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "acl": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - }, - }, - { - name: "All Enabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "local", - WAFMode: "enabled", - RateLimitMode: "enabled", - ACLMode: "enabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": true}, - "crowdsec": map[string]interface{}{ - "mode": "local", - "api_url": "", - "enabled": true, - }, - "waf": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "rate_limit": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "acl": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := NewSecurityHandler(tt.cfg, nil, nil) - router := gin.New() - router.GET("/security/status", handler.GetStatus) - - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) - router.ServeHTTP(w, req) - - assert.Equal(t, tt.expectedStatus, w.Code) - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - - // Helper to convert map[string]interface{} to JSON and back to normalize types - // (e.g. int vs float64) - expectedJSON, _ := json.Marshal(tt.expectedBody) - var expectedNormalized map[string]interface{} - json.Unmarshal(expectedJSON, &expectedNormalized) - - assert.Equal(t, expectedNormalized, response) - }) - } -} -package handlers - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - - "github.com/Wikid82/charon/backend/internal/config" -) - -func TestSecurityHandler_GetStatus(t *testing.T) { - gin.SetMode(gin.TestMode) - - tests := []struct { - name string - cfg config.SecurityConfig - expectedStatus int - expectedBody map[string]interface{} - }{ - { - name: "All Disabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "disabled", - WAFMode: "disabled", - RateLimitMode: "disabled", - ACLMode: "disabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": false}, - "crowdsec": map[string]interface{}{ - "mode": "disabled", - "api_url": "", - "enabled": false, - }, - "waf": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "rate_limit": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "acl": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - }, - }, - { - name: "All Enabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "local", - WAFMode: "enabled", - RateLimitMode: "enabled", - ACLMode: "enabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": true}, - "crowdsec": map[string]interface{}{ - "mode": "local", - "api_url": "", - "enabled": true, - }, - "waf": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "rate_limit": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "acl": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := NewSecurityHandler(tt.cfg, nil, nil) - router := gin.New() - router.GET("/security/status", handler.GetStatus) - - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) - router.ServeHTTP(w, req) - - assert.Equal(t, tt.expectedStatus, w.Code) - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - - // Helper to convert map[string]interface{} to JSON and back to normalize types - // (e.g. int vs float64) - expectedJSON, _ := json.Marshal(tt.expectedBody) - var expectedNormalized map[string]interface{} - json.Unmarshal(expectedJSON, &expectedNormalized) - - assert.Equal(t, expectedNormalized, response) - }) - } -} -package handlers - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - - "github.com/Wikid82/charon/backend/internal/config" -) - -func TestSecurityHandler_GetStatus(t *testing.T) { - gin.SetMode(gin.TestMode) - - tests := []struct { - name string - cfg config.SecurityConfig - expectedStatus int - expectedBody map[string]interface{} - }{ - { - name: "All Disabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "disabled", - WAFMode: "disabled", - RateLimitMode: "disabled", - ACLMode: "disabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": false}, - "crowdsec": map[string]interface{}{ - "mode": "disabled", - "api_url": "", - "enabled": false, - }, - "waf": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "rate_limit": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "acl": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - }, - }, - { - name: "All Enabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "local", - WAFMode: "enabled", - RateLimitMode: "enabled", - ACLMode: "enabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": true}, - "crowdsec": map[string]interface{}{ - "mode": "local", - "api_url": "", - "enabled": true, - }, - "waf": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "rate_limit": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "acl": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := NewSecurityHandler(tt.cfg, nil, nil) - router := gin.New() - router.GET("/security/status", handler.GetStatus) - - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) - router.ServeHTTP(w, req) - - assert.Equal(t, tt.expectedStatus, w.Code) - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - - // Helper to convert map[string]interface{} to JSON and back to normalize types - // (e.g. int vs float64) - expectedJSON, _ := json.Marshal(tt.expectedBody) - var expectedNormalized map[string]interface{} - json.Unmarshal(expectedJSON, &expectedNormalized) - - assert.Equal(t, expectedNormalized, response) - }) - } -} -package handlers - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - - "github.com/Wikid82/charon/backend/internal/config" -) - -func TestSecurityHandler_GetStatus(t *testing.T) { - gin.SetMode(gin.TestMode) - - tests := []struct { - name string - cfg config.SecurityConfig - expectedStatus int - expectedBody map[string]interface{} - }{ - { - name: "All Disabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "disabled", - WAFMode: "disabled", - RateLimitMode: "disabled", - ACLMode: "disabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": false}, - "crowdsec": map[string]interface{}{ - "mode": "disabled", - "api_url": "", - "enabled": false, - }, - "waf": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "rate_limit": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "acl": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - }, - }, - { - name: "All Enabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "local", - WAFMode: "enabled", - RateLimitMode: "enabled", - ACLMode: "enabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": true}, - "crowdsec": map[string]interface{}{ - "mode": "local", - "api_url": "", - "enabled": true, - }, - "waf": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "rate_limit": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "acl": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := NewSecurityHandler(tt.cfg, nil, nil) - router := gin.New() - router.GET("/security/status", handler.GetStatus) - - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) - router.ServeHTTP(w, req) - - assert.Equal(t, tt.expectedStatus, w.Code) - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - - // Helper to convert map[string]interface{} to JSON and back to normalize types - // (e.g. int vs float64) - expectedJSON, _ := json.Marshal(tt.expectedBody) - var expectedNormalized map[string]interface{} - json.Unmarshal(expectedJSON, &expectedNormalized) - - assert.Equal(t, expectedNormalized, response) - }) - } -} -package handlers - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - - "github.com/Wikid82/charon/backend/internal/config" -) - -func TestSecurityHandler_GetStatus(t *testing.T) { - gin.SetMode(gin.TestMode) - - tests := []struct { - name string - cfg config.SecurityConfig - expectedStatus int - expectedBody map[string]interface{} - }{ - { - name: "All Disabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "disabled", - WAFMode: "disabled", - RateLimitMode: "disabled", - ACLMode: "disabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": false}, - "crowdsec": map[string]interface{}{ - "mode": "disabled", - "api_url": "", - "enabled": false, - }, - "waf": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "rate_limit": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "acl": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - }, - }, - { - name: "All Enabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "local", - WAFMode: "enabled", - RateLimitMode: "enabled", - ACLMode: "enabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": true}, - "crowdsec": map[string]interface{}{ - "mode": "local", - "api_url": "", - "enabled": true, - }, - "waf": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "rate_limit": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "acl": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := NewSecurityHandler(tt.cfg, nil, nil) - router := gin.New() - router.GET("/security/status", handler.GetStatus) - - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) - router.ServeHTTP(w, req) - - assert.Equal(t, tt.expectedStatus, w.Code) - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - - // Helper to convert map[string]interface{} to JSON and back to normalize types - // (e.g. int vs float64) - expectedJSON, _ := json.Marshal(tt.expectedBody) - var expectedNormalized map[string]interface{} - json.Unmarshal(expectedJSON, &expectedNormalized) - - assert.Equal(t, expectedNormalized, response) - }) - } -} -package handlers - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - - "github.com/Wikid82/charon/backend/internal/config" -) - -func TestSecurityHandler_GetStatus(t *testing.T) { - gin.SetMode(gin.TestMode) - - tests := []struct { - name string - cfg config.SecurityConfig - expectedStatus int - expectedBody map[string]interface{} - }{ - { - expectedBody: map[string]interface{}{ - "cerberus": map[string]interface{}{"enabled": false}, - cfg: config.SecurityConfig{ - CrowdSecMode: "disabled", - WAFMode: "disabled", - RateLimitMode: "disabled", - ACLMode: "disabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "crowdsec": map[string]interface{}{ - "mode": "disabled", - "api_url": "", - "enabled": false, - }, - "waf": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "rate_limit": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - "acl": map[string]interface{}{ - "mode": "disabled", - "enabled": false, - }, - }, - }, - { - name: "All Enabled", - cfg: config.SecurityConfig{ - CrowdSecMode: "local", - WAFMode: "enabled", - RateLimitMode: "enabled", - ACLMode: "enabled", - }, - expectedStatus: http.StatusOK, - expectedBody: map[string]interface{}{ - "crowdsec": map[string]interface{}{ - "mode": "local", - "api_url": "", - "enabled": true, - }, - "waf": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "rate_limit": map[string]interface{}{ - "mode": "enabled", - "enabled": true, - }, - "acl": map[string]interface{}{ - handler := NewSecurityHandler(tt.cfg, nil, nil) - "enabled": true, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := NewSecurityHandler(tt.cfg, nil, nil) - router := gin.New() - router.GET("/security/status", handler.GetStatus) - - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/security/status", nil) - router.ServeHTTP(w, req) - - assert.Equal(t, tt.expectedStatus, w.Code) - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - - // Helper to convert map[string]interface{} to JSON and back to normalize types - // (e.g. int vs float64) - expectedJSON, _ := json.Marshal(tt.expectedBody) - var expectedNormalized map[string]interface{} - json.Unmarshal(expectedJSON, &expectedNormalized) - - assert.Equal(t, expectedNormalized, response) - }) - } -}