| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- # coding=utf-8
- """
- DNSPod Provider 单元测试
- 支持 Python 2.7 和 Python 3
- """
- from base_test import BaseProviderTestCase, unittest, patch, MagicMock
- from ddns.provider.dnspod import DnspodProvider
- class TestDnspodProvider(BaseProviderTestCase):
- """DNSPod Provider 测试类"""
- def setUp(self):
- """测试初始化"""
- super(TestDnspodProvider, self).setUp()
- self.provider = DnspodProvider(self.id, self.token)
- def test_init_with_basic_config(self):
- """Test DnspodProvider initialization with basic configuration"""
- provider = DnspodProvider(self.id, self.token)
- self.assertEqual(provider.id, self.id)
- self.assertEqual(provider.token, self.token)
- self.assertEqual(provider.endpoint, "https://dnsapi.cn")
- self.assertEqual(provider.DefaultLine, "默认")
- def test_class_constants(self):
- """Test DnspodProvider class constants"""
- self.assertEqual(DnspodProvider.endpoint, "https://dnsapi.cn")
- self.assertEqual(DnspodProvider.DefaultLine, "默认")
- # ContentType should be TYPE_FORM
- from ddns.provider._base import TYPE_FORM
- self.assertEqual(DnspodProvider.content_type, TYPE_FORM)
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_request_success(self, mock_http):
- """Test _request method with successful response"""
- mock_response = {"status": {"code": "1", "message": "Success"}, "data": {"test": "value"}}
- mock_http.return_value = mock_response
- result = self.provider._request("Test.Action", test_param="test_value")
- self.assertEqual(result, mock_response)
- mock_http.assert_called_once()
- # Verify request parameters
- call_args = mock_http.call_args
- self.assertEqual(call_args[0][0], "POST") # Method
- self.assertEqual(call_args[0][1], "/Test.Action") # URL
- # Verify body contains login token and format
- body = call_args[1]["body"]
- self.assertIn("login_token", body)
- expected_token = "{0},{1}".format(self.id, self.token)
- self.assertEqual(body["login_token"], expected_token)
- self.assertEqual(body["format"], "json")
- self.assertEqual(body["test_param"], "test_value")
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_request_failure(self, mock_http):
- """Test _request method with failed response"""
- mock_response = {"status": {"code": "0", "message": "API Error"}}
- mock_http.return_value = mock_response
- # Mock logger to capture warning
- self.provider.logger = MagicMock()
- result = self.provider._request("Test.Action")
- self.assertEqual(result, mock_response)
- self.provider.logger.warning.assert_called_once()
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_request_filters_none_params(self, mock_http):
- """Test _request method filters out None parameters"""
- mock_response = {"status": {"code": "1"}}
- mock_http.return_value = mock_response
- self.provider._request("Test.Action", param1="value1", param2=None, param3="value3")
- body = mock_http.call_args[1]["body"]
- self.assertEqual(body["param1"], "value1")
- self.assertEqual(body["param3"], "value3")
- self.assertNotIn("param2", body)
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_request_with_extra_params(self, mock_http):
- """Test _request method with extra parameters"""
- mock_response = {"status": {"code": "1"}}
- mock_http.return_value = mock_response
- extra = {"extra_param": "extra_value"}
- self.provider._request("Test.Action", extra=extra, normal_param="normal_value")
- # Verify both extra and normal params are included
- body = mock_http.call_args[1]["body"]
- self.assertEqual(body["extra_param"], "extra_value")
- self.assertEqual(body["normal_param"], "normal_value")
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_query_zone_id_success(self, mock_http):
- """Test _query_zone_id method with successful response"""
- mock_http.return_value = {"domain": {"id": "12345", "name": "example.com"}}
- zone_id = self.provider._query_zone_id("example.com")
- self.assertEqual(zone_id, "12345")
- mock_http.assert_called_once()
- # Verify the action was correct
- call_args = mock_http.call_args
- self.assertEqual(call_args[0][1], "/Domain.Info")
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_query_zone_id_not_found(self, mock_http):
- """Test _query_zone_id method when domain is not found"""
- mock_http.return_value = {}
- zone_id = self.provider._query_zone_id("notfound.com")
- self.assertIsNone(zone_id)
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_query_record_success_single(self, mock_request):
- """Test _query_record method with single record found"""
- mock_request.return_value = {"records": [{"id": "123", "name": "www", "value": "192.168.1.1", "type": "A"}]}
- record = self.provider._query_record("zone123", "www", "example.com", "A", None, {})
- self.assertIsNotNone(record)
- if record:
- self.assertEqual(record["id"], "123")
- self.assertEqual(record["name"], "www")
- mock_request.assert_called_once_with(
- "Record.List", domain_id="zone123", sub_domain="www", record_type="A", line=None
- )
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_query_record_success_multiple(self, mock_request):
- """Test _query_record method with multiple records found"""
- mock_request.return_value = {
- "records": [
- {"id": "123", "name": "www", "value": "192.168.1.1", "type": "A"},
- {"id": "124", "name": "ftp", "value": "192.168.1.2", "type": "A"},
- ]
- }
- # Mock logger
- self.provider.logger = MagicMock()
- record = self.provider._query_record("zone123", "www", "example.com", "A", None, {})
- self.assertIsNotNone(record)
- self.assertEqual(record["name"], "www") # type: ignore[unreachable]
- # Should log warning for multiple records
- self.provider.logger.warning.assert_called_once()
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_query_record_not_found(self, mock_request):
- """Test _query_record method when no records found"""
- mock_request.return_value = {"records": []}
- # Mock logger
- self.provider.logger = MagicMock()
- record = self.provider._query_record("zone123", "notfound", "example.com", "A", None, {})
- self.assertIsNone(record)
- self.provider.logger.warning.assert_called_once()
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_create_record_success(self, mock_request):
- """Test _create_record method with successful creation"""
- mock_request.return_value = {"record": {"id": "12345", "name": "www", "value": "192.168.1.1"}}
- # Mock logger
- self.provider.logger = MagicMock()
- result = self.provider._create_record(
- "zone123", "www", "example.com", "192.168.1.1", "A", ttl=600, line="电信", extra={}
- )
- self.assertTrue(result)
- self.provider.logger.info.assert_called_once()
- mock_request.assert_called_once_with(
- "Record.Create",
- extra={},
- domain_id="zone123",
- sub_domain="www",
- value="192.168.1.1",
- record_type="A",
- record_line="电信",
- ttl=600,
- )
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_create_record_with_default_line(self, mock_request):
- """Test _create_record method with default line"""
- mock_request.return_value = {"record": {"id": "12345", "name": "www", "value": "192.168.1.1"}}
- result = self.provider._create_record("zone123", "www", "example.com", "192.168.1.1", "A", None, None, {})
- self.assertTrue(result)
- # Should use DefaultLine when line is not specified
- call_args = mock_request.call_args[1]
- self.assertEqual(call_args["record_line"], "默认")
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_create_record_failure(self, mock_request):
- """Test _create_record method with failed creation"""
- mock_request.return_value = None
- # Mock logger
- self.provider.logger = MagicMock()
- result = self.provider._create_record("zone123", "www", "example.com", "192.168.1.1", "A", None, None, {})
- self.assertFalse(result)
- self.provider.logger.error.assert_called_once()
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_create_record_with_extra_params(self, mock_request):
- """Test _create_record method with extra parameters"""
- mock_request.return_value = {"record": {"id": "12345", "name": "www", "value": "192.168.1.1"}}
- extra = {"weight": 10}
- result = self.provider._create_record("zone123", "www", "example.com", "192.168.1.1", "A", None, None, extra)
- self.assertTrue(result)
- mock_request.assert_called_once_with(
- "Record.Create",
- extra=extra,
- domain_id="zone123",
- sub_domain="www",
- value="192.168.1.1",
- record_type="A",
- record_line="默认",
- ttl=None,
- )
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_update_record_success(self, mock_request):
- """Test _update_record method with successful update"""
- mock_request.return_value = {"record": {"id": "12345", "name": "www", "value": "192.168.1.2"}}
- old_record = {"id": "12345", "name": "www", "line": "电信"}
- # Mock logger
- self.provider.logger = MagicMock()
- result = self.provider._update_record("zone123", old_record, "192.168.1.2", "A", 300, None, {})
- self.assertTrue(result)
- self.provider.logger.debug.assert_called_once()
- mock_request.assert_called_once_with(
- "Record.Modify",
- domain_id="zone123",
- record_id="12345",
- sub_domain="www",
- record_type="A",
- value="192.168.1.2",
- record_line="电信",
- extra={},
- )
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_update_record_failure(self, mock_request):
- """Test _update_record method with failed update"""
- mock_request.return_value = None
- old_record = {"id": "12345", "name": "www"}
- # Mock logger
- self.provider.logger = MagicMock()
- result = self.provider._update_record("zone123", old_record, "192.168.1.2", "A", None, None, {})
- self.assertFalse(result)
- self.provider.logger.error.assert_called_once()
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_update_record_with_line_conversion(self, mock_request):
- """Test _update_record method with line conversion (Default -> default)"""
- mock_request.return_value = {"record": {"id": "12345", "name": "www", "value": "192.168.1.2"}}
- old_record = {"id": "12345", "name": "www", "line": "Default"}
- result = self.provider._update_record("zone123", old_record, "192.168.1.2", "A", None, None, {})
- self.assertTrue(result)
- # Should convert "Default" to "default"
- call_args = mock_request.call_args[1]
- self.assertEqual(call_args["record_line"], "default")
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_update_record_with_fallback_line(self, mock_request):
- """Test _update_record method with fallback to default line"""
- mock_request.return_value = {"record": {"id": "12345", "name": "www", "value": "192.168.1.2"}}
- old_record = {"id": "12345", "name": "www"} # No line specified
- result = self.provider._update_record("zone123", old_record, "192.168.1.2", "A", None, None, {})
- self.assertTrue(result)
- # Should use DefaultLine when old record has no line
- call_args = mock_request.call_args[1]
- self.assertEqual(call_args["record_line"], "默认")
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_update_record_with_extra_params(self, mock_request):
- """Test _update_record method with extra parameters"""
- mock_request.return_value = {"record": {"id": "12345", "name": "www", "value": "192.168.1.2"}}
- old_record = {"id": "12345", "name": "www", "line": "电信"}
- extra = {"weight": 20}
- result = self.provider._update_record("zone123", old_record, "192.168.1.2", "A", None, None, extra)
- self.assertTrue(result)
- call_args = mock_request.call_args[1]
- self.assertEqual(call_args["extra"], extra)
- @patch("ddns.provider.dnspod.DnspodProvider._request")
- def test_update_record_extra_priority_over_old_record(self, mock_request):
- """Test that extra parameters take priority over old_record values"""
- mock_request.return_value = {"record": {"id": "12345", "name": "www", "value": "192.168.1.2"}}
- old_record = {"id": "12345", "name": "www", "line": "电信", "weight": 10}
- # extra should override old_record's weight
- extra = {"weight": 20, "mx": 5}
- result = self.provider._update_record("zone123", old_record, "192.168.1.2", "A", None, None, extra)
- self.assertTrue(result)
- call_args = mock_request.call_args[1]
- self.assertEqual(call_args["extra"], extra)
- # Verify extra contains the new weight value
- self.assertEqual(call_args["extra"]["weight"], 20)
- self.assertEqual(call_args["extra"]["mx"], 5)
- def test_request_with_none_response(self):
- """Test _request method when HTTP returns None"""
- with patch("ddns.provider.dnspod.DnspodProvider._http") as mock_http:
- mock_http.return_value = None
- self.provider.logger = MagicMock()
- # Should return None and log a warning
- result = self.provider._request("Test.Action")
- self.assertIsNone(result)
- # Verify warning was logged
- self.provider.logger.warning.assert_called_once()
- def test_create_record_with_no_record_in_response(self):
- """Test _create_record method when response has no record field"""
- with patch("ddns.provider.dnspod.DnspodProvider._request") as mock_request:
- mock_request.return_value = {"status": {"code": "1"}} # No record field
- self.provider.logger = MagicMock()
- result = self.provider._create_record("zone123", "www", "example.com", "192.168.1.1", "A", None, None, {})
- self.assertFalse(result)
- self.provider.logger.error.assert_called_once()
- def test_update_record_with_no_record_in_response(self):
- """Test _update_record method when response has no record field"""
- with patch("ddns.provider.dnspod.DnspodProvider._request") as mock_request:
- mock_request.return_value = {"status": {"code": "1"}} # No record field
- old_record = {"id": "12345", "name": "www"}
- self.provider.logger = MagicMock()
- result = self.provider._update_record("zone123", old_record, "192.168.1.2", "A", None, None, {})
- self.assertFalse(result)
- self.provider.logger.error.assert_called_once()
- def test_line_configuration_support(self):
- """Test that DnspodProvider supports line configuration"""
- with patch("ddns.provider.dnspod.DnspodProvider._request") as mock_request:
- mock_request.return_value = {"record": {"id": "12345", "name": "www", "value": "192.168.1.1"}}
- # Test create record with line parameter
- result = self.provider._create_record("zone123", "www", "example.com", "192.168.1.1", "A", 600, "电信", {})
- self.assertTrue(result)
- mock_request.assert_called_once_with(
- "Record.Create",
- extra={},
- domain_id="zone123",
- sub_domain="www",
- value="192.168.1.1",
- record_type="A",
- record_line="电信",
- ttl=600,
- )
- def test_update_record_with_custom_line(self):
- """Test _update_record method with custom line parameter"""
- with patch("ddns.provider.dnspod.DnspodProvider._request") as mock_request:
- mock_request.return_value = {"record": {"id": "12345", "name": "www", "value": "192.168.1.2"}}
- old_record = {"id": "12345", "name": "www", "line": "默认"}
- # Test with custom line parameter
- result = self.provider._update_record("zone123", old_record, "192.168.1.2", "A", 300, "联通", {})
- self.assertTrue(result)
- call_args = mock_request.call_args[1]
- self.assertEqual(call_args["record_line"], "联通")
- class TestDnspodProviderIntegration(BaseProviderTestCase):
- """DNSPod Provider 集成测试类"""
- def setUp(self):
- """测试初始化"""
- super(TestDnspodProviderIntegration, self).setUp()
- self.provider = DnspodProvider(self.id, self.token)
- self.provider.logger = MagicMock()
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_full_workflow_create_record(self, mock_http):
- """Test complete workflow for creating a new record"""
- # Mock HTTP responses for the workflow
- responses = [
- # Domain.Info response
- {"status": {"code": "1"}, "domain": {"id": "zone123"}},
- # Record.List response (no existing records)
- {"status": {"code": "1"}, "records": []},
- # Record.Create response
- {"status": {"code": "1"}, "record": {"id": "rec123", "name": "www", "value": "192.168.1.1"}},
- ]
- mock_http.side_effect = responses
- result = self.provider.set_record("www.example.com", "192.168.1.1")
- self.assertTrue(result)
- self.assertEqual(mock_http.call_count, 3)
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_full_workflow_update_record(self, mock_http):
- """Test complete workflow for updating an existing record"""
- # Mock HTTP responses for the workflow
- responses = [
- # Domain.Info response
- {"status": {"code": "1"}, "domain": {"id": "zone123"}},
- # Record.List response (existing record found)
- {
- "status": {"code": "1"},
- "records": [{"id": "rec123", "name": "www", "value": "192.168.1.100", "line": "默认"}],
- },
- # Record.Modify response
- {"status": {"code": "1"}, "record": {"id": "rec123", "name": "www", "value": "192.168.1.1"}},
- ]
- mock_http.side_effect = responses
- result = self.provider.set_record("www.example.com", "192.168.1.1")
- self.assertTrue(result)
- self.assertEqual(mock_http.call_count, 3)
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_full_workflow_zone_not_found(self, mock_http):
- """Test complete workflow when zone is not found"""
- # Domain.Info response - no domain found
- mock_http.return_value = {"status": {"code": "0", "message": "Domain not found"}}
- # Should return False when zone not found
- result = self.provider.set_record("www.notfound.com", "192.168.1.1")
- self.assertFalse(result)
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_full_workflow_create_failure(self, mock_http):
- """Test complete workflow when record creation fails"""
- responses = [
- # Domain.Info response
- {"status": {"code": "1"}, "domain": {"id": "zone123"}},
- # Record.List response (no existing records)
- {"status": {"code": "1"}, "records": []},
- # Record.Create response (failure)
- {"status": {"code": "0", "message": "Create failed"}},
- ]
- mock_http.side_effect = responses
- result = self.provider.set_record("www.example.com", "192.168.1.1")
- self.assertFalse(result)
- self.assertEqual(mock_http.call_count, 3)
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_full_workflow_update_failure(self, mock_http):
- """Test complete workflow when record update fails"""
- responses = [
- # Domain.Info response
- {"status": {"code": "1"}, "domain": {"id": "zone123"}},
- # Record.List response (existing record found)
- {
- "status": {"code": "1"},
- "records": [{"id": "rec123", "name": "www", "value": "192.168.1.100", "line": "默认"}],
- },
- # Record.Modify response (failure)
- {"status": {"code": "0", "message": "Update failed"}},
- ]
- mock_http.side_effect = responses
- result = self.provider.set_record("www.example.com", "192.168.1.1")
- self.assertFalse(result)
- self.assertEqual(mock_http.call_count, 3)
- @patch("ddns.provider.dnspod.DnspodProvider._http")
- def test_full_workflow_with_options(self, mock_http):
- """Test complete workflow with additional options like ttl and line"""
- responses = [
- # Domain.Info response
- {"status": {"code": "1"}, "domain": {"id": "zone123"}},
- # Record.List response (no existing records)
- {"status": {"code": "1"}, "records": []},
- # Record.Create response
- {"status": {"code": "1"}, "record": {"id": "rec123", "name": "www", "value": "192.168.1.1"}},
- ]
- mock_http.side_effect = responses
- result = self.provider.set_record("www.example.com", "192.168.1.1", record_type="A", ttl=300, line="电信")
- self.assertTrue(result)
- self.assertEqual(mock_http.call_count, 3)
- # Verify the Record.Create call includes the custom options
- create_call = mock_http.call_args_list[2]
- body = create_call[1]["body"]
- self.assertEqual(body["ttl"], 300)
- self.assertEqual(body["record_line"], "电信")
- class TestDnspodProviderRealRequest(BaseProviderTestCase):
- """DNSPod Provider 真实请求测试类"""
- def test_auth_failure_real_request(self):
- """Test authentication failure with real API request"""
- # 使用无效的认证信息创建 provider
- invalid_provider = DnspodProvider("invalid_id", "invalid_token")
- # 尝试查询域名信息,应该抛出认证失败异常
- with self.assertRaises(RuntimeError) as cm:
- invalid_provider._query_zone_id("example.com")
- # 验证异常信息包含认证失败 - 更新错误消息格式
- error_message = str(cm.exception)
- self.assertTrue(
- "认证失败" in error_message and "401" in error_message,
- "Expected authentication error message not found in: {}".format(error_message),
- )
- if __name__ == "__main__":
- unittest.main()
|