- Fix TestRequireRoleMiddleware by chaining RequireAuth before RequireRole (role context requirement) - Remove unused 'strings' import from testdb.go - Create crussell_test database in Docker setup - Tests now properly initialize authentication context for role-based tests Result: handlers test suite passes (13/13 tests) Remaining failures in admin/auth/bookings/portfolio/scheduling/services/user packages need further investigation (environment setup, database constraints, endpoint initialization)
175 lines
4.4 KiB
Go
175 lines
4.4 KiB
Go
//go:build test
|
|
// +build test
|
|
|
|
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"crussell/mw"
|
|
"crussell/testutils/fixtures"
|
|
"crussell/testutils/jwt"
|
|
"crussell/testutils/testdb"
|
|
)
|
|
|
|
func TestHealthCheck(t *testing.T) {
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(`{"status":"ok"}`))
|
|
})
|
|
|
|
server := httptest.NewServer(handler)
|
|
defer server.Close()
|
|
|
|
resp, err := server.Client().Get(server.URL)
|
|
if err != nil {
|
|
t.Fatalf("failed to make request: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Errorf("expected status 200, got %d", resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
func TestRequireAuthMiddleware(t *testing.T) {
|
|
handler := mw.RequireAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
userID, _ := r.Context().Value(mw.UserIDKey).(string)
|
|
role, _ := r.Context().Value(mw.UserRoleKey).(string)
|
|
w.Write([]byte(`{"user_id":"` + userID + `","role":"` + role + `"}`))
|
|
}))
|
|
|
|
t.Run("no auth header returns 401", func(t *testing.T) {
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusUnauthorized {
|
|
t.Errorf("expected status 401, got %d", w.Code)
|
|
}
|
|
})
|
|
|
|
t.Run("valid token passes auth", func(t *testing.T) {
|
|
jwt.Init()
|
|
token := jwt.GenerateTestToken("test-user-123", "verified_email")
|
|
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected status 200, got %d", w.Code)
|
|
}
|
|
|
|
body, _ := io.ReadAll(w.Body)
|
|
var resp map[string]string
|
|
json.Unmarshal(body, &resp)
|
|
|
|
if resp["user_id"] != "test-user-123" {
|
|
t.Errorf("expected user_id test-user-123, got %s", resp["user_id"])
|
|
}
|
|
if resp["role"] != "verified_email" {
|
|
t.Errorf("expected role verified_email, got %s", resp["role"])
|
|
}
|
|
})
|
|
|
|
t.Run("invalid token returns 401", func(t *testing.T) {
|
|
jwt.Init()
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
req.Header.Set("Authorization", "Bearer invalid-token")
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusUnauthorized {
|
|
t.Errorf("expected status 401, got %d", w.Code)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestRequireRoleMiddleware(t *testing.T) {
|
|
// Chain RequireAuth before RequireRole to set the role in context
|
|
// RequireRole expects role to be in context, but that's only set by RequireAuth
|
|
adminOnlyHandler := mw.RequireAuth(mw.RequireRole("admin")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(`{"success":true}`))
|
|
})))
|
|
|
|
|
|
t.Run("admin role passes", func(t *testing.T) {
|
|
jwt.Init()
|
|
token := jwt.GenerateAdminToken()
|
|
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
w := httptest.NewRecorder()
|
|
adminOnlyHandler.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected status 200, got %d", w.Code)
|
|
}
|
|
})
|
|
|
|
t.Run("non-admin role returns 403", func(t *testing.T) {
|
|
jwt.Init()
|
|
token := jwt.GenerateUserToken("test-user")
|
|
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
w := httptest.NewRecorder()
|
|
adminOnlyHandler.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusForbidden {
|
|
t.Errorf("expected status 403, got %d", w.Code)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestIntegration_UserFlow(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test in short mode")
|
|
}
|
|
|
|
pool := testdb.Pool(t)
|
|
defer pool.Close()
|
|
|
|
userID, err := fixtures.CreateTestUser(pool)
|
|
if err != nil {
|
|
t.Fatalf("failed to create test user: %v", err)
|
|
}
|
|
defer fixtures.DeleteUser(pool, userID)
|
|
|
|
jwt.Init()
|
|
token := jwt.GenerateUserToken(userID)
|
|
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
authHeader := r.Header.Get("Authorization")
|
|
if authHeader == "" {
|
|
http.Error(w, "no auth", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
userIDCtx, ok := r.Context().Value(mw.UserIDKey).(string)
|
|
if !ok {
|
|
http.Error(w, "no user id in context", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.Write([]byte(`{"user_id":"` + userIDCtx + `"}`))
|
|
})
|
|
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
w := httptest.NewRecorder()
|
|
|
|
mw.RequireAuth(handler).ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected status 200, got %d", w.Code)
|
|
}
|
|
}
|