from __future__ import annotations from pathlib import Path from cf_temp_email_deploy import cli from cf_temp_email_deploy.environment import ToolVersion from cf_temp_email_deploy.wrangler import build_wrangler_command class FakeCloudflareClient: def __init__(self, config: object) -> None: self.config = config self.resolve_account_called = False def __enter__(self) -> "FakeCloudflareClient": return self def __exit__(self, exc_type: object, exc: object, tb: object) -> None: return None def verify_token(self) -> dict[str, str]: return {"status": "active"} def resolve_account(self, *, account_id: str = "", account_name: str = "") -> dict[str, str]: self.resolve_account_called = True return {"id": account_id or "acc-1", "name": account_name or "demo-account"} def resolve_zone(self, *, zone_name: str, account_id: str = "") -> dict[str, str]: payload = {"id": "zone-1", "name": zone_name} if not account_id: payload["account"] = {"id": "acc-from-zone", "name": "zone-account"} return payload def list_dns_records(self, zone_id: str, *, name: str = "", record_type: str = "") -> list[dict[str, str]]: _ = zone_id _ = name _ = record_type return [ {"type": "MX", "name": "mail.kotei.us.ci", "content": "route1.mx.cloudflare.net"}, {"type": "MX", "name": "maila.kotei.us.ci", "content": "route2.mx.cloudflare.net"}, {"type": "CNAME", "name": "email.kotei.us.ci", "content": "cf-temp-email-pages.pages.dev"}, ] def list_email_routing_addresses(self, *, account_id: str) -> list[dict[str, str]]: _ = account_id return [{"email": "verified@kotei.us.ci", "status": "verified"}] def email_address_ready(self, address: dict[str, str], target: str) -> bool: return address["email"] == target and address["status"] == "verified" def list_d1_databases(self, *, account_id: str, database_name: str = "") -> list[dict[str, str]]: _ = account_id _ = database_name return [{"name": "cf-temp-email-kotei"}] def list_pages_projects(self, *, account_id: str) -> list[dict[str, str]]: _ = account_id return [{"name": "cf-temp-email-pages", "subdomain": "cf-temp-email-pages.pages.dev"}] def get_catch_all(self, *, zone_id: str) -> dict[str, object]: _ = zone_id return {"actions": [{"type": "worker", "value": ["temp-email-api"]}]} def extract_worker_targets(self, value: object) -> set[str]: if isinstance(value, list): return {str(item) for item in value} return set() def list_worker_scripts(self, *, account_id: str) -> list[dict[str, object]]: _ = account_id return [{"id": "temp-email-api", "handlers": ["fetch", "email"]}] def worker_script_supports_email(self, script: dict[str, object]) -> bool: handlers = script.get("handlers", []) return isinstance(handlers, list) and "email" in handlers def is_authentication_error(self, error: Exception) -> bool: _ = error return False def test_init_config_and_check_command(monkeypatch, tmp_path: Path) -> None: config_path = tmp_path / "config.toml" monkeypatch.setattr( cli, "check_required_tools", lambda runner: { "git": ToolVersion(name="git", version="git version 2.43.0"), "node": ToolVersion(name="node", version="v24.14.0"), "npm": ToolVersion(name="npm", version="11.9.0"), }, ) monkeypatch.setattr(cli, "CloudflareClient", FakeCloudflareClient) assert cli.main(["init-config", "--config", str(config_path)]) == 0 assert ( cli.main( [ "check", "--config", str(config_path), "--api-token", "token-value", "--account-id", "acc-1", "--zone-name", "example.com", ] ) == 0 ) state_path = tmp_path / ".deploy" / "state.toml" assert state_path.exists() state_text = state_path.read_text(encoding="utf-8") config_text = config_path.read_text(encoding="utf-8") assert "zone-1" in state_text assert 'api_token = "token-value"' in config_text def test_build_wrangler_command_uses_npm_exec() -> None: assert build_wrangler_command("deploy", "--minify") == ( "npm", "exec", "--", "wrangler", "deploy", "--minify", ) def test_build_parser_uses_cf_temp_email_program_name() -> None: parser = cli.build_parser() assert parser.prog == "cf-temp-email" def test_resolve_cli_argv_defaults_to_deploy() -> None: assert cli.resolve_cli_argv([]) == ["deploy"] assert cli.resolve_cli_argv(["--config", "config.toml"]) == ["deploy", "--config", "config.toml"] assert cli.resolve_cli_argv(["deploy", "--config", "config.toml"]) == ["deploy", "--config", "config.toml"] def test_main_defaults_to_deploy_when_subcommand_is_omitted(monkeypatch, tmp_path: Path) -> None: config_path = tmp_path / "config.toml" captured: dict[str, object] = {} def fake_handle_deploy(args: object) -> int: captured["command"] = getattr(args, "command", "") captured["config"] = getattr(args, "config", None) return 0 monkeypatch.setattr(cli, "handle_deploy", fake_handle_deploy) assert cli.main(["--config", str(config_path)]) == 0 assert captured["command"] == "deploy" assert captured["config"] == config_path def test_check_command_can_resolve_zone_without_explicit_account(monkeypatch, tmp_path: Path) -> None: config_path = tmp_path / "config.toml" monkeypatch.setattr( cli, "check_required_tools", lambda runner: { "git": ToolVersion(name="git", version="git version 2.43.0"), "node": ToolVersion(name="node", version="v24.14.0"), "npm": ToolVersion(name="npm", version="11.9.0"), }, ) monkeypatch.setattr(cli, "CloudflareClient", FakeCloudflareClient) assert cli.main(["init-config", "--config", str(config_path)]) == 0 assert ( cli.main( [ "check", "--config", str(config_path), "--api-token", "token-value", "--zone-name", "osozos.top", ] ) == 0 ) state_path = tmp_path / ".deploy" / "state.toml" state_text = state_path.read_text(encoding="utf-8") assert "acc-from-zone" in state_text assert "osozos.top" in state_text def test_check_command_supports_profile_scoped_config_and_state(monkeypatch, tmp_path: Path) -> None: config_path = tmp_path / "config.toml" monkeypatch.setattr( cli, "check_required_tools", lambda runner: { "git": ToolVersion(name="git", version="git version 2.43.0"), "node": ToolVersion(name="node", version="v24.14.0"), "npm": ToolVersion(name="npm", version="11.9.0"), }, ) monkeypatch.setattr(cli, "CloudflareClient", FakeCloudflareClient) assert cli.main(["init-config", "--config", str(config_path)]) == 0 assert ( cli.main( [ "check", "--config", str(config_path), "--profile", "account_b", "--api-token", "token-b", "--account-id", "acc-b", "--zone-name", "kotei.asia", "--pages-domain", "email.kotei.asia", "--set", 'mail.domains=["mail.kotei.asia","maila.kotei.asia"]', ] ) == 0 ) profile_state_path = tmp_path / ".deploy" / "profiles" / "account_b" / "state.toml" assert profile_state_path.exists() config_text = config_path.read_text(encoding="utf-8") state_text = profile_state_path.read_text(encoding="utf-8") assert '[profiles.account_b.cloudflare]' in config_text assert 'api_token = "token-b"' in config_text assert 'domains = ["mail.kotei.asia", "maila.kotei.asia"]' in config_text assert "acc-b" in state_text def test_discover_config_writes_detected_cloudflare_resources(monkeypatch, tmp_path: Path) -> None: config_path = tmp_path / "config.toml" monkeypatch.setattr(cli, "CloudflareClient", FakeCloudflareClient) assert cli.main(["init-config", "--config", str(config_path)]) == 0 assert ( cli.main( [ "discover-config", "--config", str(config_path), "--profile", "kotei", "--api-token", "token-value", "--zone-name", "kotei.us.ci", ] ) == 0 ) config_text = config_path.read_text(encoding="utf-8") assert '[profiles.kotei.cloudflare]' in config_text assert 'zone_name = "kotei.us.ci"' in config_text assert 'account_id = "acc-from-zone"' in config_text assert 'verified_destination_address = "verified@kotei.us.ci"' in config_text assert 'domains = ["mail.kotei.us.ci", "maila.kotei.us.ci"]' in config_text assert 'project_name = "cf-temp-email-pages"' in config_text assert 'custom_domain = "email.kotei.us.ci"' in config_text assert 'script_name = "temp-email-api"' in config_text