diff --git a/document-parser/infra/settings.py b/document-parser/infra/settings.py index ebb596c..60cc3c5 100644 --- a/document-parser/infra/settings.py +++ b/document-parser/infra/settings.py @@ -25,6 +25,27 @@ class Settings: default_factory=lambda: ["http://localhost:3000", "http://localhost:5173"] ) + def __post_init__(self) -> None: + errors: list[str] = [] + if self.document_timeout <= 0: + errors.append(f"document_timeout must be > 0 (got {self.document_timeout})") + if self.conversion_timeout <= 0: + errors.append(f"conversion_timeout must be > 0 (got {self.conversion_timeout})") + if self.max_concurrent_analyses < 1: + errors.append( + f"max_concurrent_analyses must be >= 1 (got {self.max_concurrent_analyses})" + ) + if self.max_page_count < 0: + errors.append(f"max_page_count must be >= 0 (got {self.max_page_count})") + if self.max_file_size < 0: + errors.append(f"max_file_size must be >= 0 (got {self.max_file_size})") + if self.default_table_mode not in ("accurate", "fast"): + errors.append( + f"default_table_mode must be 'accurate' or 'fast' (got '{self.default_table_mode}')" + ) + if errors: + raise ValueError("Invalid settings:\n " + "\n ".join(errors)) + @classmethod def from_env(cls) -> Settings: """Build a Settings instance from environment variables.""" diff --git a/document-parser/tests/test_settings.py b/document-parser/tests/test_settings.py index 1b8837c..aa27233 100644 --- a/document-parser/tests/test_settings.py +++ b/document-parser/tests/test_settings.py @@ -29,6 +29,57 @@ class TestSettingsDefaults: s.upload_dir = "/other" # type: ignore[misc] +class TestSettingsValidation: + def test_negative_document_timeout_rejected(self): + import pytest + + with pytest.raises(ValueError, match="document_timeout must be > 0"): + Settings(document_timeout=-1.0) + + def test_zero_document_timeout_rejected(self): + import pytest + + with pytest.raises(ValueError, match="document_timeout must be > 0"): + Settings(document_timeout=0) + + def test_negative_conversion_timeout_rejected(self): + import pytest + + with pytest.raises(ValueError, match="conversion_timeout must be > 0"): + Settings(conversion_timeout=-1) + + def test_zero_max_concurrent_rejected(self): + import pytest + + with pytest.raises(ValueError, match="max_concurrent_analyses must be >= 1"): + Settings(max_concurrent_analyses=0) + + def test_negative_max_page_count_rejected(self): + import pytest + + with pytest.raises(ValueError, match="max_page_count must be >= 0"): + Settings(max_page_count=-1) + + def test_negative_max_file_size_rejected(self): + import pytest + + with pytest.raises(ValueError, match="max_file_size must be >= 0"): + Settings(max_file_size=-1) + + def test_invalid_table_mode_rejected(self): + import pytest + + with pytest.raises(ValueError, match="default_table_mode must be"): + Settings(default_table_mode="turbo") + + def test_multiple_errors_reported(self): + import pytest + + with pytest.raises(ValueError, match="document_timeout") as exc_info: + Settings(document_timeout=-1, conversion_timeout=-1) + assert "conversion_timeout" in str(exc_info.value) + + class TestSettingsFromEnv: def test_reads_env_vars(self, monkeypatch): monkeypatch.setenv("APP_VERSION", "1.2.3")