test_provider_edgeone_dns.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. # coding=utf-8
  2. """
  3. Unit tests for EdgeOneDnsProvider
  4. 腾讯云 EdgeOne DNS 提供商单元测试 - 非加速域名管理
  5. @author: NewFuture
  6. """
  7. from base_test import BaseProviderTestCase, unittest, patch, MagicMock
  8. from ddns.provider.edgeone_dns import EdgeOneDnsProvider
  9. class TestEdgeOneDnsProvider(BaseProviderTestCase):
  10. """Test EdgeOneDnsProvider functionality"""
  11. def setUp(self):
  12. """Set up test fixtures"""
  13. super(TestEdgeOneDnsProvider, self).setUp()
  14. self.provider = EdgeOneDnsProvider(self.id, self.token)
  15. self.logger = self.mock_logger(self.provider)
  16. def test_init(self):
  17. """Test provider initialization"""
  18. self.assertProviderInitialized(self.provider)
  19. self.assertEqual(self.provider.service, "teo")
  20. self.assertEqual(self.provider.version_date, "2022-09-01")
  21. self.assertEqual(self.provider.endpoint, "https://teo.tencentcloudapi.com")
  22. self.assertEqual(self.provider.content_type, "application/json")
  23. def test_validate_success(self):
  24. """Test successful validation"""
  25. # Should not raise any exception
  26. self.provider._validate()
  27. def test_validate_missing_id(self):
  28. """Test validation with missing id"""
  29. with self.assertRaises(ValueError) as context:
  30. EdgeOneDnsProvider("", self.token, self.logger)
  31. self.assertIn("id", str(context.exception))
  32. def test_validate_missing_token(self):
  33. """Test validation with missing token"""
  34. with self.assertRaises(ValueError) as context:
  35. EdgeOneDnsProvider(self.id, "", self.logger)
  36. self.assertIn("token", str(context.exception))
  37. @patch.object(EdgeOneDnsProvider, "_request")
  38. def test_query_zone_id_success(self, mock_request):
  39. """Test successful zone ID query (inherited from EdgeOneProvider)"""
  40. domain = "example.com"
  41. expected_zone_id = "zone-123456789"
  42. mock_request.return_value = {
  43. "Zones": [{"ZoneId": expected_zone_id, "ZoneName": domain, "ActiveStatus": "active", "Status": "active"}]
  44. }
  45. zone_id = self.provider._query_zone_id(domain)
  46. self.assertEqual(zone_id, expected_zone_id)
  47. @patch.object(EdgeOneDnsProvider, "_request")
  48. def test_query_zone_id_not_found(self, mock_request):
  49. """Test zone ID query when domain not found"""
  50. mock_request.return_value = {"Zones": []}
  51. zone_id = self.provider._query_zone_id("nonexistent.com")
  52. self.assertIsNone(zone_id)
  53. @patch.object(EdgeOneDnsProvider, "_request")
  54. def test_query_record_found(self, mock_request):
  55. """Test successful DNS record query"""
  56. mock_request.return_value = {
  57. "DnsRecords": [
  58. {
  59. "RecordId": "record-123456789",
  60. "Name": "www.example.com",
  61. "Type": "A",
  62. "Content": "1.2.3.4",
  63. "Status": "active",
  64. "TTL": 600,
  65. }
  66. ]
  67. }
  68. record = self.provider._query_record("zone-123456789", "www", "example.com", "A", None, {})
  69. self.assertIsNotNone(record)
  70. if record: # Type narrowing for mypy
  71. self.assertEqual(record["RecordId"], "record-123456789")
  72. self.assertEqual(record["Name"], "www.example.com")
  73. self.assertEqual(record["Type"], "A")
  74. self.assertEqual(record["Content"], "1.2.3.4")
  75. # Verify request call was made correctly
  76. mock_request.assert_called_once()
  77. @patch.object(EdgeOneDnsProvider, "_request")
  78. def test_query_record_not_found(self, mock_request):
  79. """Test DNS record query when record not found"""
  80. mock_request.return_value = {"DnsRecords": []}
  81. record = self.provider._query_record("zone-123456789", "www", "example.com", "A", None, {})
  82. self.assertIsNone(record)
  83. @patch.object(EdgeOneDnsProvider, "_request")
  84. def test_query_record_wrong_type(self, mock_request):
  85. """Test DNS record query with mismatched record type"""
  86. mock_request.return_value = {
  87. "DnsRecords": [
  88. {
  89. "RecordId": "record-123456789",
  90. "Name": "www.example.com",
  91. "Type": "AAAA",
  92. "Content": "::1",
  93. "Status": "active",
  94. }
  95. ]
  96. }
  97. # Query for A record, but only AAAA exists
  98. record = self.provider._query_record("zone-123456789", "www", "example.com", "A", None, {})
  99. self.assertIsNone(record)
  100. @patch.object(EdgeOneDnsProvider, "_request")
  101. def test_query_record_root_domain(self, mock_request):
  102. """Test DNS record query for root domain (@)"""
  103. mock_request.return_value = {
  104. "DnsRecords": [
  105. {
  106. "RecordId": "record-123456789",
  107. "Name": "example.com",
  108. "Type": "A",
  109. "Content": "1.2.3.4",
  110. "Status": "active",
  111. }
  112. ]
  113. }
  114. record = self.provider._query_record("zone-123456789", "@", "example.com", "A", None, {})
  115. self.assertIsNotNone(record)
  116. self.assertEqual(record["Name"], "example.com")
  117. @patch.object(EdgeOneDnsProvider, "_request")
  118. def test_create_record_success(self, mock_request):
  119. """Test successful DNS record creation"""
  120. mock_request.return_value = {"RequestId": "req-123456789"}
  121. result = self.provider._create_record("zone-123456789", "www", "example.com", "1.2.3.4", "A", 600, None, {})
  122. self.assertTrue(result)
  123. # Verify request call was made correctly
  124. mock_request.assert_called_once()
  125. args, kwargs = mock_request.call_args
  126. self.assertEqual(args[0], "CreateDnsRecord")
  127. self.assertEqual(kwargs["ZoneId"], "zone-123456789")
  128. self.assertEqual(kwargs["Name"], "www.example.com")
  129. self.assertEqual(kwargs["Type"], "A")
  130. self.assertEqual(kwargs["Content"], "1.2.3.4")
  131. @patch.object(EdgeOneDnsProvider, "_request")
  132. def test_create_record_root_domain(self, mock_request):
  133. """Test DNS record creation for root domain"""
  134. mock_request.return_value = {"RequestId": "req-123456789"}
  135. result = self.provider._create_record("zone-123456789", "@", "example.com", "1.2.3.4", "A", 300, None, {})
  136. self.assertTrue(result)
  137. # Verify domain name is correct for root
  138. args, kwargs = mock_request.call_args
  139. self.assertEqual(kwargs["Name"], "example.com")
  140. @patch.object(EdgeOneDnsProvider, "_request")
  141. def test_create_record_aaaa(self, mock_request):
  142. """Test DNS record creation with AAAA record"""
  143. mock_request.return_value = {"RequestId": "req-123456789"}
  144. result = self.provider._create_record(
  145. "zone-123456789", "ipv6", "example.com", "2001:db8::1", "AAAA", 300, None, {}
  146. )
  147. self.assertTrue(result)
  148. # Verify parameters
  149. args, kwargs = mock_request.call_args
  150. self.assertEqual(kwargs["Type"], "AAAA")
  151. self.assertEqual(kwargs["Content"], "2001:db8::1")
  152. @patch.object(EdgeOneDnsProvider, "_request")
  153. def test_create_record_failure(self, mock_request):
  154. """Test DNS record creation failure"""
  155. mock_request.return_value = None
  156. result = self.provider._create_record("zone-123456789", "www", "example.com", "1.2.3.4", "A", 300, None, {})
  157. self.assertFalse(result)
  158. @patch.object(EdgeOneDnsProvider, "_request")
  159. def test_update_record_success(self, mock_request):
  160. """Test successful DNS record update"""
  161. mock_request.return_value = {"RequestId": "req-123456789"}
  162. old_record = {
  163. "RecordId": "record-123456789",
  164. "Name": "www.example.com",
  165. "Type": "A",
  166. "Content": "1.2.3.4",
  167. "Status": "active",
  168. }
  169. result = self.provider._update_record("zone-123456789", old_record, "5.6.7.8", "A", 600, None, {})
  170. self.assertTrue(result)
  171. # Verify request call was made
  172. mock_request.assert_called_once()
  173. args, kwargs = mock_request.call_args
  174. self.assertEqual(args[0], "ModifyDnsRecords")
  175. self.assertEqual(kwargs["ZoneId"], "zone-123456789")
  176. # Verify the DnsRecords parameter contains the updated record
  177. self.assertIn("DnsRecords", kwargs)
  178. self.assertEqual(len(kwargs["DnsRecords"]), 1)
  179. updated_record = kwargs["DnsRecords"][0]
  180. self.assertEqual(updated_record["RecordId"], "record-123456789")
  181. self.assertEqual(updated_record["Content"], "5.6.7.8")
  182. @patch.object(EdgeOneDnsProvider, "_request")
  183. def test_update_record_change_type(self, mock_request):
  184. """Test DNS record update with record type change"""
  185. mock_request.return_value = {"RequestId": "req-123456789"}
  186. old_record = {"RecordId": "record-123456789", "Name": "www.example.com", "Type": "A", "Content": "1.2.3.4"}
  187. result = self.provider._update_record("zone-123456789", old_record, "2001:db8::1", "AAAA", None, None, {})
  188. self.assertTrue(result)
  189. # Verify type is changed
  190. args, kwargs = mock_request.call_args
  191. updated_record = kwargs["DnsRecords"][0]
  192. self.assertEqual(updated_record["Type"], "AAAA")
  193. self.assertEqual(updated_record["Content"], "2001:db8::1")
  194. @patch.object(EdgeOneDnsProvider, "_request")
  195. def test_update_record_failure(self, mock_request):
  196. """Test DNS record update failure"""
  197. mock_request.return_value = None # API call failed
  198. old_record = {"RecordId": "record-123456789", "Name": "www.example.com", "Type": "A", "Content": "1.2.3.4"}
  199. result = self.provider._update_record("zone-123456789", old_record, "5.6.7.8", "A", None, None, {})
  200. self.assertFalse(result)
  201. @patch.object(EdgeOneDnsProvider, "_request")
  202. def test_set_record_create_new(self, mock_request):
  203. """Test set_record creating a new DNS record"""
  204. # Mock HTTP responses for the workflow
  205. responses = [
  206. # DescribeZones response (get zone ID for main domain)
  207. {"Zones": [{"ZoneId": "zone-123456789", "ZoneName": "example.com"}]},
  208. # DescribeDnsRecords response (no existing DNS record for subdomain)
  209. {"DnsRecords": []},
  210. # CreateDnsRecord response (DNS record created successfully)
  211. {"RequestId": "req-123456789"},
  212. ]
  213. mock_request.side_effect = responses
  214. result = self.provider.set_record("www.example.com", "1.2.3.4", "A")
  215. self.assertTrue(result)
  216. self.assertEqual(mock_request.call_count, 3) # Zone lookup, record query, and create calls
  217. @patch.object(EdgeOneDnsProvider, "_request")
  218. def test_set_record_update_existing(self, mock_request):
  219. """Test set_record updating an existing DNS record"""
  220. # Mock HTTP responses for the workflow
  221. responses = [
  222. # DescribeZones response (get zone ID for main domain)
  223. {"Zones": [{"ZoneId": "zone-123456789", "ZoneName": "example.com"}]},
  224. # DescribeDnsRecords response (existing DNS record found)
  225. {
  226. "DnsRecords": [
  227. {
  228. "RecordId": "record-123456789",
  229. "Name": "www.example.com",
  230. "Type": "A",
  231. "Content": "1.2.3.4",
  232. "Status": "active",
  233. }
  234. ]
  235. },
  236. # ModifyDnsRecords response (DNS record updated successfully)
  237. {"RequestId": "req-123456789"},
  238. ]
  239. mock_request.side_effect = responses
  240. result = self.provider.set_record("www.example.com", "5.6.7.8", "A")
  241. self.assertTrue(result)
  242. self.assertEqual(mock_request.call_count, 3)
  243. class TestEdgeOneDnsProviderIntegration(BaseProviderTestCase):
  244. """Integration tests for EdgeOneDnsProvider"""
  245. def setUp(self):
  246. """Set up test fixtures"""
  247. super(TestEdgeOneDnsProviderIntegration, self).setUp()
  248. self.provider = EdgeOneDnsProvider(self.id, self.token)
  249. self.logger = self.mock_logger(self.provider)
  250. @patch.object(EdgeOneDnsProvider, "_request")
  251. def test_full_domain_resolution_flow(self, mock_request):
  252. """Test complete domain resolution flow for creating new DNS records"""
  253. # Mock request responses for the workflow
  254. responses = [
  255. # DescribeZones response (get zone ID for main domain)
  256. {"Zones": [{"ZoneId": "zone-123456789", "ZoneName": "example.com"}]},
  257. # DescribeDnsRecords response (no existing DNS record for subdomain)
  258. {"DnsRecords": []},
  259. # CreateDnsRecord response (DNS record created successfully)
  260. {"RequestId": "req-123456789"},
  261. ]
  262. mock_request.side_effect = responses
  263. result = self.provider.set_record("test.example.com", "1.2.3.4", "A", ttl=600)
  264. self.assertTrue(result)
  265. self.assertEqual(mock_request.call_count, 3) # Zone lookup, record query, and create calls
  266. @patch.object(EdgeOneDnsProvider, "_request")
  267. def test_custom_domain_format(self, mock_request):
  268. """Test custom domain format with ~ separator (create new DNS record)"""
  269. # Mock request responses
  270. responses = [
  271. # DescribeZones response (get zone ID for main domain)
  272. {"Zones": [{"ZoneId": "zone-123456789", "ZoneName": "example.com"}]},
  273. # DescribeDnsRecords response (no existing DNS record for subdomain)
  274. {"DnsRecords": []},
  275. # CreateDnsRecord response (DNS record created successfully)
  276. {"RequestId": "req-123456789"},
  277. ]
  278. mock_request.side_effect = responses
  279. result = self.provider.set_record("test~example.com", "1.2.3.4", "A")
  280. self.assertTrue(result)
  281. # Zone lookup, record query, and create calls should be made
  282. self.assertEqual(mock_request.call_count, 3)
  283. @patch.object(EdgeOneDnsProvider, "_request")
  284. def test_update_existing_record(self, mock_request):
  285. """Test updating an existing DNS record"""
  286. # Mock request responses for the workflow
  287. responses = [
  288. # DescribeZones response (get zone ID for main domain)
  289. {"Zones": [{"ZoneId": "zone-123456789", "ZoneName": "example.com"}]},
  290. # DescribeDnsRecords response (existing DNS record found)
  291. {
  292. "DnsRecords": [
  293. {
  294. "RecordId": "record-123456789",
  295. "Name": "test.example.com",
  296. "Type": "A",
  297. "Content": "1.2.3.4",
  298. "Status": "active",
  299. }
  300. ]
  301. },
  302. # ModifyDnsRecords response (DNS record updated successfully)
  303. {"RequestId": "req-123456789"},
  304. ]
  305. mock_request.side_effect = responses
  306. result = self.provider.set_record("test.example.com", "5.6.7.8", "A", ttl=300)
  307. self.assertTrue(result)
  308. self.assertEqual(mock_request.call_count, 3)
  309. # Verify the ModifyDnsRecords call
  310. modify_call = mock_request.call_args_list[2]
  311. self.assertEqual(modify_call[0][0], "ModifyDnsRecords")
  312. @patch.object(EdgeOneDnsProvider, "_request")
  313. def test_ipv6_record(self, mock_request):
  314. """Test creating IPv6 DNS record"""
  315. # Mock request responses
  316. responses = [
  317. # DescribeZones response
  318. {"Zones": [{"ZoneId": "zone-123456789", "ZoneName": "example.com"}]},
  319. # DescribeDnsRecords response (no existing record)
  320. {"DnsRecords": []},
  321. # CreateDnsRecord response
  322. {"RequestId": "req-123456789"},
  323. ]
  324. mock_request.side_effect = responses
  325. result = self.provider.set_record("ipv6.example.com", "2001:db8::1", "AAAA")
  326. self.assertTrue(result)
  327. # Verify the CreateDnsRecord call includes correct parameters
  328. create_call = mock_request.call_args_list[2]
  329. self.assertEqual(create_call[1]["Type"], "AAAA")
  330. self.assertEqual(create_call[1]["Content"], "2001:db8::1")
  331. @patch.object(EdgeOneDnsProvider, "_request")
  332. def test_api_error_handling(self, mock_request):
  333. """Test API error handling"""
  334. # Mock API error response - the _request method returns None on error
  335. mock_request.return_value = None
  336. # This should return False because zone_id cannot be resolved
  337. result = self.provider.set_record("test.example.com", "1.2.3.4", "A")
  338. self.assertFalse(result)
  339. # At least one call should be made to try to resolve zone ID
  340. self.assertGreater(mock_request.call_count, 0)
  341. class TestEdgeOneDnsProviderRealRequest(BaseProviderTestCase):
  342. """EdgeOne DNS Provider 真实请求测试类"""
  343. def setUp(self):
  344. """Set up test fixtures"""
  345. super(TestEdgeOneDnsProviderRealRequest, self).setUp()
  346. def test_auth_failure_real_request(self):
  347. """Test authentication failure with real API request"""
  348. # 使用无效的认证信息创建 provider
  349. invalid_provider = EdgeOneDnsProvider("invalid_id", "invalid_token")
  350. # Mock logger to capture error logs
  351. invalid_provider.logger = MagicMock()
  352. # 尝试查询域名信息,应该返回认证失败
  353. result = invalid_provider._query_zone_id("example.com")
  354. # 认证失败时应该返回 None (因为 API 会返回错误)
  355. self.assertIsNone(result)
  356. # 验证错误日志被记录
  357. # 应该有错误日志调用,因为 API 返回认证错误
  358. self.assertGreaterEqual(invalid_provider.logger.error.call_count, 1)
  359. # 检查日志内容包含认证相关的错误信息
  360. error_calls = invalid_provider.logger.error.call_args_list
  361. logged_messages = [str(call) for call in error_calls]
  362. # 至少有一个日志应该包含EdgeOne API 错误信息
  363. has_auth_error = any(
  364. "edgeone api error" in msg.lower() or "authfailure" in msg.lower() or "unauthorized" in msg.lower()
  365. for msg in logged_messages
  366. )
  367. self.assertTrue(
  368. has_auth_error,
  369. "EdgeOne authentication error not found in logs. Logged messages: {0}".format(logged_messages),
  370. )
  371. if __name__ == "__main__":
  372. unittest.main()