From 168f31ca3439107d2233c5cd7050fa588effc2f9 Mon Sep 17 00:00:00 2001 From: Pritom14 Date: Thu, 9 Apr 2026 15:07:48 +0530 Subject: [PATCH 1/2] fix: add field validation to Settings config (#10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - storage_mode: str → Literal["lite", "full"] (invalid values now caught at startup) - text_density_threshold: validated to 0.0–1.0 range - port: validated to 1–65535 range --- token0/config.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/token0/config.py b/token0/config.py index 7413ade..743cc01 100644 --- a/token0/config.py +++ b/token0/config.py @@ -1,10 +1,13 @@ +from typing import Literal + +from pydantic import field_validator from pydantic_settings import BaseSettings class Settings(BaseSettings): # Storage mode: "lite" (SQLite + in-memory) or "full" (Postgres + Redis + S3) # Use "lite" for local dev/testing, "full" for production - storage_mode: str = "lite" + storage_mode: Literal["lite", "full"] = "lite" # Database — only needed in full mode database_url: str = "postgresql+asyncpg://token0:token0@localhost:5432/token0" @@ -40,6 +43,20 @@ class Settings(BaseSettings): jpeg_quality: int = 85 text_density_threshold: float = 0.52 # Above this → OCR route instead of vision + @field_validator("text_density_threshold") + @classmethod + def validate_threshold(cls, v: float) -> float: + if not 0.0 <= v <= 1.0: + raise ValueError(f"text_density_threshold must be between 0.0 and 1.0, got {v}") + return v + + @field_validator("port") + @classmethod + def validate_port(cls, v: int) -> int: + if not 1 <= v <= 65535: + raise ValueError(f"port must be between 1 and 65535, got {v}") + return v + @property def is_lite(self) -> bool: return self.storage_mode == "lite" From 2e5bc40ca1736d21f29e57b5f615fdc779d2756f Mon Sep 17 00:00:00 2001 From: Pritom14 Date: Thu, 9 Apr 2026 15:30:23 +0530 Subject: [PATCH 2/2] test: add validator tests for Settings config fields --- tests/test_config.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/test_config.py diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..00b3719 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,42 @@ +"""Tests for Settings field validators.""" + +import pytest +from pydantic import ValidationError + +from token0.config import Settings + + +def test_storage_mode_valid_values(): + assert Settings(storage_mode="lite").storage_mode == "lite" + assert Settings(storage_mode="full").storage_mode == "full" + + +def test_storage_mode_invalid_raises(): + with pytest.raises(ValidationError): + Settings(storage_mode="prod") + + +def test_text_density_threshold_valid(): + assert Settings(text_density_threshold=0.0).text_density_threshold == 0.0 + assert Settings(text_density_threshold=1.0).text_density_threshold == 1.0 + assert Settings(text_density_threshold=0.52).text_density_threshold == 0.52 + + +def test_text_density_threshold_out_of_range_raises(): + with pytest.raises(ValidationError): + Settings(text_density_threshold=1.1) + with pytest.raises(ValidationError): + Settings(text_density_threshold=-0.1) + + +def test_port_valid(): + assert Settings(port=8000).port == 8000 + assert Settings(port=1).port == 1 + assert Settings(port=65535).port == 65535 + + +def test_port_out_of_range_raises(): + with pytest.raises(ValidationError): + Settings(port=0) + with pytest.raises(ValidationError): + Settings(port=65536)