Files
2026-01-27 10:45:15 +08:00

303 lines
7.3 KiB
Go

package routes
import (
"authorization/db"
"bytes"
"database/sql"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/gorilla/mux"
)
func TestMain(m *testing.M) {
os.Setenv("GO_ENV", "development")
code := m.Run()
os.Unsetenv("GO_ENV")
os.Exit(code)
}
func setupMockDB(t *testing.T) (*sql.DB, sqlmock.Sqlmock, func()) {
mockDB, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("Failed to create mock database: %v", err)
}
originalDB := db.DB
db.DB = mockDB
cleanup := func() {
db.DB = originalDB
mockDB.Close()
}
return mockDB, mock, cleanup
}
func TestSetupRoutes_HealthEndpoint(t *testing.T) {
mockDB, _, cleanup := setupMockDB(t)
defer cleanup()
router := mux.NewRouter()
SetupRoutes(router, mockDB)
req := httptest.NewRequest("GET", "/health", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("Expected status %d for /health, got %d", http.StatusOK, w.Code)
}
}
func TestSetupRoutes_ReadyEndpoint(t *testing.T) {
mockDB, _, cleanup := setupMockDB(t)
defer cleanup()
router := mux.NewRouter()
SetupRoutes(router, mockDB)
req := httptest.NewRequest("GET", "/ready", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// The ready endpoint returns 503 when db.DB global is not initialized
// This is expected behavior in unit tests where we don't initialize global state
if w.Code != http.StatusServiceUnavailable && w.Code != http.StatusOK {
t.Errorf("Expected status 503 or 200 for /ready, got %d", w.Code)
}
}
func TestSetupRoutes_SwaggerEndpoint(t *testing.T) {
mockDB, _, cleanup := setupMockDB(t)
defer cleanup()
router := mux.NewRouter()
SetupRoutes(router, mockDB)
req := httptest.NewRequest("GET", "/swagger/", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Swagger endpoint should be registered (might return various status codes depending on setup)
// We just check that it doesn't return 404
if w.Code == http.StatusNotFound {
t.Error("Swagger endpoint should be registered")
}
}
func TestSetupRoutes_AuthCheckEndpoint(t *testing.T) {
// Test that the auth check endpoint is properly registered
// We can test the routing without full DB initialization
router := mux.NewRouter()
SetupRoutes(router, nil) // Pass nil DB, handlers should handle it gracefully
req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBufferString(`{"userId":"user123","resource":"doc","action":"read"}`))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Endpoint is registered (status won't be 404)
if w.Code == http.StatusNotFound {
t.Error("Auth check endpoint should be registered")
}
// Will likely return 401 (no JWT) or 500 (no DB) but that's OK - route exists
if w.Code != http.StatusUnauthorized && w.Code != http.StatusInternalServerError && w.Code != http.StatusForbidden {
t.Logf("Auth check returned status %d (expected 401, 403, or 500 without proper setup)", w.Code)
}
}
func TestSetupRoutes_MethodRestrictions(t *testing.T) {
mockDB, _, cleanup := setupMockDB(t)
defer cleanup()
router := mux.NewRouter()
SetupRoutes(router, mockDB)
tests := []struct {
name string
method string
path string
expectNotFound bool
}{
{
name: "GET on health is allowed",
method: "GET",
path: "/health",
expectNotFound: false,
},
{
name: "POST on health is not allowed",
method: "POST",
path: "/health",
expectNotFound: true,
},
{
name: "GET on ready is allowed",
method: "GET",
path: "/ready",
expectNotFound: false,
},
{
name: "POST on auth/check is allowed",
method: "POST",
path: "/v1/auth/check",
expectNotFound: false,
},
{
name: "GET on auth/check is not allowed",
method: "GET",
path: "/v1/auth/check",
expectNotFound: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(tt.method, tt.path, nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if tt.expectNotFound && w.Code != http.StatusNotFound && w.Code != http.StatusMethodNotAllowed {
t.Errorf("Expected 404 or 405, got %d", w.Code)
}
if !tt.expectNotFound && (w.Code == http.StatusNotFound || w.Code == http.StatusMethodNotAllowed) {
t.Errorf("Expected route to exist, got %d", w.Code)
}
})
}
}
func TestSetupRoutes_RouterConfiguration(t *testing.T) {
mockDB, _, cleanup := setupMockDB(t)
defer cleanup()
router := mux.NewRouter()
// Before setup, routes should not exist
req := httptest.NewRequest("GET", "/health", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if w.Code != http.StatusNotFound {
t.Log("Route might already exist before setup (not necessarily an error)")
}
// After setup, health route should exist
SetupRoutes(router, mockDB)
req = httptest.NewRequest("GET", "/health", nil)
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
if w.Code == http.StatusNotFound {
t.Error("Health route should exist after SetupRoutes")
}
}
func TestSetupRoutes_WithNilDB(t *testing.T) {
router := mux.NewRouter()
// Should not panic with nil DB
defer func() {
if r := recover(); r != nil {
t.Errorf("SetupRoutes should not panic with nil DB: %v", r)
}
}()
SetupRoutes(router, nil)
}
func TestSetupRoutes_PathPrefix(t *testing.T) {
mockDB, _, cleanup := setupMockDB(t)
defer cleanup()
router := mux.NewRouter()
SetupRoutes(router, mockDB)
// Test that auth routes use /v1/auth prefix
req := httptest.NewRequest("POST", "/v1/auth/check", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Should not be 404 (route exists, even if auth fails)
if w.Code == http.StatusNotFound {
t.Error("Auth route with /v1/auth prefix should exist")
}
}
func TestSetupRoutes_MultipleInitializations(t *testing.T) {
mockDB, _, cleanup := setupMockDB(t)
defer cleanup()
router := mux.NewRouter()
// Setup routes twice
SetupRoutes(router, mockDB)
SetupRoutes(router, mockDB)
// Should still work
req := httptest.NewRequest("GET", "/health", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("Expected status %d after multiple setups, got %d", http.StatusOK, w.Code)
}
}
func TestSetupRoutes_AllEndpoints(t *testing.T) {
// Test that all endpoints are properly registered
router := mux.NewRouter()
SetupRoutes(router, nil)
endpoints := []struct {
method string
path string
}{
{"GET", "/health"},
{"GET", "/ready"},
{"POST", "/v1/auth/check"},
{"GET", "/swagger/"},
}
for _, ep := range endpoints {
t.Run(ep.method+" "+ep.path, func(t *testing.T) {
req := httptest.NewRequest(ep.method, ep.path, nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Endpoint should be registered (not 404 or 405)
if w.Code == http.StatusNotFound {
t.Errorf("Endpoint %s %s should be registered", ep.method, ep.path)
}
})
}
}
func TestSetupRoutes_DBParameter(t *testing.T) {
// Test that SetupRoutes accepts *sql.DB parameter
router := mux.NewRouter()
var mockDB *sql.DB = nil
// Should compile and not panic
defer func() {
if r := recover(); r != nil {
t.Errorf("SetupRoutes should accept *sql.DB parameter: %v", r)
}
}()
SetupRoutes(router, mockDB)
}