|
|
"""Unit tests for API dependencies.""" |
|
|
import pytest |
|
|
import base64 |
|
|
import json |
|
|
from unittest.mock import patch |
|
|
from fastapi import HTTPException |
|
|
|
|
|
|
|
|
class TestValidateBearerToken: |
|
|
"""Test cases for bearer token validation dependency.""" |
|
|
|
|
|
def setup_method(self): |
|
|
"""Set up test fixtures.""" |
|
|
|
|
|
header = {"alg": "HS256", "typ": "JWT"} |
|
|
payload = {"sub": "1234567890", "name": "John Doe", "iat": 1516239022} |
|
|
|
|
|
header_b64 = base64.urlsafe_b64encode( |
|
|
json.dumps(header).encode() |
|
|
).decode().rstrip('=') |
|
|
payload_b64 = base64.urlsafe_b64encode( |
|
|
json.dumps(payload).encode() |
|
|
).decode().rstrip('=') |
|
|
|
|
|
self.valid_token = f"{header_b64}.{payload_b64}.test_signature" |
|
|
self.valid_auth_header = f"Bearer {self.valid_token}" |
|
|
|
|
|
@pytest.mark.asyncio |
|
|
async def test_validate_bearer_token_success(self): |
|
|
"""Test successful bearer token validation.""" |
|
|
|
|
|
from interfaces.api.dependencies import validate_bearer_token |
|
|
|
|
|
result = await validate_bearer_token(self.valid_auth_header) |
|
|
assert result == self.valid_token |
|
|
|
|
|
@pytest.mark.asyncio |
|
|
async def test_validate_bearer_token_missing_header(self): |
|
|
"""Test validation fails when Authorization header is missing.""" |
|
|
from interfaces.api.dependencies import validate_bearer_token |
|
|
|
|
|
with pytest.raises(HTTPException) as exc_info: |
|
|
await validate_bearer_token(None) |
|
|
|
|
|
assert exc_info.value.status_code == 401 |
|
|
assert "Missing Authorization header" in exc_info.value.detail |
|
|
assert exc_info.value.headers["WWW-Authenticate"] == "Bearer" |
|
|
|
|
|
@pytest.mark.asyncio |
|
|
async def test_validate_bearer_token_invalid_format(self): |
|
|
"""Test validation fails for invalid Authorization header format.""" |
|
|
from interfaces.api.dependencies import validate_bearer_token |
|
|
|
|
|
with pytest.raises(HTTPException) as exc_info: |
|
|
await validate_bearer_token("Basic dXNlcjpwYXNz") |
|
|
|
|
|
assert exc_info.value.status_code == 401 |
|
|
assert "Invalid Authorization header format" in exc_info.value.detail |
|
|
assert exc_info.value.headers["WWW-Authenticate"] == "Bearer" |
|
|
|
|
|
@pytest.mark.asyncio |
|
|
async def test_validate_bearer_token_missing_bearer_prefix(self): |
|
|
"""Test validation fails when Bearer prefix is missing.""" |
|
|
from interfaces.api.dependencies import validate_bearer_token |
|
|
|
|
|
with pytest.raises(HTTPException) as exc_info: |
|
|
await validate_bearer_token(self.valid_token) |
|
|
|
|
|
assert exc_info.value.status_code == 401 |
|
|
assert "Invalid Authorization header format" in exc_info.value.detail |
|
|
|
|
|
@pytest.mark.asyncio |
|
|
async def test_validate_bearer_token_empty_token(self): |
|
|
"""Test validation fails for empty token after Bearer prefix.""" |
|
|
from interfaces.api.dependencies import validate_bearer_token |
|
|
|
|
|
with pytest.raises(HTTPException) as exc_info: |
|
|
await validate_bearer_token("Bearer ") |
|
|
|
|
|
assert exc_info.value.status_code == 401 |
|
|
assert "Empty bearer token" in exc_info.value.detail |
|
|
|
|
|
@pytest.mark.asyncio |
|
|
async def test_validate_bearer_token_invalid_jwt_structure(self): |
|
|
"""Test validation fails for invalid JWT token structure.""" |
|
|
from interfaces.api.dependencies import validate_bearer_token |
|
|
|
|
|
with pytest.raises(HTTPException) as exc_info: |
|
|
await validate_bearer_token("Bearer invalid.token") |
|
|
|
|
|
assert exc_info.value.status_code == 401 |
|
|
assert "Invalid JWT token structure" in exc_info.value.detail |
|
|
|
|
|
@pytest.mark.asyncio |
|
|
async def test_validate_bearer_token_malformed_jwt(self): |
|
|
"""Test validation fails for malformed JWT.""" |
|
|
from interfaces.api.dependencies import validate_bearer_token |
|
|
|
|
|
with pytest.raises(HTTPException) as exc_info: |
|
|
await validate_bearer_token("Bearer not_a_jwt_at_all") |
|
|
|
|
|
assert exc_info.value.status_code == 401 |
|
|
assert "Invalid JWT token structure" in exc_info.value.detail |
|
|
|
|
|
@pytest.mark.asyncio |
|
|
async def test_validate_bearer_token_case_sensitive(self): |
|
|
"""Test that Bearer prefix is case sensitive.""" |
|
|
with pytest.raises(HTTPException) as exc_info: |
|
|
await validate_bearer_token(f"bearer {self.valid_token}") |
|
|
|
|
|
assert exc_info.value.status_code == 401 |
|
|
assert "Invalid Authorization header format" in exc_info.value.detail |
|
|
|
|
|
@pytest.mark.asyncio |
|
|
async def test_validate_bearer_token_extra_spaces(self): |
|
|
"""Test handling of extra spaces in Authorization header.""" |
|
|
|
|
|
result = await validate_bearer_token(f"Bearer {self.valid_token}") |
|
|
assert result == self.valid_token |
|
|
|
|
|
|
|
|
with pytest.raises(HTTPException) as exc_info: |
|
|
await validate_bearer_token(f"Bearer {self.valid_token}") |
|
|
|
|
|
assert exc_info.value.status_code == 401 |