Loading tests/test_cli_dotenv.py +4 −6 Original line number Diff line number Diff line Loading @@ -7,6 +7,8 @@ from pathlib import Path import pytest from tdoc_crawler.config import TDocCrawlerConfig class TestLoadDotenvConsolidation: """Test that load_dotenv is called once at CLI entry.""" Loading Loading @@ -59,23 +61,19 @@ class TestLoadDotenvConsolidation: class TestBackwardCompatibility: """Test that existing .env patterns still work.""" def test_existing_env_vars_still_work(self, monkeypatch) -> None: def test_existing_env_vars_still_work(self, monkeypatch: pytest.MonkeyPatch) -> None: """Direct environment variables still take precedence.""" test_path = Path.home() / ".explicit_cache_test" monkeypatch.setenv("TDC_CACHE_DIR", str(test_path)) from tdoc_crawler.config import TDocCrawlerConfig config = TDocCrawlerConfig() assert config.path.cache_dir == test_path def test_http_cache_env_vars_work(self, monkeypatch) -> None: def test_http_cache_env_vars_work(self, monkeypatch: pytest.MonkeyPatch) -> None: """HTTP_CACHE_TTL env var still works.""" monkeypatch.setenv("HTTP_CACHE_TTL", "3600") from tdoc_crawler.config import TDocCrawlerConfig config = TDocCrawlerConfig() assert config.http.cache_ttl == 3600 Loading tests/test_credentials.py +15 −17 Original line number Diff line number Diff line Loading @@ -6,7 +6,6 @@ import pytest from tdoc_crawler.config.settings import CredentialsConfig from tdoc_crawler.credentials import resolve_credentials from tdoc_crawler.models.base import PortalCredentials class TestCredentialsWithConfig: Loading @@ -14,7 +13,7 @@ class TestCredentialsWithConfig: def test_resolve_uses_config_credentials(self) -> None: """resolve_credentials uses credentials_config when no CLI params.""" config = CredentialsConfig(username="config_user", password="config_pass") config: CredentialsConfig = CredentialsConfig(username="config_user", password="config_pass") result = resolve_credentials(credentials_config=config) Loading @@ -24,7 +23,7 @@ class TestCredentialsWithConfig: def test_resolve_cli_overrides_config(self) -> None: """CLI parameters take precedence over credentials_config.""" config = CredentialsConfig(username="config_user", password="config_pass") config: CredentialsConfig = CredentialsConfig(username="config_user", password="config_pass") result = resolve_credentials( username="cli_user", Loading @@ -35,7 +34,7 @@ class TestCredentialsWithConfig: assert result.username == "cli_user" assert result.password == "cli_pass" def test_resolve_config_overrides_env(self, monkeypatch) -> None: def test_resolve_config_overrides_env(self, monkeypatch: pytest.MonkeyPatch) -> None: """credentials_config takes precedence over env vars.""" monkeypatch.setenv("TDC_EOL_USERNAME", "env_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "env_pass") Loading @@ -47,18 +46,17 @@ class TestCredentialsWithConfig: assert result.username == "config_user" assert result.password == "config_pass" def test_resolve_env_when_no_config(self, monkeypatch) -> None: def test_resolve_env_when_no_config(self, monkeypatch: pytest.MonkeyPatch) -> None: """Without credentials_config, falls back to env vars.""" monkeypatch.setenv("TDC_EOL_USERNAME", "env_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "env_pass") result = resolve_credentials() assert result is not None assert result.username == "env_user" assert result.password == "env_pass" def test_resolve_returns_none_when_no_creds(self, monkeypatch) -> None: def test_resolve_returns_none_when_no_creds(self, monkeypatch: pytest.MonkeyPatch) -> None: """Returns None when no credentials found anywhere.""" # Clear any existing env vars that might interfere monkeypatch.delenv("TDC_EOL_USERNAME", raising=False) Loading @@ -82,34 +80,34 @@ class TestCredentialsWithConfig: class TestCredentialsResolutionOrder: """Test resolution order: CLI > Config > Env > Prompt.""" def test_full_resolution_order(self, monkeypatch) -> None: def test_full_resolution_order(self, monkeypatch: pytest.MonkeyPatch) -> None: """Verify complete precedence chain.""" monkeypatch.setenv("TDC_EOL_USERNAME", "env_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "env_pass") config = CredentialsConfig(username="config_user", password="config_pass") # CLI should win over everything result = resolve_credentials( credentials_config=config, username="cli_user", password="cli_pass", credentials_config=CredentialsConfig(username="config_user", password="config_pass"), ) # CLI should win assert result.username == "cli_user" assert result.password == "cli_pass" def test_config_beats_env(self, monkeypatch) -> None: def test_config_beats_env(self, monkeypatch: pytest.MonkeyPatch) -> None: """Config values should override env vars.""" monkeypatch.setenv("TDC_EOL_USERNAME", "env_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "env_pass") config = CredentialsConfig(username="config_user", password="config_pass") result = resolve_credentials( credentials_config=CredentialsConfig(username="config_user", password="config_pass"), ) result = resolve_credentials(credentials_config=config) assert result.username == "config_user" assert result.password == "config_pass" def test_env_beats_prompt(self, monkeypatch) -> None: def test_env_beats_prompt(self, monkeypatch: pytest.MonkeyPatch) -> None: """Env vars should prevent prompting.""" monkeypatch.setenv("TDC_EOL_USERNAME", "env_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "env_pass") Loading @@ -126,7 +124,7 @@ class TestCredentialsResolutionOrder: class TestCredentialsBackwardCompatibility: """Test backward compatibility without credentials_config.""" def test_resolve_with_no_config_works(self, monkeypatch) -> None: def test_resolve_with_no_config_works(self, monkeypatch: pytest.MonkeyPatch) -> None: """resolve_credentials works without credentials_config parameter.""" monkeypatch.setenv("TDC_EOL_USERNAME", "legacy_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "legacy_pass") Loading @@ -138,7 +136,7 @@ class TestCredentialsBackwardCompatibility: assert result.username == "legacy_user" assert result.password == "legacy_pass" def test_resolve_with_cli_only(self, monkeypatch) -> None: def test_resolve_with_cli_only(self, monkeypatch: pytest.MonkeyPatch) -> None: """CLI-only credentials still work.""" # Clear any existing env vars monkeypatch.delenv("TDC_EOL_USERNAME", raising=False) Loading tests/test_http_client.py +7 −14 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ from hishel.requests import CacheAdapter from requests.adapters import HTTPAdapter from tdoc_crawler.config import DEFAULT_HTTP_CACHE_FILENAME, CacheManager, reset_cache_managers from tdoc_crawler.config.settings import HttpConfig from tdoc_crawler.http_client import PoolConfig, create_cached_session, download_to_file from tdoc_crawler.http_client.session import SSLContextCacheAdapter, SSLContextHTTPAdapter, resolve_ssl_verify from tdoc_crawler.models.base import HttpCacheConfig Loading Loading @@ -460,8 +461,6 @@ class TestHttpSessionWithConfig: def test_resolve_ssl_verify_uses_http_config(self) -> None: """resolve_ssl_verify uses http_config.verify_ssl when no explicit verify.""" from tdoc_crawler.config.settings import HttpConfig http_config = HttpConfig(verify_ssl=False) result = resolve_ssl_verify(http_config=http_config) Loading @@ -470,15 +469,13 @@ class TestHttpSessionWithConfig: def test_resolve_ssl_verify_explicit_overrides_config(self) -> None: """Explicit verify parameter takes precedence over http_config.""" from tdoc_crawler.config.settings import HttpConfig http_config = HttpConfig(verify_ssl=False) result = resolve_ssl_verify(verify=True, http_config=http_config) assert result is True def test_resolve_ssl_verify_fallback_to_env(self, monkeypatch) -> None: def test_resolve_ssl_verify_fallback_to_env(self, monkeypatch: pytest.MonkeyPatch) -> None: """Without http_config, falls back to env var.""" monkeypatch.setenv("TDC_VERIFY_SSL", "false") Loading @@ -488,8 +485,6 @@ class TestHttpSessionWithConfig: def test_create_cached_session_uses_config_cache_enabled(self, test_cache_dir: Path) -> None: """create_cached_session respects http_config.cache_enabled=False.""" from tdoc_crawler.config.settings import HttpConfig CacheManager(root_path=test_cache_dir, name="test_config_cache").register() http_config = HttpConfig(cache_enabled=False) Loading @@ -503,21 +498,19 @@ class TestHttpSessionWithConfig: def test_create_cached_session_explicit_overrides_config(self, test_cache_dir: Path) -> None: """Explicit http_cache_enabled overrides http_config.""" from tdoc_crawler.config.settings import HttpConfig CacheManager(root_path=test_cache_dir, name="test_explicit_override").register() http_config = HttpConfig(cache_enabled=False) http_config = HttpConfig(cache_enabled=True) # Explicit True should override config False session = create_cached_session( cache_manager_name="test_explicit_override", http_config=http_config, http_cache_enabled=True, http_cache_enabled=False, ) # Explicit False should override http_config.cache_enabled=True assert session is not None # Should have cache adapter since explicit True overrides config False assert isinstance(session.adapters["http://"], CacheAdapter) # Should have plain HTTP adapter since caching is explicitly disabled assert isinstance(session.adapters["http://"], SSLContextHTTPAdapter) session.close() Loading Loading
tests/test_cli_dotenv.py +4 −6 Original line number Diff line number Diff line Loading @@ -7,6 +7,8 @@ from pathlib import Path import pytest from tdoc_crawler.config import TDocCrawlerConfig class TestLoadDotenvConsolidation: """Test that load_dotenv is called once at CLI entry.""" Loading Loading @@ -59,23 +61,19 @@ class TestLoadDotenvConsolidation: class TestBackwardCompatibility: """Test that existing .env patterns still work.""" def test_existing_env_vars_still_work(self, monkeypatch) -> None: def test_existing_env_vars_still_work(self, monkeypatch: pytest.MonkeyPatch) -> None: """Direct environment variables still take precedence.""" test_path = Path.home() / ".explicit_cache_test" monkeypatch.setenv("TDC_CACHE_DIR", str(test_path)) from tdoc_crawler.config import TDocCrawlerConfig config = TDocCrawlerConfig() assert config.path.cache_dir == test_path def test_http_cache_env_vars_work(self, monkeypatch) -> None: def test_http_cache_env_vars_work(self, monkeypatch: pytest.MonkeyPatch) -> None: """HTTP_CACHE_TTL env var still works.""" monkeypatch.setenv("HTTP_CACHE_TTL", "3600") from tdoc_crawler.config import TDocCrawlerConfig config = TDocCrawlerConfig() assert config.http.cache_ttl == 3600 Loading
tests/test_credentials.py +15 −17 Original line number Diff line number Diff line Loading @@ -6,7 +6,6 @@ import pytest from tdoc_crawler.config.settings import CredentialsConfig from tdoc_crawler.credentials import resolve_credentials from tdoc_crawler.models.base import PortalCredentials class TestCredentialsWithConfig: Loading @@ -14,7 +13,7 @@ class TestCredentialsWithConfig: def test_resolve_uses_config_credentials(self) -> None: """resolve_credentials uses credentials_config when no CLI params.""" config = CredentialsConfig(username="config_user", password="config_pass") config: CredentialsConfig = CredentialsConfig(username="config_user", password="config_pass") result = resolve_credentials(credentials_config=config) Loading @@ -24,7 +23,7 @@ class TestCredentialsWithConfig: def test_resolve_cli_overrides_config(self) -> None: """CLI parameters take precedence over credentials_config.""" config = CredentialsConfig(username="config_user", password="config_pass") config: CredentialsConfig = CredentialsConfig(username="config_user", password="config_pass") result = resolve_credentials( username="cli_user", Loading @@ -35,7 +34,7 @@ class TestCredentialsWithConfig: assert result.username == "cli_user" assert result.password == "cli_pass" def test_resolve_config_overrides_env(self, monkeypatch) -> None: def test_resolve_config_overrides_env(self, monkeypatch: pytest.MonkeyPatch) -> None: """credentials_config takes precedence over env vars.""" monkeypatch.setenv("TDC_EOL_USERNAME", "env_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "env_pass") Loading @@ -47,18 +46,17 @@ class TestCredentialsWithConfig: assert result.username == "config_user" assert result.password == "config_pass" def test_resolve_env_when_no_config(self, monkeypatch) -> None: def test_resolve_env_when_no_config(self, monkeypatch: pytest.MonkeyPatch) -> None: """Without credentials_config, falls back to env vars.""" monkeypatch.setenv("TDC_EOL_USERNAME", "env_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "env_pass") result = resolve_credentials() assert result is not None assert result.username == "env_user" assert result.password == "env_pass" def test_resolve_returns_none_when_no_creds(self, monkeypatch) -> None: def test_resolve_returns_none_when_no_creds(self, monkeypatch: pytest.MonkeyPatch) -> None: """Returns None when no credentials found anywhere.""" # Clear any existing env vars that might interfere monkeypatch.delenv("TDC_EOL_USERNAME", raising=False) Loading @@ -82,34 +80,34 @@ class TestCredentialsWithConfig: class TestCredentialsResolutionOrder: """Test resolution order: CLI > Config > Env > Prompt.""" def test_full_resolution_order(self, monkeypatch) -> None: def test_full_resolution_order(self, monkeypatch: pytest.MonkeyPatch) -> None: """Verify complete precedence chain.""" monkeypatch.setenv("TDC_EOL_USERNAME", "env_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "env_pass") config = CredentialsConfig(username="config_user", password="config_pass") # CLI should win over everything result = resolve_credentials( credentials_config=config, username="cli_user", password="cli_pass", credentials_config=CredentialsConfig(username="config_user", password="config_pass"), ) # CLI should win assert result.username == "cli_user" assert result.password == "cli_pass" def test_config_beats_env(self, monkeypatch) -> None: def test_config_beats_env(self, monkeypatch: pytest.MonkeyPatch) -> None: """Config values should override env vars.""" monkeypatch.setenv("TDC_EOL_USERNAME", "env_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "env_pass") config = CredentialsConfig(username="config_user", password="config_pass") result = resolve_credentials( credentials_config=CredentialsConfig(username="config_user", password="config_pass"), ) result = resolve_credentials(credentials_config=config) assert result.username == "config_user" assert result.password == "config_pass" def test_env_beats_prompt(self, monkeypatch) -> None: def test_env_beats_prompt(self, monkeypatch: pytest.MonkeyPatch) -> None: """Env vars should prevent prompting.""" monkeypatch.setenv("TDC_EOL_USERNAME", "env_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "env_pass") Loading @@ -126,7 +124,7 @@ class TestCredentialsResolutionOrder: class TestCredentialsBackwardCompatibility: """Test backward compatibility without credentials_config.""" def test_resolve_with_no_config_works(self, monkeypatch) -> None: def test_resolve_with_no_config_works(self, monkeypatch: pytest.MonkeyPatch) -> None: """resolve_credentials works without credentials_config parameter.""" monkeypatch.setenv("TDC_EOL_USERNAME", "legacy_user") monkeypatch.setenv("TDC_EOL_PASSWORD", "legacy_pass") Loading @@ -138,7 +136,7 @@ class TestCredentialsBackwardCompatibility: assert result.username == "legacy_user" assert result.password == "legacy_pass" def test_resolve_with_cli_only(self, monkeypatch) -> None: def test_resolve_with_cli_only(self, monkeypatch: pytest.MonkeyPatch) -> None: """CLI-only credentials still work.""" # Clear any existing env vars monkeypatch.delenv("TDC_EOL_USERNAME", raising=False) Loading
tests/test_http_client.py +7 −14 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ from hishel.requests import CacheAdapter from requests.adapters import HTTPAdapter from tdoc_crawler.config import DEFAULT_HTTP_CACHE_FILENAME, CacheManager, reset_cache_managers from tdoc_crawler.config.settings import HttpConfig from tdoc_crawler.http_client import PoolConfig, create_cached_session, download_to_file from tdoc_crawler.http_client.session import SSLContextCacheAdapter, SSLContextHTTPAdapter, resolve_ssl_verify from tdoc_crawler.models.base import HttpCacheConfig Loading Loading @@ -460,8 +461,6 @@ class TestHttpSessionWithConfig: def test_resolve_ssl_verify_uses_http_config(self) -> None: """resolve_ssl_verify uses http_config.verify_ssl when no explicit verify.""" from tdoc_crawler.config.settings import HttpConfig http_config = HttpConfig(verify_ssl=False) result = resolve_ssl_verify(http_config=http_config) Loading @@ -470,15 +469,13 @@ class TestHttpSessionWithConfig: def test_resolve_ssl_verify_explicit_overrides_config(self) -> None: """Explicit verify parameter takes precedence over http_config.""" from tdoc_crawler.config.settings import HttpConfig http_config = HttpConfig(verify_ssl=False) result = resolve_ssl_verify(verify=True, http_config=http_config) assert result is True def test_resolve_ssl_verify_fallback_to_env(self, monkeypatch) -> None: def test_resolve_ssl_verify_fallback_to_env(self, monkeypatch: pytest.MonkeyPatch) -> None: """Without http_config, falls back to env var.""" monkeypatch.setenv("TDC_VERIFY_SSL", "false") Loading @@ -488,8 +485,6 @@ class TestHttpSessionWithConfig: def test_create_cached_session_uses_config_cache_enabled(self, test_cache_dir: Path) -> None: """create_cached_session respects http_config.cache_enabled=False.""" from tdoc_crawler.config.settings import HttpConfig CacheManager(root_path=test_cache_dir, name="test_config_cache").register() http_config = HttpConfig(cache_enabled=False) Loading @@ -503,21 +498,19 @@ class TestHttpSessionWithConfig: def test_create_cached_session_explicit_overrides_config(self, test_cache_dir: Path) -> None: """Explicit http_cache_enabled overrides http_config.""" from tdoc_crawler.config.settings import HttpConfig CacheManager(root_path=test_cache_dir, name="test_explicit_override").register() http_config = HttpConfig(cache_enabled=False) http_config = HttpConfig(cache_enabled=True) # Explicit True should override config False session = create_cached_session( cache_manager_name="test_explicit_override", http_config=http_config, http_cache_enabled=True, http_cache_enabled=False, ) # Explicit False should override http_config.cache_enabled=True assert session is not None # Should have cache adapter since explicit True overrides config False assert isinstance(session.adapters["http://"], CacheAdapter) # Should have plain HTTP adapter since caching is explicitly disabled assert isinstance(session.adapters["http://"], SSLContextHTTPAdapter) session.close() Loading