added unit testing
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"authorization/db"
|
||||
"authorization/models"
|
||||
"authorization/redisclient"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
)
|
||||
|
||||
func TestHealthHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
wantStatus int
|
||||
wantBodyStatus string
|
||||
}{
|
||||
{
|
||||
name: "returns 200 OK with ok status",
|
||||
wantStatus: http.StatusOK,
|
||||
wantBodyStatus: "ok",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/health", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
HealthHandler(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != tt.wantStatus {
|
||||
t.Errorf("status = %v, want %v", resp.StatusCode, tt.wantStatus)
|
||||
}
|
||||
|
||||
var healthResp models.HealthResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&healthResp); err != nil {
|
||||
t.Fatalf("failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if healthResp.Status != tt.wantBodyStatus {
|
||||
t.Errorf("status = %v, want %v", healthResp.Status, tt.wantBodyStatus)
|
||||
}
|
||||
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if contentType != "application/json" {
|
||||
t.Errorf("Content-Type = %v, want application/json", contentType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadyHandler_AllHealthy(t *testing.T) {
|
||||
// Setup mock DB
|
||||
mockDB, mock, err := sqlmock.New(sqlmock.MonitorPingsOption(true))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create mock db: %v", err)
|
||||
}
|
||||
defer mockDB.Close()
|
||||
|
||||
// Expect successful ping
|
||||
mock.ExpectPing()
|
||||
|
||||
// Save original and set mock
|
||||
originalDB := db.DB
|
||||
db.DB = mockDB
|
||||
defer func() { db.DB = originalDB }()
|
||||
|
||||
// Save original Redis and set to nil (not checking Redis in this test)
|
||||
originalRedis := redisclient.RDB
|
||||
redisclient.RDB = nil
|
||||
defer func() { redisclient.RDB = originalRedis }()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/ready", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
ReadyHandler(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
defer resp.Body.Close()
|
||||
|
||||
var healthResp models.HealthResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&healthResp); err != nil {
|
||||
t.Fatalf("failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if healthResp.Services["database"] != "healthy" {
|
||||
t.Errorf("database status = %v, want healthy", healthResp.Services["database"])
|
||||
}
|
||||
|
||||
// Verify mock expectations
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadyHandler_DBUnhealthy(t *testing.T) {
|
||||
// Setup mock DB that fails ping
|
||||
mockDB, mock, err := sqlmock.New(sqlmock.MonitorPingsOption(true))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create mock db: %v", err)
|
||||
}
|
||||
defer mockDB.Close()
|
||||
|
||||
// Expect ping to fail
|
||||
mock.ExpectPing().WillReturnError(sql.ErrConnDone)
|
||||
|
||||
// Save original and set mock
|
||||
originalDB := db.DB
|
||||
db.DB = mockDB
|
||||
defer func() { db.DB = originalDB }()
|
||||
|
||||
// Save original Redis and set to nil
|
||||
originalRedis := redisclient.RDB
|
||||
redisclient.RDB = nil
|
||||
defer func() { redisclient.RDB = originalRedis }()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/ready", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
ReadyHandler(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusServiceUnavailable {
|
||||
t.Errorf("status = %v, want %v", resp.StatusCode, http.StatusServiceUnavailable)
|
||||
}
|
||||
|
||||
var healthResp models.HealthResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&healthResp); err != nil {
|
||||
t.Fatalf("failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if healthResp.Status != "not_ready" {
|
||||
t.Errorf("status = %v, want not_ready", healthResp.Status)
|
||||
}
|
||||
|
||||
if healthResp.Services["database"] != "unhealthy" {
|
||||
t.Errorf("database status = %v, want unhealthy", healthResp.Services["database"])
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadyHandler_DBNotInitialized(t *testing.T) {
|
||||
// Save original and set to nil
|
||||
originalDB := db.DB
|
||||
db.DB = nil
|
||||
defer func() { db.DB = originalDB }()
|
||||
|
||||
originalRedis := redisclient.RDB
|
||||
redisclient.RDB = nil
|
||||
defer func() { redisclient.RDB = originalRedis }()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/ready", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
ReadyHandler(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusServiceUnavailable {
|
||||
t.Errorf("status = %v, want %v", resp.StatusCode, http.StatusServiceUnavailable)
|
||||
}
|
||||
|
||||
var healthResp models.HealthResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&healthResp); err != nil {
|
||||
t.Fatalf("failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if healthResp.Status != "not_ready" {
|
||||
t.Errorf("status = %v, want not_ready", healthResp.Status)
|
||||
}
|
||||
|
||||
if healthResp.Services["database"] != "not_initialized" {
|
||||
t.Errorf("database status = %v, want not_initialized", healthResp.Services["database"])
|
||||
}
|
||||
|
||||
if healthResp.Services["redis"] != "not_initialized" {
|
||||
t.Errorf("redis status = %v, want not_initialized", healthResp.Services["redis"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadyHandler_ContentType(t *testing.T) {
|
||||
originalDB := db.DB
|
||||
db.DB = nil
|
||||
defer func() { db.DB = originalDB }()
|
||||
|
||||
originalRedis := redisclient.RDB
|
||||
redisclient.RDB = nil
|
||||
defer func() { redisclient.RDB = originalRedis }()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/ready", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
ReadyHandler(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
defer resp.Body.Close()
|
||||
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if contentType != "application/json" {
|
||||
t.Errorf("Content-Type = %v, want application/json", contentType)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user