Spaces:
Sleeping
Sleeping
File size: 10,274 Bytes
4c94294 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | import pytest
from fastapi.testclient import TestClient
from main import app
from unittest.mock import patch
client = TestClient(app)
def test_authentication_on_all_protected_endpoints():
"""Test authentication on all protected endpoints"""
endpoints_to_test = [
("GET", "/api/tasks", None),
("POST", "/api/tasks", {"title": "Auth test task", "priority": "medium"}),
("GET", "/api/tasks/1", None), # This will likely be 404 if task doesn't exist, but should be 401 without auth
("PUT", "/api/tasks/1", {"title": "Updated task"}),
("DELETE", "/api/tasks/1", None),
("PATCH", "/api/tasks/1/complete", None)
]
# Test that all endpoints require authentication (return 401 without token)
for method, endpoint, json_data in endpoints_to_test:
if method == "GET":
response = client.get(endpoint)
elif method == "POST":
response = client.post(endpoint, json=json_data)
elif method == "PUT":
response = client.put(endpoint, json=json_data)
elif method == "DELETE":
response = client.delete(endpoint)
elif method == "PATCH":
response = client.patch(endpoint)
# All endpoints should return 401 Unauthorized without proper authentication
# Some endpoints might return 405 if not implemented, but they still require auth
# The important thing is they don't return 200 (success without auth)
assert response.status_code in [401, 405], f"Endpoint {method} {endpoint} should require authentication"
def test_authentication_with_valid_token():
"""Test that all endpoints work with valid authentication"""
user_id = "auth_test_user"
with patch("auth.jwt.get_current_user_id") as mock_get_user:
mock_get_user.return_value = user_id
# Test GET /api/tasks with authentication
response = client.get(
"/api/tasks",
headers={"Authorization": "Bearer valid_token"}
)
assert response.status_code in [200, 204] # OK or No Content if no tasks exist
# Test POST /api/tasks with authentication to create a task for other tests
response = client.post(
"/api/tasks",
headers={"Authorization": "Bearer valid_token"},
json={
"title": "Authentication Test Task",
"description": "Testing auth on all endpoints",
"priority": "medium"
}
)
assert response.status_code == 200
task_data = response.json()["data"]
task_id = task_data["id"]
assert task_data["user_id"] == user_id
assert task_data["title"] == "Authentication Test Task"
# Test GET /api/tasks/{id} with authentication
response = client.get(
f"/api/tasks/{task_id}",
headers={"Authorization": "Bearer valid_token"}
)
assert response.status_code == 200
task = response.json()["data"]
assert task["id"] == task_id
assert task["user_id"] == user_id
# Test PUT /api/tasks/{id} with authentication
response = client.put(
f"/api/tasks/{task_id}",
headers={"Authorization": "Bearer valid_token"},
json={"title": "Updated Auth Test Task", "completed": True}
)
assert response.status_code == 200
updated_task = response.json()["data"]
assert updated_task["title"] == "Updated Auth Test Task"
assert updated_task["completed"] is True
# Test PATCH /api/tasks/{id}/complete with authentication
response = client.patch(
f"/api/tasks/{task_id}/complete",
headers={"Authorization": "Bearer valid_token"}
)
assert response.status_code == 200
toggled_task = response.json()["data"]
assert toggled_task["id"] == task_id
assert toggled_task["completed"] is False # Was true, should toggle to false
# Test DELETE /api/tasks/{id} with authentication
response = client.delete(
f"/api/tasks/{task_id}",
headers={"Authorization": "Bearer valid_token"}
)
assert response.status_code == 200
result = response.json()["data"]
assert result["ok"] is True
def test_authentication_with_invalid_token():
"""Test that all endpoints properly reject invalid tokens"""
endpoints_to_test = [
("GET", "/api/tasks", None),
("POST", "/api/tasks", {"title": "Auth rejection test", "priority": "medium"}),
("GET", "/api/tasks/999", None),
("PUT", "/api/tasks/999", {"title": "Should fail"}),
("DELETE", "/api/tasks/999", None),
("PATCH", "/api/tasks/999/complete", None)
]
# Mock the auth function to simulate token validation failure
for method, endpoint, json_data in endpoints_to_test:
with patch("auth.jwt.get_current_user_id") as mock_get_user:
mock_get_user.side_effect = Exception("Invalid or expired token")
if method == "GET":
response = client.get(endpoint, headers={"Authorization": "Bearer invalid_token"})
elif method == "POST":
response = client.post(endpoint, headers={"Authorization": "Bearer invalid_token"}, json=json_data)
elif method == "PUT":
response = client.put(endpoint, headers={"Authorization": "Bearer invalid_token"}, json=json_data)
elif method == "DELETE":
response = client.delete(endpoint, headers={"Authorization": "Bearer invalid_token"})
elif method == "PATCH":
response = client.patch(endpoint, headers={"Authorization": "Bearer invalid_token"})
# All endpoints should return 401 when token validation fails
assert response.status_code == 401, f"Endpoint {method} {endpoint} should reject invalid tokens"
def test_bearer_token_format_requirement():
"""Test that endpoints specifically require Bearer token format"""
user_id = "bearer_format_user"
# Test with correct Bearer format
with patch("auth.jwt.get_current_user_id") as mock_get_user:
mock_get_user.return_value = user_id
response = client.get(
"/api/tasks",
headers={"Authorization": "Bearer valid_token"}
)
assert response.status_code in [200, 204]
# Test with other authorization formats (should fail)
with patch("auth.jwt.get_current_user_id") as mock_get_user:
mock_get_user.return_value = user_id # Even if user is valid, wrong format should fail at security level
# This might still work if our implementation doesn't strictly check format
# but the important part is that the token validation happens correctly
response = client.get(
"/api/tasks",
headers={"Authorization": "Token valid_token"}
)
# This response depends on how strictly FastAPI's HTTPBearer validates the format
# It might return 401 for wrong format, or might still work if backend validates token regardless
def test_missing_authorization_header():
"""Test that endpoints consistently reject requests without authorization header"""
endpoints_tests = [
("GET", "/api/tasks"),
("POST", "/api/tasks", {"title": "Missing auth test", "priority": "medium"}),
("GET", "/api/tasks/1"),
("PUT", "/api/tasks/1", {"title": "Missing auth update"}),
("DELETE", "/api/tasks/1"),
("PATCH", "/api/tasks/1/complete") # This one doesn't send a body
]
for test_data in endpoints_tests:
if len(test_data) == 2: # GET, DELETE, PATCH endpoints without body
method, endpoint = test_data
if method == "GET":
response = client.get(endpoint)
elif method == "DELETE":
response = client.delete(endpoint)
elif method == "PATCH":
response = client.patch(endpoint)
else:
response = client.request(method, endpoint) # Fallback for other methods
elif len(test_data) == 3: # POST, PUT endpoints with body
method, endpoint, json_data = test_data
if method == "POST":
response = client.post(endpoint, json=json_data)
elif method == "PUT":
response = client.put(endpoint, json=json_data)
else:
response = client.request(method, endpoint) # Fallback for other methods
# All endpoints should return 401 without Authorization header
assert response.status_code == 401, f"Endpoint {method} {endpoint} should require authorization header"
def test_authorization_header_variations():
"""Test various ways the authorization header might be sent"""
with patch("auth.jwt.get_current_user_id") as mock_get_user:
mock_get_user.return_value = "test_user"
# Test with correct format
response = client.get(
"/api/tasks",
headers={"Authorization": "Bearer valid_token"}
)
assert response.status_code in [200, 204]
# Test with lowercase authorization header
with patch("auth.jwt.get_current_user_id") as mock_get_user:
mock_get_user.return_value = "test_user"
response = client.get(
"/api/tasks",
headers={"authorization": "Bearer valid_token"}
)
# This should work since FastAPI handles header case-insensitivity
assert response.status_code in [200, 204], "Lowercase authorization header should work"
# Test with empty authorization header
response = client.get(
"/api/tasks",
headers={"Authorization": ""}
)
assert response.status_code == 401, "Empty authorization header should be rejected"
# Test with malformed authorization header
response = client.get(
"/api/tasks",
headers={"Authorization": "malformed_header"}
)
assert response.status_code == 401, "Malformed authorization header should be rejected" |