""" Integration tests for domain verification flow. Tests the complete domain verification flow including DNS verification, email discovery, and code verification. """ import pytest from fastapi.testclient import TestClient from unittest.mock import Mock @pytest.fixture def verification_app(monkeypatch, tmp_path): """Create app for verification testing.""" db_path = tmp_path / "test.db" monkeypatch.setenv("GONDULF_SECRET_KEY", "a" * 32) monkeypatch.setenv("GONDULF_BASE_URL", "https://auth.example.com") monkeypatch.setenv("GONDULF_DATABASE_URL", f"sqlite:///{db_path}") monkeypatch.setenv("GONDULF_DEBUG", "true") from gondulf.main import app return app @pytest.fixture def verification_client(verification_app): """Create test client for verification tests.""" with TestClient(verification_app) as client: yield client @pytest.fixture def mock_verification_deps(verification_app, mock_dns_service, mock_email_service, mock_html_fetcher_with_email, mock_rate_limiter, test_code_storage): """Setup mock dependencies for verification.""" from gondulf.dependencies import get_verification_service, get_rate_limiter from gondulf.services.domain_verification import DomainVerificationService from gondulf.services.relme_parser import RelMeParser service = DomainVerificationService( dns_service=mock_dns_service, email_service=mock_email_service, code_storage=test_code_storage, html_fetcher=mock_html_fetcher_with_email, relme_parser=RelMeParser() ) verification_app.dependency_overrides[get_verification_service] = lambda: service verification_app.dependency_overrides[get_rate_limiter] = lambda: mock_rate_limiter yield service, test_code_storage verification_app.dependency_overrides.clear() class TestStartVerification: """Tests for starting domain verification.""" def test_start_verification_success(self, verification_client, mock_verification_deps): """Test successful start of domain verification.""" response = verification_client.post( "/api/verify/start", data={"me": "https://user.example.com"} ) assert response.status_code == 200 data = response.json() assert data["success"] is True assert "email" in data # Email should be masked assert "*" in data["email"] def test_start_verification_invalid_me_url(self, verification_client, mock_verification_deps): """Test verification fails with invalid me URL.""" response = verification_client.post( "/api/verify/start", data={"me": "not-a-valid-url"} ) assert response.status_code == 200 data = response.json() assert data["success"] is False assert data["error"] == "invalid_me_url" def test_start_verification_rate_limited(self, verification_app, verification_client, mock_rate_limiter_exceeded, verification_service): """Test verification fails when rate limited.""" from gondulf.dependencies import get_rate_limiter, get_verification_service verification_app.dependency_overrides[get_rate_limiter] = lambda: mock_rate_limiter_exceeded verification_app.dependency_overrides[get_verification_service] = lambda: verification_service response = verification_client.post( "/api/verify/start", data={"me": "https://user.example.com"} ) assert response.status_code == 200 data = response.json() assert data["success"] is False assert data["error"] == "rate_limit_exceeded" verification_app.dependency_overrides.clear() def test_start_verification_dns_failure(self, verification_app, verification_client, verification_service_dns_failure, mock_rate_limiter): """Test verification fails when DNS check fails.""" from gondulf.dependencies import get_rate_limiter, get_verification_service verification_app.dependency_overrides[get_rate_limiter] = lambda: mock_rate_limiter verification_app.dependency_overrides[get_verification_service] = lambda: verification_service_dns_failure response = verification_client.post( "/api/verify/start", data={"me": "https://user.example.com"} ) assert response.status_code == 200 data = response.json() assert data["success"] is False assert data["error"] == "dns_verification_failed" verification_app.dependency_overrides.clear() class TestVerifyCode: """Tests for verifying email code.""" def test_verify_code_success(self, verification_client, mock_verification_deps): """Test successful code verification.""" service, code_storage = mock_verification_deps # First start verification to store the code verification_client.post( "/api/verify/start", data={"me": "https://example.com/"} ) # Get the stored code stored_code = code_storage.get("email_verify:example.com") assert stored_code is not None # Verify the code response = verification_client.post( "/api/verify/code", data={"domain": "example.com", "code": stored_code} ) assert response.status_code == 200 data = response.json() assert data["success"] is True assert "email" in data def test_verify_code_invalid_code(self, verification_client, mock_verification_deps): """Test verification fails with invalid code.""" response = verification_client.post( "/api/verify/code", data={"domain": "example.com", "code": "000000"} ) assert response.status_code == 200 data = response.json() assert data["success"] is False assert data["error"] == "invalid_code" def test_verify_code_wrong_domain(self, verification_client, mock_verification_deps): """Test verification fails with wrong domain.""" service, code_storage = mock_verification_deps # Start verification for one domain verification_client.post( "/api/verify/start", data={"me": "https://example.com/"} ) # Get the stored code stored_code = code_storage.get("email_verify:example.com") # Try to verify with different domain response = verification_client.post( "/api/verify/code", data={"domain": "other.example.com", "code": stored_code} ) assert response.status_code == 200 data = response.json() assert data["success"] is False class TestVerificationSecurityHeaders: """Security tests for verification endpoints.""" def test_start_verification_security_headers(self, verification_client, mock_verification_deps): """Test verification endpoints include security headers.""" response = verification_client.post( "/api/verify/start", data={"me": "https://user.example.com"} ) assert "X-Frame-Options" in response.headers assert "X-Content-Type-Options" in response.headers def test_verify_code_security_headers(self, verification_client, mock_verification_deps): """Test code verification endpoint includes security headers.""" response = verification_client.post( "/api/verify/code", data={"domain": "example.com", "code": "123456"} ) assert "X-Frame-Options" in response.headers assert "X-Content-Type-Options" in response.headers class TestVerificationResponseFormat: """Tests for verification endpoint response formats.""" def test_start_verification_returns_json(self, verification_client, mock_verification_deps): """Test start verification returns JSON.""" response = verification_client.post( "/api/verify/start", data={"me": "https://user.example.com"} ) assert "application/json" in response.headers["content-type"] def test_verify_code_returns_json(self, verification_client, mock_verification_deps): """Test code verification returns JSON.""" response = verification_client.post( "/api/verify/code", data={"domain": "example.com", "code": "123456"} ) assert "application/json" in response.headers["content-type"] def test_success_response_includes_method(self, verification_client, mock_verification_deps): """Test successful verification includes verification method.""" response = verification_client.post( "/api/verify/start", data={"me": "https://user.example.com"} ) data = response.json() assert data["success"] is True assert "verification_method" in data