//go:build test package mw import ( "context" "net/http" "net/http/httptest" "testing" "crussell/testutils/jwt" ) func testHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userID, _ := GetUserID(r.Context()) role, _ := GetUserRole(r.Context()) jti, _ := GetJTI(r.Context()) w.WriteHeader(http.StatusOK) w.Write([]byte(userID + "|" + role + "|" + jti)) }) } // ============================================================================= // RequireAuth // ============================================================================= func TestRequireAuth_NoHeader(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) w := httptest.NewRecorder() RequireAuth(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401, got %d", w.Code) } } func TestRequireAuth_MalformedHeader(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) req.Header.Set("Authorization", "NotBearer token123") w := httptest.NewRecorder() RequireAuth(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401, got %d", w.Code) } } func TestRequireAuth_EmptyToken(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) req.Header.Set("Authorization", "Bearer ") w := httptest.NewRecorder() RequireAuth(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401, got %d", w.Code) } } func TestRequireAuth_InvalidToken(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) req.Header.Set("Authorization", "Bearer this.is.not.a.valid.jwt") w := httptest.NewRecorder() RequireAuth(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401, got %d", w.Code) } } func TestRequireAuth_ValidToken(t *testing.T) { token := jwt.GenerateTestToken("testuser123", "verified_email") req := httptest.NewRequest("GET", "/", nil) req.Header.Set("Authorization", "Bearer "+token) w := httptest.NewRecorder() RequireAuth(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200, got %d: %s", w.Code, w.Body.String()) } body := w.Body.String() if body == "" || body == "|" { t.Errorf("expected user info in response, got %q", body) } } func TestRequireAuth_AdminToken(t *testing.T) { token := jwt.GenerateAdminToken() req := httptest.NewRequest("GET", "/", nil) req.Header.Set("Authorization", "Bearer "+token) w := httptest.NewRecorder() RequireAuth(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200, got %d: %s", w.Code, w.Body.String()) } } func TestRequireAuth_SetsAllContextFields(t *testing.T) { token := jwt.GenerateTestToken("testuser123", "admin") var capturedID, capturedRole, capturedJTI string collector := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { capturedID, _ = GetUserID(r.Context()) capturedRole, _ = GetUserRole(r.Context()) capturedJTI, _ = GetJTI(r.Context()) w.WriteHeader(http.StatusOK) }) req := httptest.NewRequest("GET", "/", nil) req.Header.Set("Authorization", "Bearer "+token) w := httptest.NewRecorder() RequireAuth(collector).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d", w.Code) } if capturedID != "testuser123" { t.Errorf("expected userID 'testuser123', got %q", capturedID) } if capturedRole != "admin" { t.Errorf("expected role 'admin', got %q", capturedRole) } if capturedJTI == "" { t.Error("expected non-empty JTI") } } // ============================================================================= // OptionalAuth // ============================================================================= func TestOptionalAuth_NoHeader(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) w := httptest.NewRecorder() OptionalAuth(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 (pass-through), got %d", w.Code) } body := w.Body.String() if body != "||" { t.Errorf("expected empty context (||), got %q", body) } } func TestOptionalAuth_ValidToken(t *testing.T) { token := jwt.GenerateTestToken("testuser123", "verified_email") req := httptest.NewRequest("GET", "/", nil) req.Header.Set("Authorization", "Bearer "+token) w := httptest.NewRecorder() OptionalAuth(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200, got %d", w.Code) } body := w.Body.String() if body == "||" { t.Error("expected context to be set with valid token") } } func TestOptionalAuth_InvalidToken(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) req.Header.Set("Authorization", "Bearer obviously.invalid.token") w := httptest.NewRecorder() OptionalAuth(testHandler()).ServeHTTP(w, req) // Should pass through without setting context if w.Code != http.StatusOK { t.Errorf("expected 200 (pass-through on invalid token), got %d", w.Code) } body := w.Body.String() if body != "||" { t.Errorf("expected empty context on invalid token, got %q", body) } } func TestOptionalAuth_NoBearer(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) req.Header.Set("Authorization", "Basic somecreds") w := httptest.NewRecorder() OptionalAuth(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 (pass-through without Bearer prefix), got %d", w.Code) } } // ============================================================================= // RequireRole // ============================================================================= func TestRequireRole_Allowed(t *testing.T) { handler := RequireRole("admin")(testHandler()) req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "admin") req = req.WithContext(ctx) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 for admin role, got %d", w.Code) } } func TestRequireRole_MultipleRoles(t *testing.T) { handler := RequireRole("admin", "verified_email")(testHandler()) req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "verified_email") req = req.WithContext(ctx) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 for verified_email in allowed list, got %d", w.Code) } } func TestRequireRole_Forbidden(t *testing.T) { handler := RequireRole("admin")(testHandler()) req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "verified_email") req = req.WithContext(ctx) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != http.StatusForbidden { t.Errorf("expected 403 for non-admin role, got %d", w.Code) } } func TestRequireRole_NoRoleInContext(t *testing.T) { handler := RequireRole("admin")(testHandler()) req := httptest.NewRequest("GET", "/", nil) // No UserRoleKey in context w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401 when no role in context, got %d", w.Code) } } func TestRequireRole_EmptyAllowedList(t *testing.T) { handler := RequireRole()(testHandler()) req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "admin") req = req.WithContext(ctx) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != http.StatusForbidden { t.Errorf("expected 403 with empty allowed list, got %d", w.Code) } } // ============================================================================= // RequireVerified (wraps RequireRole("verified_email", "admin")) // ============================================================================= func TestRequireVerified_VerifiedEmail(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "verified_email") req = req.WithContext(ctx) w := httptest.NewRecorder() RequireVerified(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 for verified_email, got %d", w.Code) } } func TestRequireVerified_Admin(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "admin") req = req.WithContext(ctx) w := httptest.NewRecorder() RequireVerified(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 for admin, got %d", w.Code) } } func TestRequireVerified_Unverified(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "unverified_email") req = req.WithContext(ctx) w := httptest.NewRecorder() RequireVerified(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusForbidden { t.Errorf("expected 403 for unverified, got %d", w.Code) } } // ============================================================================= // RequireAdmin (RequireRole("admin") + UserIDKey guard) // ============================================================================= func TestRequireAdmin_AdminRole(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "admin") ctx = context.WithValue(ctx, UserIDKey, "adminuser001") req = req.WithContext(ctx) w := httptest.NewRecorder() RequireAdmin(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 for admin, got %d", w.Code) } } func TestRequireAdmin_NonAdmin(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "verified_email") ctx = context.WithValue(ctx, UserIDKey, "someuser001") req = req.WithContext(ctx) w := httptest.NewRecorder() RequireAdmin(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusForbidden { t.Errorf("expected 403 for non-admin, got %d", w.Code) } } func TestRequireAdmin_NoUserIDInContext(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "admin") // No UserIDKey set req = req.WithContext(ctx) w := httptest.NewRecorder() RequireAdmin(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401 when UserID missing from context, got %d", w.Code) } } func TestRequireAdmin_EmptyUserID(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) ctx := context.WithValue(req.Context(), UserRoleKey, "admin") ctx = context.WithValue(ctx, UserIDKey, "") req = req.WithContext(ctx) w := httptest.NewRecorder() RequireAdmin(testHandler()).ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401 for empty UserID, got %d", w.Code) } } // ============================================================================= // Context helpers — GetUserID, GetUserRole, GetJTI // ============================================================================= func TestGetUserID_Present(t *testing.T) { ctx := context.WithValue(context.Background(), UserIDKey, "abc123") id, ok := GetUserID(ctx) if !ok { t.Error("expected ok=true") } if id != "abc123" { t.Errorf("expected 'abc123', got %q", id) } } func TestGetUserID_Missing(t *testing.T) { _, ok := GetUserID(context.Background()) if ok { t.Error("expected ok=false") } } func TestGetUserRole_Present(t *testing.T) { ctx := context.WithValue(context.Background(), UserRoleKey, "admin") role, ok := GetUserRole(ctx) if !ok { t.Error("expected ok=true") } if role != "admin" { t.Errorf("expected 'admin', got %q", role) } } func TestGetUserRole_Missing(t *testing.T) { _, ok := GetUserRole(context.Background()) if ok { t.Error("expected ok=false") } } func TestGetJTI_Present(t *testing.T) { ctx := context.WithValue(context.Background(), JTIKey, "jti_test_123") jti, ok := GetJTI(ctx) if !ok { t.Error("expected ok=true") } if jti != "jti_test_123" { t.Errorf("expected 'jti_test_123', got %q", jti) } } func TestGetJTI_Missing(t *testing.T) { _, ok := GetJTI(context.Background()) if ok { t.Error("expected ok=false") } } // ============================================================================= // Full middleware chain — RequireAuth → RequireAdmin // ============================================================================= func TestAuthChain_AdminTokenFullFlow(t *testing.T) { token := jwt.GenerateAdminToken() handler := RequireAuth(RequireAdmin(testHandler())) req := httptest.NewRequest("GET", "/admin/dashboard", nil) req.Header.Set("Authorization", "Bearer "+token) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 for admin token through full chain, got %d: %s", w.Code, w.Body.String()) } } func TestAuthChain_VerifiedTokenBlockedByRequireAdmin(t *testing.T) { token := jwt.GenerateTestToken("user123abc", "verified_email") handler := RequireAuth(RequireAdmin(testHandler())) req := httptest.NewRequest("GET", "/admin/dashboard", nil) req.Header.Set("Authorization", "Bearer "+token) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != http.StatusForbidden { t.Errorf("expected 403 for verified_email through RequireAdmin, got %d", w.Code) } } func TestAuthChain_NoTokenBlockedByRequireAuth(t *testing.T) { handler := RequireAuth(RequireAdmin(testHandler())) req := httptest.NewRequest("GET", "/admin/dashboard", nil) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401 with no token, got %d", w.Code) } }