| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 |
- # coding=utf-8
- """
- Unit tests for AlidnsProvider
- @author: Github Copilot
- """
- from base_test import BaseProviderTestCase, unittest, patch
- from ddns.provider.alidns import AlidnsProvider
- class TestAlidnsProvider(BaseProviderTestCase):
- """Test cases for AlidnsProvider"""
- def setUp(self):
- """Set up test fixtures"""
- super(TestAlidnsProvider, self).setUp()
- self.id = "test_access_key_id"
- self.token = "test_access_key_secret"
- def test_class_constants(self):
- """Test AlidnsProvider class constants"""
- self.assertEqual(AlidnsProvider.endpoint, "https://alidns.aliyuncs.com")
- self.assertEqual(AlidnsProvider.content_type, "application/x-www-form-urlencoded")
- self.assertTrue(AlidnsProvider.decode_response)
- def test_init_with_basic_config(self):
- """Test AlidnsProvider initialization with basic configuration"""
- provider = AlidnsProvider(self.id, self.token)
- self.assertEqual(provider.id, self.id)
- self.assertEqual(provider.token, self.token)
- self.assertEqual(provider.endpoint, "https://alidns.aliyuncs.com")
- def test_request_basic(self):
- """Test _request method with basic parameters"""
- provider = AlidnsProvider(self.id, self.token)
- # Only mock the HTTP call to avoid actual network requests
- with patch.object(provider, "_http") as mock_http:
- mock_http.return_value = {"Success": True}
- result = provider._request("TestAction", DomainName="example.com")
- # Verify HTTP call was made with correct method and path
- mock_http.assert_called_once()
- call_args = mock_http.call_args
- self.assertEqual(call_args[0], ("POST", "/"))
- # Verify body and headers are present
- self.assertIn("body", call_args[1])
- self.assertIn("headers", call_args[1])
- # Verify headers contain required v3 signature fields
- headers = call_args[1]["headers"]
- self.assertIn("Authorization", headers)
- self.assertIn("x-acs-action", headers)
- self.assertIn("x-acs-date", headers)
- self.assertIn("x-acs-version", headers)
- self.assertEqual(headers["x-acs-action"], "TestAction")
- # Verify body is form-encoded
- body = call_args[1]["body"]
- self.assertIn("DomainName=example.com", body)
- self.assertEqual(result, {"Success": True})
- def test_request_filters_none_params(self):
- """Test _request method filters out None parameters"""
- provider = AlidnsProvider(self.id, self.token)
- # Only mock the HTTP call
- with patch.object(provider, "_http") as mock_http:
- mock_http.return_value = {}
- provider._request("TestAction", DomainName="example.com", TTL=None, Line=None)
- # Verify HTTP call was made
- mock_http.assert_called_once()
- # Body should not contain None parameters
- call_args = mock_http.call_args[1]
- body = call_args.get("body", "")
- self.assertNotIn("TTL=None", body)
- self.assertNotIn("Line=None", body)
- self.assertIn("DomainName=example.com", body)
- def test_split_zone_and_sub_success(self):
- """Test _split_zone_and_sub method with successful response"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- mock_http.return_value = {"DomainName": "example.com", "RR": "sub"}
- main, sub, zone_id = provider._split_zone_and_sub("sub.example.com")
- mock_http.assert_called_once()
- # Verify GetMainDomainName API was called via headers
- call_headers = mock_http.call_args[1]["headers"]
- self.assertEqual(call_headers["x-acs-action"], "GetMainDomainName")
- # Verify the input parameter in body
- call_body = mock_http.call_args[1]["body"]
- self.assertIn("InputString=sub.example.com", call_body)
- self.assertEqual(main, "example.com")
- self.assertEqual(sub, "sub")
- self.assertEqual(zone_id, "example.com")
- def test_split_zone_and_sub_not_found(self):
- """Test _split_zone_and_sub method when domain is not found"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- mock_http.return_value = {}
- main, sub, zone_id = provider._split_zone_and_sub("notfound.com")
- mock_http.assert_called_once()
- self.assertIsNone(main)
- self.assertIsNone(sub)
- self.assertEqual(zone_id, "notfound.com")
- def test_query_record_success_single(self):
- """Test _query_record method with single record found"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- mock_http.return_value = {
- "DomainRecords": {"Record": [{"RR": "www", "RecordId": "123", "Value": "1.2.3.4", "Type": "A"}]}
- }
- result = provider._query_record("example.com", "www", "example.com", "A", None, {})
- mock_http.assert_called_once()
- # Verify DescribeSubDomainRecords API was called via headers
- call_headers = mock_http.call_args[1]["headers"]
- self.assertEqual(call_headers["x-acs-action"], "DescribeSubDomainRecords")
- # Verify parameters in body
- call_body = mock_http.call_args[1]["body"]
- self.assertIn("SubDomain=www.example.com", call_body)
- self.assertIn("DomainName=example.com", call_body)
- self.assertIn("Type=A", call_body)
- self.assertIsNotNone(result)
- if result: # Type narrowing for mypy
- self.assertEqual(result["RecordId"], "123")
- self.assertEqual(result["RR"], "www")
- def test_query_record_not_found(self):
- """Test _query_record method when no matching record is found"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- mock_http.return_value = {"DomainRecords": {"Record": []}}
- result = provider._query_record("example.com", "www", "example.com", "A", None, {})
- self.assertIsNone(result)
- def test_query_record_empty_response(self):
- """Test _query_record method with empty records response"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- mock_http.return_value = {"DomainRecords": {"Record": []}}
- result = provider._query_record("example.com", "www", "example.com", "A", None, {})
- self.assertIsNone(result)
- def test_query_record_with_extra_params(self):
- """Test _query_record method with extra parameters"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- mock_http.return_value = {"DomainRecords": {"Record": []}}
- extra = {"Lang": "en", "Status": "Enable"}
- provider._query_record("example.com", "www", "example.com", "A", "default", extra)
- mock_http.assert_called_once()
- # Verify extra parameters are included in the request
- call_body = mock_http.call_args[1]["body"]
- self.assertIn("Lang=en", call_body)
- self.assertIn("Status=Enable", call_body)
- self.assertIn("Line=default", call_body)
- def test_create_record_success(self):
- """Test _create_record method with successful creation"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_request") as mock_request:
- mock_request.return_value = {"RecordId": "123456"}
- result = provider._create_record("example.com", "www", "example.com", "1.2.3.4", "A", 300, "default", {})
- mock_request.assert_called_once_with(
- "AddDomainRecord",
- DomainName="example.com",
- RR="www",
- Value="1.2.3.4",
- Type="A",
- TTL=300,
- Line="default",
- )
- self.assertTrue(result)
- def test_create_record_failure(self):
- """Test _create_record method with failed creation"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_request") as mock_request:
- mock_request.return_value = {"Error": "Invalid domain"}
- result = provider._create_record("example.com", "www", "example.com", "1.2.3.4", "A", None, None, {})
- self.assertFalse(result)
- def test_create_record_with_extra_params(self):
- """Test _create_record method with extra parameters"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_request") as mock_request:
- mock_request.return_value = {"RecordId": "123456"}
- extra = {"Priority": 10, "Remark": "Test record"}
- result = provider._create_record("t.com", "www", "t.com", "1.2.3.4", "A", 300, "default", extra)
- mock_request.assert_called_once_with(
- "AddDomainRecord",
- DomainName="t.com",
- RR="www",
- Value="1.2.3.4",
- Type="A",
- TTL=300,
- Line="default",
- Priority=10,
- Remark="Test record",
- )
- self.assertTrue(result)
- def test_update_record_success(self):
- """Test _update_record method with successful update"""
- provider = AlidnsProvider(self.id, self.token)
- old_record = {"RecordId": "123456", "RR": "www", "Line": "default"}
- with patch.object(provider, "_request") as mock_request:
- mock_request.return_value = {"RecordId": "123456"}
- result = provider._update_record("example.com", old_record, "5.6.7.8", "A", 600, "unicom", {})
- mock_request.assert_called_once_with(
- "UpdateDomainRecord", RecordId="123456", Value="5.6.7.8", RR="www", Type="A", TTL=600, Line="unicom"
- )
- self.assertTrue(result)
- def test_update_record_with_fallback_line(self):
- """Test _update_record method uses old record's line when line is None"""
- provider = AlidnsProvider(self.id, self.token)
- old_record = {"RecordId": "123456", "RR": "www", "Line": "default"}
- with patch.object(provider, "_request") as mock_request:
- mock_request.return_value = {"RecordId": "123456"}
- result = provider._update_record("example.com", old_record, "5.6.7.8", "A", None, None, {})
- mock_request.assert_called_once_with(
- "UpdateDomainRecord",
- RecordId="123456",
- Value="5.6.7.8",
- RR="www",
- Type="A",
- TTL=None,
- Line="default", # Should use old record's line
- )
- self.assertTrue(result)
- def test_update_record_failure(self):
- """Test _update_record method with failed update"""
- provider = AlidnsProvider(self.id, self.token)
- old_record = {"RecordId": "123456", "RR": "www", "Line": "default"}
- with patch.object(provider, "_request") as mock_request:
- mock_request.return_value = {"Error": "Record not found"}
- result = provider._update_record("example.com", old_record, "5.6.7.8", "A", None, None, {})
- self.assertFalse(result)
- def test_update_record_no_changes(self):
- """Test _update_record method when no changes are detected"""
- provider = AlidnsProvider(self.id, self.token)
- old_record = {"RecordId": "123456", "RR": "www", "Value": "1.2.3.4", "Type": "A", "TTL": 300, "Line": "default"}
- with patch.object(provider, "_request") as mock_request:
- # Same value, type, and TTL should skip update
- result = provider._update_record("example.com", old_record, "1.2.3.4", "A", 300, "default", {})
- # Should return True without making any API calls
- self.assertTrue(result)
- mock_request.assert_not_called()
- def test_update_record_with_extra_params(self):
- """Test _update_record method with extra parameters"""
- provider = AlidnsProvider(self.id, self.token)
- old_record = {"RecordId": "123456", "RR": "www", "Line": "default"}
- with patch.object(provider, "_request") as mock_request:
- mock_request.return_value = {"RecordId": "123456"}
- extra = {"Priority": 20, "Remark": "Updated record"}
- result = provider._update_record("example.com", old_record, "5.6.7.8", "A", 600, "unicom", extra)
- mock_request.assert_called_once_with(
- "UpdateDomainRecord",
- RecordId="123456",
- Value="5.6.7.8",
- RR="www",
- Type="A",
- TTL=600,
- Line="unicom",
- Priority=20,
- Remark="Updated record",
- )
- self.assertTrue(result)
- def test_update_record_extra_priority_over_old_record(self):
- """Test that extra parameters take priority over old_record values"""
- provider = AlidnsProvider(self.id, self.token)
- old_record = {"RecordId": "123456", "RR": "www", "Line": "default", "Priority": 10, "Remark": "Old remark"}
- with patch.object(provider, "_request") as mock_request:
- mock_request.return_value = {"RecordId": "123456"}
- # extra should override old_record's Priority and Remark
- extra = {"Priority": 20, "Remark": "New remark from extra"}
- result = provider._update_record("example.com", old_record, "5.6.7.8", "A", 600, "unicom", extra)
- mock_request.assert_called_once_with(
- "UpdateDomainRecord",
- RecordId="123456",
- Value="5.6.7.8",
- RR="www",
- Type="A",
- TTL=600,
- Line="unicom",
- Priority=20, # Should use extra, not old_record's 10
- Remark="New remark from extra", # Should use extra, not old_record's "Old remark"
- )
- self.assertTrue(result)
- def test_line_configuration_support(self):
- """Test that AlidnsProvider supports line configuration"""
- provider = AlidnsProvider(self.id, self.token)
- old_record = {"RecordId": "123456", "RR": "www", "Line": "default"}
- with patch.object(provider, "_request") as mock_request:
- mock_request.return_value = {"RecordId": "123456"}
- # Test with custom line parameter
- result = provider._update_record("example.com", old_record, "5.6.7.8", "A", 600, "telecom", {})
- mock_request.assert_called_once_with(
- "UpdateDomainRecord",
- RecordId="123456",
- Value="5.6.7.8",
- RR="www",
- Type="A",
- TTL=600,
- Line="telecom", # Should use the provided line parameter
- )
- self.assertTrue(result)
- def test_create_record_with_line(self):
- """Test _create_record method with line parameter"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_request") as mock_request:
- mock_request.return_value = {"RecordId": "123456"}
- result = provider._create_record("example.com", "www", "example.com", "1.2.3.4", "A", 300, "unicom", {})
- mock_request.assert_called_once_with(
- "AddDomainRecord", DomainName="example.com", RR="www", Value="1.2.3.4", Type="A", TTL=300, Line="unicom"
- )
- self.assertTrue(result)
- class TestAlidnsProviderIntegration(BaseProviderTestCase):
- """Integration test cases for AlidnsProvider - testing with minimal mocking"""
- def setUp(self):
- """Set up test fixtures"""
- super(TestAlidnsProviderIntegration, self).setUp()
- self.id = "test_access_key_id"
- self.token = "test_access_key_secret"
- def test_full_workflow_create_new_record(self):
- """Test complete workflow for creating a new record"""
- provider = AlidnsProvider(self.id, self.token)
- # Mock only the HTTP layer to simulate API responses
- with patch.object(provider, "_http") as mock_http:
- # Simulate API responses in order: GetMainDomainName, DescribeSubDomainRecords, AddDomainRecord
- mock_http.side_effect = [
- {"DomainName": "example.com", "RR": "www"}, # _split_zone_and_sub response
- {"DomainRecords": {"Record": []}}, # _query_record response (no existing record)
- {"RecordId": "123456"}, # _create_record response
- ]
- result = provider.set_record("www.example.com", "1.2.3.4", "A", 300, "default")
- self.assertTrue(result)
- # Verify the actual HTTP calls made - should be 3 calls
- self.assertEqual(mock_http.call_count, 3)
- # Check that proper API actions were called by examining request headers
- call_actions = [call[1]["headers"]["x-acs-action"] for call in mock_http.call_args_list]
- self.assertIn("GetMainDomainName", call_actions)
- self.assertIn("DescribeSubDomainRecords", call_actions)
- self.assertIn("AddDomainRecord", call_actions)
- def test_full_workflow_update_existing_record(self):
- """Test complete workflow for updating an existing record"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- # Simulate API responses: GetMainDomainName, DescribeSubDomainRecords, UpdateDomainRecord
- mock_http.side_effect = [
- {"DomainName": "example.com", "RR": "www"}, # _split_zone_and_sub response
- { # _query_record response (existing record found)
- "DomainRecords": {
- "Record": [
- {"RecordId": "123456", "RR": "www", "Value": "5.6.7.8", "Type": "A", "Line": "default"}
- ]
- }
- },
- {"RecordId": "123456"}, # _update_record response
- ]
- result = provider.set_record("www.example.com", "1.2.3.4", "A", 300, "default")
- self.assertTrue(result)
- # Verify 3 HTTP calls were made
- self.assertEqual(mock_http.call_count, 3)
- # Check that UpdateDomainRecord was called
- call_actions = [call[1]["headers"]["x-acs-action"] for call in mock_http.call_args_list]
- self.assertIn("UpdateDomainRecord", call_actions)
- def test_full_workflow_zone_not_found(self):
- """Test complete workflow when zone is not found"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- # Simulate API returning empty response for zone query
- mock_http.return_value = {}
- # Should return False when zone not found
- result = provider.set_record("www.nonexistent.com", "1.2.3.4", "A")
- self.assertFalse(result)
- def test_full_workflow_create_failure(self):
- """Test complete workflow when record creation fails"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- # Simulate responses: zone found, no existing record, creation fails
- mock_http.side_effect = [
- {"DomainName": "example.com", "RR": "www"}, # _split_zone_and_sub response
- {"DomainRecords": {"Record": []}}, # _query_record response (no existing record)
- {"Error": "API error", "Code": "InvalidParameter"}, # _create_record fails
- ]
- result = provider.set_record("www.example.com", "1.2.3.4", "A")
- self.assertFalse(result)
- def test_full_workflow_update_failure(self):
- """Test complete workflow when record update fails"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- # Simulate responses: zone found, existing record found, update fails
- mock_http.side_effect = [
- {"DomainName": "example.com", "RR": "www"}, # _split_zone_and_sub response
- { # _query_record response (existing record found)
- "DomainRecords": {
- "Record": [
- {"RecordId": "123456", "RR": "www", "Value": "5.6.7.8", "Type": "A", "Line": "default"}
- ]
- }
- },
- {"Error": "API error", "Code": "InvalidParameter"}, # _update_record fails
- ]
- result = provider.set_record("www.example.com", "1.2.3.4", "A")
- self.assertFalse(result)
- def test_full_workflow_with_options(self):
- """Test complete workflow with additional options like ttl and line"""
- provider = AlidnsProvider(self.id, self.token)
- with patch.object(provider, "_http") as mock_http:
- # Simulate successful creation with custom options
- mock_http.side_effect = [
- {"DomainName": "example.com", "RR": "www"}, # _split_zone_and_sub response
- {"DomainRecords": {"Record": []}}, # _query_record response (no existing record)
- {"RecordId": "123456"}, # _create_record response
- ]
- result = provider.set_record("www.example.com", "1.2.3.4", "A", 600, "unicom")
- self.assertTrue(result)
- # Verify that extra parameters are passed through correctly
- self.assertEqual(mock_http.call_count, 3)
- # Check that the create call contains the correct parameters
- # Find the AddDomainRecord call (should be the last one)
- add_record_call = None
- for call in mock_http.call_args_list:
- if call[1]["headers"]["x-acs-action"] == "AddDomainRecord":
- add_record_call = call
- break
- self.assertIsNotNone(add_record_call)
- if add_record_call:
- create_body = add_record_call[1]["body"]
- self.assertIn("TTL=600", create_body)
- self.assertIn("Line=unicom", create_body)
- if __name__ == "__main__":
- unittest.main()
|