Initial import of cf-temp-email deploy CLI

This commit is contained in:
mmc
2026-03-26 08:06:02 +08:00
commit 4100e9cf72
29 changed files with 6703 additions and 0 deletions

275
tests/test_cli.py Normal file
View File

@@ -0,0 +1,275 @@
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