from __future__ import annotations from pathlib import Path import pytest from cf_temp_email_deploy.config import ( apply_overrides, default_config_document, load_config, parse_toml_value, save_toml_document, save_state, set_dotted_value, ) from cf_temp_email_deploy.errors import ConfigError from cf_temp_email_deploy.models import DeploymentState def test_parse_toml_value_supports_structured_literals() -> None: assert parse_toml_value('"abc"') == "abc" assert parse_toml_value("true") is True assert parse_toml_value("[1, 2, 3]") == [1, 2, 3] assert parse_toml_value("plain-text") == "plain-text" def test_apply_overrides_and_load_config(tmp_path: Path) -> None: document = default_config_document() apply_overrides( document, { "cloudflare.zone_name": "example.org", "mail.domains": ["mail.example.org"], "worker.vars.PREFIX": "custom", }, ) config_path = tmp_path / "config.toml" save_toml_document(config_path, document) config = load_config(config_path) assert config.cloudflare.zone_name == "example.org" assert config.mail.domains == ["mail.example.org"] assert config.worker.vars["PREFIX"] == "custom" assert config.derived_worker_vars()["DOMAINS"] == ["mail.example.org"] assert config.derived_worker_vars()["DISABLE_ANONYMOUS_USER_CREATE_EMAIL"] is True assert config.source.repo_ref == "" def test_default_config_marks_jwt_secret_optional() -> None: document = default_config_document() assert document["worker"]["secrets"]["JWT_SECRET"] == "" def test_default_config_includes_user_access_and_linuxdo_sections() -> None: document = default_config_document() assert document["user_access"]["require_login_to_create"] is True assert document["user_access"]["allow_user_register"] is False assert document["linuxdo"]["linuxdo_oauth"] is False assert document["linuxdo"]["client_id"] == "" assert document["linuxdo"]["client_secret"] == "" def test_load_config_requires_linuxdo_credentials_when_oauth_enabled(tmp_path: Path) -> None: document = default_config_document() document["linuxdo"]["linuxdo_oauth"] = True document["linuxdo"]["client_id"] = "" document["linuxdo"]["client_secret"] = "" config_path = tmp_path / "config.toml" save_toml_document(config_path, document) with pytest.raises(ConfigError, match="linuxdo.client_id"): load_config(config_path) def test_load_config_requires_admin_passwords(tmp_path: Path) -> None: document = default_config_document() document["worker"]["vars"]["ADMIN_PASSWORDS"] = [] config_path = tmp_path / "config.toml" save_toml_document(config_path, document) with pytest.raises(ConfigError, match="ADMIN_PASSWORDS"): load_config(config_path) def test_load_config_requires_worker_domain_or_workers_dev(tmp_path: Path) -> None: document = default_config_document() document["worker"]["use_workers_dev"] = False document["worker"]["custom_domain"] = "" config_path = tmp_path / "config.toml" save_toml_document(config_path, document) with pytest.raises(ConfigError, match="worker.custom_domain"): load_config(config_path) def test_load_config_merges_selected_profile(tmp_path: Path) -> None: document = default_config_document() document["cloudflare"]["zone_name"] = "base.example.com" document["mail"]["domains"] = ["base.example.com"] document["pages"]["custom_domain"] = "app.base.example.com" document["profiles"] = { "account_b": { "cloudflare": { "account_id": "acc-b", "zone_name": "kotei.asia", "api_token": "token-b", }, "mail": {"domains": ["mail.kotei.asia", "maila.kotei.asia"]}, "pages": {"custom_domain": "email.kotei.asia"}, } } config_path = tmp_path / "config.toml" save_toml_document(config_path, document) config = load_config(config_path, profile="account_b") assert config.cloudflare.account_id == "acc-b" assert config.cloudflare.zone_name == "kotei.asia" assert config.mail.domains == ["mail.kotei.asia", "maila.kotei.asia"] assert config.pages.custom_domain == "email.kotei.asia" def test_load_config_rejects_missing_profile(tmp_path: Path) -> None: config_path = tmp_path / "config.toml" save_toml_document(config_path, default_config_document()) with pytest.raises(ConfigError, match="未找到 profile"): load_config(config_path, profile="missing") def test_set_dotted_value_rejects_scalar_parent() -> None: document = default_config_document() set_dotted_value(document, "cloudflare.account_id", "abc") try: set_dotted_value(document, "cloudflare.account_id.value", "x") except Exception as exc: # pragma: no cover - narrow assertion below assert "父节点不是表" in str(exc) else: # pragma: no cover raise AssertionError("expected parent table validation failure") def test_save_state_roundtrip(tmp_path: Path) -> None: state_path = tmp_path / ".deploy" / "state.toml" state = DeploymentState() state.mark_checkpoint("environment_checked") state.worker.script_name = "worker-a" state.worker.workers_dev_url = "https://worker-a.example.workers.dev" save_state(state_path, state) loaded_text = state_path.read_text(encoding="utf-8") assert "environment_checked" in loaded_text assert "worker-a" in loaded_text