# tests/unit/middleware/test_security_headers.py """Unit tests for SecurityHeadersMiddleware.""" import pytest from middleware.security_headers import SecurityHeadersMiddleware @pytest.mark.unit class TestSecurityHeadersMiddleware: """Tests for security headers on responses.""" def test_nosniff_header(self, client): """X-Content-Type-Options: nosniff is present.""" response = client.get("/health") assert response.headers.get("X-Content-Type-Options") == "nosniff" def test_frame_options_header(self, client): """X-Frame-Options: SAMEORIGIN is present.""" response = client.get("/health") assert response.headers.get("X-Frame-Options") == "SAMEORIGIN" def test_referrer_policy_header(self, client): """Referrer-Policy is set correctly.""" response = client.get("/health") assert ( response.headers.get("Referrer-Policy") == "strict-origin-when-cross-origin" ) def test_permissions_policy_header(self, client): """Permissions-Policy restricts camera/mic/geo.""" response = client.get("/health") assert ( response.headers.get("Permissions-Policy") == "camera=(), microphone=(), geolocation=()" ) def test_no_hsts_on_http(self, client): """HSTS header is NOT set for plain HTTP requests.""" response = client.get("/health") # TestClient uses http by default assert "Strict-Transport-Security" not in response.headers def test_headers_on_api_routes(self, client): """Security headers are present on API routes too.""" response = client.get("/api/v1/health") assert response.headers.get("X-Content-Type-Options") == "nosniff" assert response.headers.get("X-Frame-Options") == "SAMEORIGIN" def test_headers_on_404(self, client): """Security headers are present even on 404 responses.""" response = client.get("/nonexistent-path-that-returns-404") assert response.headers.get("X-Content-Type-Options") == "nosniff" assert response.headers.get("Referrer-Policy") == "strict-origin-when-cross-origin"