1+ import pytest
2+ from unittest .mock import patch , AsyncMock
3+ from fastapi import FastAPI
4+ from fastapi .testclient import TestClient
5+ from starlette .responses import JSONResponse
6+ from app .middleware .auth_middleware import AuthMiddleware
7+ import firebase_admin .auth
8+
9+
10+ # To test: public paths, protected routes, missing token, invalid token, revoked token, expired token
11+ # also test routes w/ different permissions? (e.g. admin, user, etc.)
12+
13+ # Create a test FastAPI app and apply AuthMiddleware
14+ app = FastAPI ()
15+ app .add_middleware (AuthMiddleware , public_paths = ["/public" ])
16+
17+ @app .get ("/public" )
18+ async def public_route ():
19+ return {"message" : "This is a public route" }
20+
21+ @app .get ("/protected" )
22+ async def protected_route ():
23+ return {"message" : "This is a protected route" }
24+ #make one for admin, volunteer, participant
25+
26+ client = TestClient (app )
27+
28+ @pytest .fixture
29+ def mock_firebase (): #makes mock objects to get mock responses - get sample token
30+ """Fixture to mock Firebase authentication methods."""
31+ with patch ("firebase_admin.auth.verify_id_token" ) as mock_verify , \
32+ patch ("firebase_admin.auth.get_user" ) as mock_get_user :
33+
34+ mock_verify .return_value = {
35+ "uid" : "test_user" ,
36+ 37+ "name" : "Test User" ,
38+ "picture" : "https://example.com/picture.jpg" ,
39+ "claims" : {"role" : "admin" },
40+ }
41+
42+ mock_get_user .return_value = AsyncMock (email_verified = True )
43+
44+ yield mock_verify , mock_get_user
45+
46+
47+ def test_public_route ():
48+ """Public routes should be accessible without authentication."""
49+ response = client .get ("/public" )
50+ assert response .status_code == 200 #successful
51+ assert response .json () == {"message" : "This is a public route" }
52+
53+
54+ def test_protected_route_access_granted (mock_firebase ):
55+ #mock firebase specifies for admin rn
56+ """Authenticated users with a valid token should be granted access."""
57+ headers = {"Authorization" : "Bearer valid_token" }
58+ response = client .get ("/protected" , headers = headers )
59+
60+ assert response .status_code == 200
61+ assert response .json () == {"message" : "This is a protected route" }
62+ assert "X-Auth-User-ID" in response .headers
63+
64+
65+ def test_protected_route_missing_token ():
66+ """Requests without an authentication token should be denied."""
67+ response = client .get ("/protected" )
68+
69+ assert response .status_code == 401
70+ assert response .json ()["detail" ] == "Authentication required"
71+
72+
73+ def test_protected_route_invalid_token ():
74+ """Requests with an invalid authentication token should be denied."""
75+ with patch ("firebase_admin.auth.verify_id_token" , side_effect = Exception ("Invalid Token" )):
76+ headers = {"Authorization" : "Bearer invalid_token" }
77+ response = client .get ("/protected" , headers = headers )
78+
79+ assert response .status_code == 401
80+ assert "Authentication failed: Invalid Token" in response .json ()["detail" ]
81+
82+
83+ def test_protected_route_revoked_token ():
84+ """Requests with a revoked token should be denied."""
85+ with patch ("firebase_admin.auth.verify_id_token" , side_effect = firebase_admin .auth .RevokedIdTokenError (message = "Token revoked" )):
86+ headers = {"Authorization" : "Bearer revoked_token" }
87+ response = client .get ("/protected" , headers = headers )
88+ print (response .json ())
89+
90+ assert response .status_code == 401
91+ assert "Token has been revoked. Please reauthenticate." in response .json ()["detail" ]
92+
93+
94+ def test_protected_route_expired_token ():
95+ """Requests with an expired token should be denied."""
96+ with patch ("firebase_admin.auth.verify_id_token" , side_effect = firebase_admin .auth .ExpiredIdTokenError (message = "Token expired" , cause = "test" )):
97+ headers = {"Authorization" : "Bearer expired_token" }
98+ response = client .get ("/protected" , headers = headers )
99+ print (response .json ())
100+
101+ assert response .status_code == 401
102+ assert "Token has expired. Please reauthenticate." in response .json ()["detail" ]
0 commit comments