276 lines
9.4 KiB
Python
276 lines
9.4 KiB
Python
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
|