import sys import os import pytest from fastapi.testclient import TestClient sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) from app.main import app client = TestClient(app) def test_cors_allowed_origin_preflight(): """Test browser OPTIONS preflight for allowed origin (http://localhost:3000).""" response = client.options( "/v1/onboarding/start", headers={ "Origin": "http://localhost:3000", "Access-Control-Request-Method": "POST", "Access-Control-Request-Headers": "authorization, content-type, x-api-key", }, ) assert response.status_code == 200 assert response.headers.get("access-control-allow-origin") == "http://localhost:3000" assert response.headers.get("access-control-allow-credentials") == "true" assert "POST" in response.headers.get("access-control-allow-methods", "").upper() def test_cors_allowed_origin_actual_request(): """Test actual POST request from allowed origin (http://localhost:5173).""" response = client.post( "/v1/onboarding/start", json={ "patient": {"id": "PAT-CORS-1", "name": "CORS Test", "age": 30, "gender": "female"}, "category": {"type": "general", "code": "general_medicine", "name": "General Medicine"}, "language": "en" }, headers={ "Origin": "http://localhost:5173", "Authorization": "Bearer test_token_PAT-CORS-1" } ) assert response.status_code == 200 assert response.headers.get("access-control-allow-origin") == "http://localhost:5173" assert response.headers.get("access-control-allow-credentials") == "true" def test_cors_unauthorized_request_returns_cors_headers(): """Test that 401 Unauthorized responses STILL return CORS headers to allowed origin.""" response = client.post( "/v1/onboarding/start", json={"patient": {"id": "PAT-NOAUTH"}}, headers={"Origin": "http://localhost:3000"} ) assert response.status_code == 401 assert response.headers.get("access-control-allow-origin") == "http://localhost:3000" assert response.headers.get("access-control-allow-credentials") == "true" def test_cors_forbidden_request_returns_cors_headers(): """Test that 403 Forbidden responses STILL return CORS headers to allowed origin.""" # 1. Create session for PAT-CORS-OWNER res_start = client.post( "/v1/onboarding/start", json={ "patient": {"id": "PAT-CORS-OWNER", "name": "Owner", "age": 28, "gender": "male"}, "category": {"type": "general", "code": "general_medicine", "name": "General Medicine"} }, headers={"Authorization": "Bearer test_token_PAT-CORS-OWNER"} ) session_id = res_start.json()["session_id"] # 2. Access with different patient PAT-CORS-ATTACKER res_idor = client.post( f"/v1/onboarding/{session_id}/message", json={"message": "IDOR attempt"}, headers={ "Origin": "http://localhost:3000", "Authorization": "Bearer test_token_PAT-CORS-ATTACKER" } ) assert res_idor.status_code == 403 assert res_idor.headers.get("access-control-allow-origin") == "http://localhost:3000" assert res_idor.headers.get("access-control-allow-credentials") == "true" def test_cors_disallowed_origin_rejected(): """Test that disallowed origin (https://malicious-example.com) is NOT allowed by CORS headers.""" response = client.options( "/v1/onboarding/start", headers={ "Origin": "https://malicious-example.com", "Access-Control-Request-Method": "POST", "Access-Control-Request-Headers": "authorization, content-type", }, ) # Browsers block preflight if access-control-allow-origin does not match assert response.headers.get("access-control-allow-origin") != "https://malicious-example.com" def test_cors_voice_endpoints(): """Test preflight and actual POST on voice onboarding endpoints.""" # Preflight voice start res_opt = client.options( "/v1/voice-onboarding/start", headers={ "Origin": "http://localhost:5173", "Access-Control-Request-Method": "POST", "Access-Control-Request-Headers": "authorization, content-type" } ) assert res_opt.status_code == 200 assert res_opt.headers.get("access-control-allow-origin") == "http://localhost:5173" # Actual voice start res_post = client.post( "/v1/voice-onboarding/start", json={ "patient": {"id": "PAT-CORS-VOICE", "name": "Voice Test", "age": 40, "gender": "female"}, "language": "en-IN" }, headers={ "Origin": "http://localhost:5173", "Authorization": "Bearer test_token_PAT-CORS-VOICE" } ) assert res_post.status_code == 200 assert res_post.headers.get("access-control-allow-origin") == "http://localhost:5173" if __name__ == '__main__': pytest.main(["-v", __file__])