Files
office_translator/tests/test_webhook_validation.py
Sepehr Ramezani 26bd096a06 feat: production deployment - full update with providers, admin, glossaries, pricing, tests
Major changes across backend, frontend, infrastructure:
- Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud)
- Admin panel: user management, pricing, settings
- Glossary system with CSV import/export
- Subscription and tier quota management
- Security hardening (rate limiting, API key auth, path traversal fixes)
- Docker compose for dev, prod, and IONOS deployment
- Alembic migrations for new tables
- Frontend: dashboard, pricing page, landing page, i18n (en/fr)
- Test suite and verification scripts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-25 15:01:47 +02:00

195 lines
7.5 KiB
Python

"""
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", '<?xml version="1.0"?>')
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