package handlers import ( "authorization/models" "bytes" "context" "encoding/json" "net/http" "net/http/httptest" "testing" ) func TestInitAuthService(t *testing.T) { // Skip this test if database is not available // In unit tests without DB, this would panic t.Skip("Skipping test - requires database connection") } func TestAuthorizeHandler_NoJWTClaims(t *testing.T) { // Setup req := httptest.NewRequest("POST", "/v1/auth/check", nil) w := httptest.NewRecorder() // Execute AuthorizeHandler(w, req) // Assert if w.Code != http.StatusUnauthorized { t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) } } func TestAuthorizeHandler_InvalidJSON(t *testing.T) { // Setup - no need to init service, we're testing JSON parsing before auth claims := &models.Claims{ UserID: "user123", Username: "testuser", Role: "admin", } req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBufferString("invalid json")) ctx := context.WithValue(req.Context(), models.ContextKey("claims"), claims) req = req.WithContext(ctx) w := httptest.NewRecorder() // Execute AuthorizeHandler(w, req) // Assert if w.Code != http.StatusBadRequest { t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code) } } func TestAuthorizeHandler_MissingRequiredFields(t *testing.T) { testCases := []struct { name string payload models.AuthorizationContext }{ { name: "Missing UserID", payload: models.AuthorizationContext{Resource: "document", Action: "read"}, }, { name: "Missing Resource", payload: models.AuthorizationContext{UserID: "user123", Action: "read"}, }, { name: "Missing Action", payload: models.AuthorizationContext{UserID: "user123", Resource: "document"}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { claims := &models.Claims{ UserID: "user123", Username: "testuser", Role: "admin", } body, _ := json.Marshal(tc.payload) req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBuffer(body)) ctx := context.WithValue(req.Context(), models.ContextKey("claims"), claims) req = req.WithContext(ctx) w := httptest.NewRecorder() AuthorizeHandler(w, req) if w.Code != http.StatusBadRequest { t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code) } }) } } func TestAuthorizeHandler_UserIDMismatch(t *testing.T) { // Setup claims := &models.Claims{ UserID: "user123", Username: "testuser", Role: "admin", } payload := models.AuthorizationContext{ UserID: "differentUser", Resource: "document", Action: "read", } body, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBuffer(body)) ctx := context.WithValue(req.Context(), models.ContextKey("claims"), claims) req = req.WithContext(ctx) w := httptest.NewRecorder() // Execute AuthorizeHandler(w, req) // Assert if w.Code != http.StatusForbidden { t.Errorf("Expected status %d, got %d", http.StatusForbidden, w.Code) } } func TestAuthorizeHandler_NilMaps(t *testing.T) { // Skip this test if database is not available if authService == nil { t.Skip("Skipping test - requires database connection") } // Setup - test that nil maps are initialized and don't cause panics claims := &models.Claims{ UserID: "user123", Username: "testuser", Role: "admin", } payload := models.AuthorizationContext{ UserID: "user123", Resource: "document", Action: "read", ResourceData: nil, // nil map Environment: nil, // nil map } body, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBuffer(body)) ctx := context.WithValue(req.Context(), models.ContextKey("claims"), claims) req = req.WithContext(ctx) w := httptest.NewRecorder() // Execute - should not panic AuthorizeHandler(w, req) // The handler should complete without panic // Status code will depend on whether permission exists in DB } // Additional comprehensive test cases func TestAuthorizeHandler_EmptyUserID(t *testing.T) { claims := &models.Claims{ UserID: "user123", Username: "testuser", Role: "admin", } payload := models.AuthorizationContext{ UserID: "", Resource: "document", Action: "read", } body, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBuffer(body)) ctx := context.WithValue(req.Context(), models.ContextKey("claims"), claims) req = req.WithContext(ctx) w := httptest.NewRecorder() AuthorizeHandler(w, req) if w.Code != http.StatusBadRequest { t.Errorf("Expected status %d for empty UserID, got %d", http.StatusBadRequest, w.Code) } } func TestAuthorizeHandler_EmptyResource(t *testing.T) { claims := &models.Claims{ UserID: "user123", Username: "testuser", Role: "admin", } payload := models.AuthorizationContext{ UserID: "user123", Resource: "", Action: "read", } body, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBuffer(body)) ctx := context.WithValue(req.Context(), models.ContextKey("claims"), claims) req = req.WithContext(ctx) w := httptest.NewRecorder() AuthorizeHandler(w, req) if w.Code != http.StatusBadRequest { t.Errorf("Expected status %d for empty Resource, got %d", http.StatusBadRequest, w.Code) } } func TestAuthorizeHandler_EmptyAction(t *testing.T) { claims := &models.Claims{ UserID: "user123", Username: "testuser", Role: "admin", } payload := models.AuthorizationContext{ UserID: "user123", Resource: "document", Action: "", } body, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBuffer(body)) ctx := context.WithValue(req.Context(), models.ContextKey("claims"), claims) req = req.WithContext(ctx) w := httptest.NewRecorder() AuthorizeHandler(w, req) if w.Code != http.StatusBadRequest { t.Errorf("Expected status %d for empty Action, got %d", http.StatusBadRequest, w.Code) } } func TestAuthorizeHandler_InvalidClaimsType(t *testing.T) { req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBufferString(`{"userId":"user123","resource":"doc","action":"read"}`)) // Set claims as wrong type ctx := context.WithValue(req.Context(), models.ContextKey("claims"), "invalid_claims_type") req = req.WithContext(ctx) w := httptest.NewRecorder() AuthorizeHandler(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("Expected status %d for invalid claims type, got %d", http.StatusUnauthorized, w.Code) } } func TestAuthorizeHandler_MalformedJSON(t *testing.T) { claims := &models.Claims{ UserID: "user123", Username: "testuser", Role: "admin", } testCases := []struct { name string payload string }{ {"Incomplete JSON", `{"userId":"user123","resource":"doc"`}, {"Invalid quotes", `{userId:"user123"}`}, {"Trailing comma", `{"userId":"user123",}`}, {"Just whitespace", ` `}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBufferString(tc.payload)) ctx := context.WithValue(req.Context(), models.ContextKey("claims"), claims) req = req.WithContext(ctx) w := httptest.NewRecorder() AuthorizeHandler(w, req) if w.Code != http.StatusBadRequest { t.Errorf("Expected status %d for malformed JSON, got %d", http.StatusBadRequest, w.Code) } }) } } func TestAuthorizeHandler_SpecialCharactersInFields(t *testing.T) { t.Skip("Skipping - requires database mock setup") testCases := []struct { name string userID string resource string action string }{ {"Special chars in resource", "user123", "document/file-name_v1.2", "read"}, {"Unicode in resource", "user123", "文档", "read"}, {"Spaces in action", "user123", "document", "read write"}, {"Special chars in userID", "user-123_test", "document", "read"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { payload := models.AuthorizationContext{ UserID: tc.userID, Resource: tc.resource, Action: tc.action, } body, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBuffer(body)) // Update claims to match userID testClaims := &models.Claims{ UserID: tc.userID, Username: "testuser", Role: "admin", } ctx := context.WithValue(req.Context(), models.ContextKey("claims"), testClaims) req = req.WithContext(ctx) w := httptest.NewRecorder() AuthorizeHandler(w, req) // Should handle special characters without crashing if w.Code == 0 { t.Error("Handler did not set response status") } }) } }