test_cleanup_stale_cf_resources_script.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. from __future__ import annotations
  2. import json
  3. from scripts import cleanup_stale_cf_resources
  4. def test_script_cleanup_removes_stale_rules_and_dns(tmp_path, monkeypatch) -> None:
  5. env_path = tmp_path / ".env"
  6. env_path.write_text(
  7. "\n".join(
  8. [
  9. 'export ZHUCE6_CFMAIL_CF_AUTH_EMAIL="cf@example.com"',
  10. 'export ZHUCE6_CFMAIL_CF_AUTH_KEY="global-key"',
  11. 'export ZHUCE6_CFMAIL_CF_ZONE_ID="zone-1"',
  12. 'export ZHUCE6_CFMAIL_ZONE_NAME="example.test"',
  13. ]
  14. )
  15. + "\n",
  16. encoding="utf-8",
  17. )
  18. config_path = tmp_path / "cfmail_accounts.json"
  19. config_path.write_text(
  20. json.dumps(
  21. {
  22. "accounts": [
  23. {
  24. "name": "active",
  25. "worker_domain": "email-api.demo",
  26. "email_domain": "auto-live.example.test",
  27. "admin_password": "pw",
  28. "enabled": True,
  29. }
  30. ]
  31. },
  32. ensure_ascii=False,
  33. indent=2,
  34. )
  35. + "\n",
  36. encoding="utf-8",
  37. )
  38. deleted: list[tuple[str, str]] = []
  39. class FakeResponse:
  40. def __init__(self, status_code: int, payload: dict[str, object]):
  41. self.status_code = status_code
  42. self._payload = payload
  43. self.content = b"{}"
  44. def json(self) -> dict[str, object]:
  45. return self._payload
  46. def fake_request(method, url, **kwargs): # type: ignore[no-untyped-def]
  47. if method == "GET" and url.endswith("/email/routing/rules?page=1&per_page=100"):
  48. return FakeResponse(
  49. 200,
  50. {
  51. "success": True,
  52. "result": [
  53. {
  54. "id": "rule-old",
  55. "name": "auto-old",
  56. "matchers": [{"field": "to", "value": "*@auto-old.example.test"}],
  57. },
  58. {
  59. "id": "rule-live",
  60. "name": "auto-live",
  61. "matchers": [{"field": "to", "value": "*@auto-live.example.test"}],
  62. },
  63. {
  64. "id": "rule-nova",
  65. "name": "nova keep",
  66. "matchers": [{"field": "to", "value": "*@nova.example.test"}],
  67. },
  68. ],
  69. "result_info": {"page": 1, "total_pages": 1},
  70. },
  71. )
  72. if method == "GET" and url.endswith("/dns_records?page=1&per_page=100"):
  73. return FakeResponse(
  74. 200,
  75. {
  76. "success": True,
  77. "result": [
  78. {"id": "dns-old-mx", "type": "MX", "name": "auto-old.example.test"},
  79. {"id": "dns-old-txt", "type": "TXT", "name": "auto-old.example.test"},
  80. {"id": "dns-live", "type": "MX", "name": "auto-live.example.test"},
  81. {"id": "dns-nova", "type": "TXT", "name": "nova.example.test"},
  82. {"id": "dns-ignore", "type": "A", "name": "auto-old.example.test"},
  83. ],
  84. "result_info": {"page": 1, "total_pages": 1},
  85. },
  86. )
  87. if method == "DELETE":
  88. deleted.append((method, url))
  89. return FakeResponse(200, {"success": True, "result": {}})
  90. raise AssertionError(f"unexpected request: {method} {url}")
  91. monkeypatch.setattr(cleanup_stale_cf_resources.cffi_requests, "request", fake_request)
  92. result = cleanup_stale_cf_resources.run_cleanup(env_file=env_path, config_path=config_path)
  93. assert result["active_domain"] == "auto-live.example.test"
  94. assert result["removed_routing_rules"] == ["rule-old"]
  95. assert sorted(result["removed_dns_records"]) == ["dns-old-mx", "dns-old-txt"]
  96. assert deleted == [
  97. ("DELETE", "https://api.cloudflare.com/client/v4/zones/zone-1/email/routing/rules/rule-old"),
  98. ("DELETE", "https://api.cloudflare.com/client/v4/zones/zone-1/dns_records/dns-old-mx"),
  99. ("DELETE", "https://api.cloudflare.com/client/v4/zones/zone-1/dns_records/dns-old-txt"),
  100. ]
  101. def test_script_cleanup_explicit_env_overrides_process_env(tmp_path, monkeypatch) -> None:
  102. env_path = tmp_path / ".env"
  103. env_path.write_text(
  104. "\n".join(
  105. [
  106. 'export ZHUCE6_CFMAIL_CF_AUTH_EMAIL="cf@example.com"',
  107. 'export ZHUCE6_CFMAIL_CF_AUTH_KEY="global-key"',
  108. 'export ZHUCE6_CFMAIL_CF_ZONE_ID="zone-1"',
  109. 'export ZHUCE6_CFMAIL_ZONE_NAME="example.test"',
  110. ]
  111. )
  112. + "\n",
  113. encoding="utf-8",
  114. )
  115. config_path = tmp_path / "cfmail_accounts.json"
  116. config_path.write_text(
  117. json.dumps(
  118. {
  119. "accounts": [
  120. {
  121. "name": "active",
  122. "worker_domain": "email-api.demo",
  123. "email_domain": "auto-live.example.test",
  124. "admin_password": "pw",
  125. "enabled": True,
  126. }
  127. ]
  128. },
  129. ensure_ascii=False,
  130. indent=2,
  131. )
  132. + "\n",
  133. encoding="utf-8",
  134. )
  135. monkeypatch.setenv("ZHUCE6_CFMAIL_CF_ZONE_ID", "wrong-zone")
  136. monkeypatch.setenv("ZHUCE6_CFMAIL_ZONE_NAME", "wrong.test")
  137. class FakeResponse:
  138. def __init__(self, status_code: int, payload: dict[str, object]):
  139. self.status_code = status_code
  140. self._payload = payload
  141. self.content = b"{}"
  142. def json(self) -> dict[str, object]:
  143. return self._payload
  144. def fake_request(method, url, **kwargs): # type: ignore[no-untyped-def]
  145. if method == "GET" and url.endswith("/email/routing/rules?page=1&per_page=100"):
  146. return FakeResponse(200, {"success": True, "result": [], "result_info": {"page": 1, "total_pages": 1}})
  147. if method == "GET" and url.endswith("/dns_records?page=1&per_page=100"):
  148. assert "zones/zone-1/" in url
  149. return FakeResponse(200, {"success": True, "result": [], "result_info": {"page": 1, "total_pages": 1}})
  150. raise AssertionError(f"unexpected request: {method} {url}")
  151. monkeypatch.setattr(cleanup_stale_cf_resources.cffi_requests, "request", fake_request)
  152. result = cleanup_stale_cf_resources.run_cleanup(env_file=env_path, config_path=config_path)
  153. assert result["active_domain"] == "auto-live.example.test"