""" Tests for webhook URL validation. Story 3.7: Webhook - Spécification URL """ import pytest from unittest.mock import patch, MagicMock class TestWebhookURLValidator: """Tests for WebhookURLValidator class.""" @pytest.fixture def validator(self): """Create a WebhookURLValidator instance.""" from middleware.validation import WebhookURLValidator return WebhookURLValidator() def test_valid_https_url(self, validator): """Valid HTTPS URL should pass.""" url = "https://example.com/webhook" is_valid, error, details = validator.validate(url) assert is_valid is True assert error is None assert details is None def test_valid_http_url(self, validator): """Valid HTTP URL should pass.""" url = "http://example.com/webhook" is_valid, error, details = validator.validate(url) assert is_valid is True assert error is None def test_invalid_scheme_ftp(self, validator): """FTP URL should be rejected.""" url = "ftp://example.com/webhook" is_valid, error, details = validator.validate(url) assert is_valid is False assert "http" in error.lower() def test_invalid_scheme_no_scheme(self, validator): """URL without scheme should be rejected.""" url = "example.com/webhook" is_valid, error, details = validator.validate(url) assert is_valid is False def test_localhost_blocked(self, validator): """Localhost should be blocked.""" urls = [ "http://localhost/webhook", "http://127.0.0.1/webhook", "http://0.0.0.0/webhook", ] for url in urls: is_valid, error, details = validator.validate(url) assert is_valid is False, f"URL {url} should be blocked" assert "localhost" in error.lower() or "priv" in error.lower() or "non autoris" in error.lower() def test_credentials_in_url_blocked(self, validator): """URLs with credentials should be blocked.""" url = "https://user:password@example.com/webhook" is_valid, error, details = validator.validate(url) assert is_valid is False assert "credentials" in error.lower() or "identifiants" in error.lower() def test_empty_url_valid(self, validator): """Empty URL should be valid (optional parameter).""" is_valid, error, details = validator.validate("") assert is_valid is True def test_none_url_valid(self, validator): """None URL should be valid (optional parameter).""" is_valid, error, details = validator.validate(None) assert is_valid is True def test_url_with_port(self, validator): """URL with port should be valid.""" url = "https://example.com:8080/webhook" is_valid, error, details = validator.validate(url) assert is_valid is True def test_url_with_query_params(self, validator): """URL with query parameters should be valid.""" url = "https://example.com/webhook?token=abc123&source=api" is_valid, error, details = validator.validate(url) assert is_valid is True def test_url_with_path(self, validator): """URL with path should be valid.""" url = "https://example.com/api/v1/notifications/translation-complete" is_valid, error, details = validator.validate(url) assert is_valid is True def test_url_missing_hostname(self, validator): """URL without hostname should be rejected.""" url = "https:///webhook" is_valid, error, details = validator.validate(url) assert is_valid is False assert "hostname" in error.lower() or "hôte" in error.lower() def test_ipv6_localhost_blocked(self, validator): """IPv6 localhost should be blocked.""" url = "http://[::1]/webhook" is_valid, error, details = validator.validate(url) assert is_valid is False def test_private_ip_blocked(self, validator): """Private IP addresses should be blocked.""" private_ips = [ "http://10.0.0.1/webhook", "http://172.16.0.1/webhook", "http://192.168.1.1/webhook", ] for url in private_ips: is_valid, error, details = validator.validate(url) assert is_valid is False, f"URL {url} should be blocked" assert "priv" in error.lower() or "non autoris" in error.lower() class TestWebhookURLIntegration: """Integration tests for webhook URL in translate endpoint.""" @pytest.fixture def client(self): """Create test client.""" from fastapi.testclient import TestClient from main import app return TestClient(app) @pytest.fixture def auth_headers(self): """Create auth headers for testing.""" # This would need a valid token in real tests return {"Authorization": "Bearer test_token"} @pytest.fixture def sample_file(self, tmp_path): """Create a sample Excel file for testing.""" import zipfile file_path = tmp_path / "test.xlsx" # Create a minimal valid xlsx file (ZIP with correct magic bytes) with zipfile.ZipFile(file_path, 'w') as zf: zf.writestr("[Content_Types].xml", '') return file_path def test_translate_with_valid_webhook_url(self, client, sample_file): """Translation with valid webhook_url should succeed.""" # This test would need proper authentication setup # For now, we test the validation logic directly from middleware.validation import webhook_validator url = "https://example.com/webhook" is_valid, error, details = webhook_validator.validate(url) assert is_valid is True def test_translate_with_invalid_webhook_url(self, client): """Translation with invalid webhook_url should return 400.""" from middleware.validation import webhook_validator url = "ftp://example.com/webhook" is_valid, error, details = webhook_validator.validate(url) assert is_valid is False assert "http" in error.lower() def test_translate_without_webhook_url(self, client): """Translation without webhook_url should succeed.""" from middleware.validation import webhook_validator # Empty webhook URL should be valid (optional) is_valid, error, details = webhook_validator.validate("") assert is_valid is True is_valid, error, details = webhook_validator.validate(None) assert is_valid is True class TestWebhookValidatorSingleton: """Tests for the webhook_validator singleton instance.""" def test_singleton_exists(self): """The webhook_validator singleton should be available.""" from middleware.validation import webhook_validator assert webhook_validator is not None def test_singleton_is_validator(self): """The webhook_validator should be a WebhookURLValidator instance.""" from middleware.validation import webhook_validator, WebhookURLValidator assert isinstance(webhook_validator, WebhookURLValidator) def test_singleton_default_settings(self): """The singleton should have default security settings enabled.""" from middleware.validation import webhook_validator assert webhook_validator.block_private_ips is True assert "http" in webhook_validator.allowed_schemes assert "https" in webhook_validator.allowed_schemes