| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- from core.settings import AppSettings
- def test_app_settings_validate_scope_defaults_to_all(monkeypatch) -> None:
- monkeypatch.delenv("ZHUCE6_VALIDATE_SCOPE", raising=False)
- settings = AppSettings.from_env()
- assert settings.validate_scope == "all"
- def test_app_settings_rejects_legacy_validate_scope(monkeypatch) -> None:
- monkeypatch.setenv("ZHUCE6_VALIDATE_SCOPE", "legacy_invalid_scope")
- settings = AppSettings.from_env()
- assert settings.validate_scope == "all"
- def test_app_settings_account_survival_default_cohort_is_ten(monkeypatch) -> None:
- monkeypatch.delenv("ZHUCE6_ACCOUNT_SURVIVAL_COHORT_SIZE", raising=False)
- settings = AppSettings.from_env()
- assert settings.account_survival_cohort_size == 10
- def test_app_settings_d1_cleanup_defaults(monkeypatch) -> None:
- monkeypatch.delenv("ZHUCE6_D1_CLEANUP_ENABLED", raising=False)
- monkeypatch.delenv("ZHUCE6_D1_CLEANUP_INTERVAL", raising=False)
- monkeypatch.delenv("ZHUCE6_D1_DATABASE_ID", raising=False)
- monkeypatch.delenv("ZHUCE6_D1_MAIL_RETENTION_HOURS", raising=False)
- monkeypatch.delenv("ZHUCE6_D1_ADDRESS_RETENTION_HOURS", raising=False)
- settings = AppSettings.from_env()
- assert settings.d1_cleanup_enabled is True
- assert settings.d1_cleanup_interval == 1800
- assert settings.d1_database_id == ""
- assert settings.d1_mail_retention_hours == 2
- assert settings.d1_address_retention_hours == 24
- def test_app_settings_d1_cleanup_env_overrides(monkeypatch) -> None:
- monkeypatch.setenv("ZHUCE6_D1_CLEANUP_ENABLED", "false")
- monkeypatch.setenv("ZHUCE6_D1_CLEANUP_INTERVAL", "900")
- monkeypatch.setenv("ZHUCE6_D1_DATABASE_ID", "db-test")
- monkeypatch.setenv("ZHUCE6_D1_MAIL_RETENTION_HOURS", "6")
- monkeypatch.setenv("ZHUCE6_D1_ADDRESS_RETENTION_HOURS", "48")
- settings = AppSettings.from_env()
- assert settings.d1_cleanup_enabled is False
- assert settings.d1_cleanup_interval == 900
- assert settings.d1_database_id == "db-test"
- assert settings.d1_mail_retention_hours == 6
- assert settings.d1_address_retention_hours == 48
- def test_app_settings_reads_proxy_pool_direct_urls(monkeypatch) -> None:
- monkeypatch.setenv("ZHUCE6_PROXY_POOL_DIRECT_URLS", "socks5://1.2.3.4:1080;http://5.6.7.8:8080")
- settings = AppSettings.from_env()
- assert settings.proxy_pool_direct_urls == "socks5://1.2.3.4:1080;http://5.6.7.8:8080"
- def test_app_settings_matches_shell_default_values(monkeypatch, tmp_path) -> None:
- keys = [
- "ZHUCE6_CONFIG_DIR",
- "ZHUCE6_STATE_DIR",
- "ZHUCE6_LOG_DIR",
- "ZHUCE6_POOL_DIR",
- "ZHUCE6_REGISTER_LOG_FILE",
- "ZHUCE6_RUNTIME_STATE_FILE",
- "ZHUCE6_ACCOUNT_SURVIVAL_STATE_FILE",
- "ZHUCE6_RESPONSES_SURVIVAL_STATE_FILE",
- "ZHUCE6_ROTATE_INTERVAL",
- "ZHUCE6_CPA_RUNTIME_RECONCILE_ENABLED",
- "ZHUCE6_CPA_RUNTIME_RECONCILE_COOLDOWN_SECONDS",
- "ZHUCE6_CPA_RUNTIME_RECONCILE_RESTART_ENABLED",
- "ZHUCE6_REGISTER_PROXY",
- "ZHUCE6_REGISTER_MAIL_PROVIDER",
- "ZHUCE6_REGISTER_BATCH_THREADS",
- "ZHUCE6_REGISTER_BATCH_TARGET_COUNT",
- "ZHUCE6_REGISTER_BATCH_INTERVAL_SECONDS",
- "ZHUCE6_ACCOUNT_SURVIVAL_ENABLED",
- "ZHUCE6_DASHBOARD_ALLOWED_ORIGINS",
- "ZHUCE6_ENABLE_PROXY_POOL",
- "ZHUCE6_PROXY_POOL_CONFIG",
- "ZHUCE6_PROXY_POOL_SIZE",
- "ZHUCE6_PROXY_POOL_REGIONS",
- "ZHUCE6_REGISTER_FRESH_PROXY_REGIONS",
- "ZHUCE6_CFMAIL_ROTATION_WINDOW",
- "ZHUCE6_CFMAIL_ROTATION_BLACKLIST_THRESHOLD",
- "ZHUCE6_CFMAIL_ROTATION_MAX_SUCCESSES",
- "ZHUCE6_ACCOUNT_SURVIVAL_PROXY",
- "ZHUCE6_RESPONSES_SURVIVAL_RECENT_WINDOW_SECONDS",
- "ZHUCE6_RESPONSES_SURVIVAL_REQUIRE_PROVENANCE",
- "ZHUCE6_WARMUP_MIN_AGE_SECONDS",
- "ZHUCE6_WARMUP_MIN_SUCCESSFUL_PROBES",
- "ZHUCE6_VALIDATE_SCOPE",
- ]
- for key in keys:
- monkeypatch.delenv(key, raising=False)
- monkeypatch.setenv("ZHUCE6_PROJECT_ROOT", str(tmp_path))
- settings = AppSettings.from_env()
- assert settings.rotate_interval == 120
- assert settings.recycle_rewarm_cooldown_seconds == 1800
- assert settings.cpa_runtime_reconcile_enabled is True
- assert settings.cpa_runtime_reconcile_cooldown_seconds == 300
- assert settings.cpa_runtime_reconcile_restart_enabled is False
- assert settings.register_proxy == "http://127.0.0.1:7899"
- assert settings.register_mail_provider == "cfmail"
- assert settings.register_batch_threads == 1
- assert settings.register_batch_target_count == 20
- assert settings.register_batch_interval_seconds == 10800
- assert settings.account_survival_enabled is True
- assert settings.dashboard_allowed_origins == ()
- assert settings.enable_proxy_pool is True
- assert settings.proxy_pool_size == 20
- assert settings.proxy_pool_regions == ("tw", "sg", "jp", "hk", "us")
- assert settings.register_fresh_proxy_regions == ("tw", "sg")
- assert settings.proxy_pool_config == (tmp_path / "clash_config.yaml").resolve()
- assert settings.cfmail_rotation_window == 10
- assert settings.cfmail_rotation_blacklist_threshold == 6
- assert settings.cfmail_rotation_max_successes == 2
- assert settings.account_survival_proxy == "http://127.0.0.1:7899"
- assert settings.responses_survival_state_file == (tmp_path / "state" / "responses_survival_tracker.json").resolve()
- assert settings.responses_survival_recent_window_seconds == 1800
- assert settings.responses_survival_require_provenance is True
- assert settings.warmup_min_age_seconds == 600
- assert settings.warmup_min_successful_probes == 2
- assert settings.config_dir == (tmp_path / "config").resolve()
- assert settings.state_dir == (tmp_path / "state").resolve()
- assert settings.log_dir == (tmp_path / "logs").resolve()
- assert settings.pool_dir == (tmp_path / "pool").resolve()
- assert settings.validate_scope == "all"
- assert not hasattr(settings, "main_" + "pool_target")
- assert not hasattr(settings, "rotate_probe_max_count")
- assert not hasattr(settings, "promotion_probe_proxy")
- assert not hasattr(settings, "promotion_probe_timeout_seconds")
- def test_app_settings_reads_dashboard_allowed_origins(monkeypatch) -> None:
- monkeypatch.setenv(
- "ZHUCE6_DASHBOARD_ALLOWED_ORIGINS",
- "http://127.0.0.1:8317, http://localhost:3000/ , https://dash.example.com",
- )
- settings = AppSettings.from_env()
- assert settings.dashboard_allowed_origins == (
- "http://127.0.0.1:8317",
- "http://localhost:3000",
- "https://dash.example.com",
- )
- def test_app_settings_validate_cfmail_env(monkeypatch) -> None:
- for key in [
- "ZHUCE6_CFMAIL_API_TOKEN",
- "ZHUCE6_CFMAIL_CF_AUTH_EMAIL",
- "ZHUCE6_CFMAIL_CF_AUTH_KEY",
- "ZHUCE6_CFMAIL_CF_ACCOUNT_ID",
- "ZHUCE6_CFMAIL_CF_ZONE_ID",
- "ZHUCE6_CFMAIL_WORKER_NAME",
- "ZHUCE6_CFMAIL_ZONE_NAME",
- ]:
- monkeypatch.delenv(key, raising=False)
- settings = AppSettings.from_env()
- missing = settings.validate_cfmail_env()
- assert missing == [
- "ZHUCE6_CFMAIL_API_TOKEN",
- "ZHUCE6_CFMAIL_CF_ACCOUNT_ID",
- "ZHUCE6_CFMAIL_CF_ZONE_ID",
- "ZHUCE6_CFMAIL_WORKER_NAME",
- "ZHUCE6_CFMAIL_ZONE_NAME",
- ]
- def test_app_settings_validate_cfmail_env_accepts_legacy_email_key_pair(monkeypatch) -> None:
- monkeypatch.delenv("ZHUCE6_CFMAIL_API_TOKEN", raising=False)
- monkeypatch.setenv("ZHUCE6_CFMAIL_CF_AUTH_EMAIL", "cf@example.com")
- monkeypatch.setenv("ZHUCE6_CFMAIL_CF_AUTH_KEY", "global-key")
- monkeypatch.setenv("ZHUCE6_CFMAIL_CF_ACCOUNT_ID", "account-1")
- monkeypatch.setenv("ZHUCE6_CFMAIL_CF_ZONE_ID", "zone-1")
- monkeypatch.setenv("ZHUCE6_CFMAIL_WORKER_NAME", "worker-one")
- monkeypatch.setenv("ZHUCE6_CFMAIL_ZONE_NAME", "example.com")
- settings = AppSettings.from_env()
- assert settings.validate_cfmail_env() == []
- def test_app_settings_reads_sub2api_backend(monkeypatch) -> None:
- monkeypatch.setenv("ZHUCE6_BACKEND", "sub2api")
- monkeypatch.setenv("ZHUCE6_SUB2API_BASE_URL", "http://127.0.0.1:8080")
- monkeypatch.setenv("ZHUCE6_SUB2API_ADMIN_EMAIL", "admin@sub2api.local")
- monkeypatch.setenv("ZHUCE6_SUB2API_ADMIN_PASSWORD", "secret")
- monkeypatch.setenv("ZHUCE6_SUB2API_API_KEY", "api-key")
- settings = AppSettings.from_env()
- assert settings.backend == "sub2api"
- assert settings.sub2api_base_url == "http://127.0.0.1:8080"
- assert settings.sub2api_admin_email == "admin@sub2api.local"
- assert settings.sub2api_admin_password == "secret"
- assert settings.sub2api_api_key == "api-key"
- def test_app_settings_no_longer_exposes_legacy_cpa_container_fields(monkeypatch, tmp_path) -> None:
- monkeypatch.setenv("ZHUCE6_PROJECT_ROOT", str(tmp_path))
- monkeypatch.setenv("ZHUCE6_CPA_MANAGEMENT_MODE", "legacy")
- monkeypatch.setenv("ZHUCE6_CPA_REMOTE_MODE", "false")
- monkeypatch.setenv("ZHUCE6_CPA_CONTAINER", "legacy-container")
- monkeypatch.setenv("ZHUCE6_CPA_AUTH_DIR", "/legacy/auth")
- settings = AppSettings.from_env()
- assert not hasattr(settings, "cpa_container")
- assert not hasattr(settings, "cpa_auth_dir")
- assert not hasattr(settings, "cpa_management_mode")
- def test_app_settings_ignores_legacy_cpa_management_env(monkeypatch, tmp_path) -> None:
- monkeypatch.setenv("ZHUCE6_PROJECT_ROOT", str(tmp_path))
- monkeypatch.setenv("ZHUCE6_CPA_MANAGEMENT_MODE", "legacy")
- monkeypatch.setenv("ZHUCE6_CPA_REMOTE_MODE", "false")
- monkeypatch.setenv("ZHUCE6_CPA_CONTAINER", "legacy-container")
- monkeypatch.setenv("ZHUCE6_CPA_AUTH_DIR", "/legacy/auth")
- monkeypatch.setenv("ZHUCE6_CPA_MANAGEMENT_BASE_URL", "http://127.0.0.1:9000/v0/management")
- monkeypatch.setenv("ZHUCE6_CPA_MANAGEMENT_KEY", "key-1")
- settings = AppSettings.from_env()
- assert settings.cpa_management_base_url == "http://127.0.0.1:9000/v0/management"
- assert settings.cpa_management_key == "key-1"
|