""" Unit tests for bunny.net API client """ import pytest from unittest.mock import Mock, patch import requests from src.deployment.bunnynet import ( BunnyNetClient, BunnyNetAPIError, BunnyNetAuthError, BunnyNetResourceConflictError, StorageZoneResult, PullZoneResult ) @pytest.fixture def bunny_client(): """Fixture to create a BunnyNetClient instance""" return BunnyNetClient(api_key="test_api_key") @pytest.fixture def mock_response(): """Fixture to create a mock response""" mock = Mock(spec=requests.Response) mock.status_code = 200 mock.json.return_value = {} mock.content = b'{}' return mock class TestBunnyNetClient: """Tests for BunnyNetClient""" def test_client_initialization(self): """Test client initializes with correct headers""" client = BunnyNetClient("test_key") assert client.api_key == "test_key" assert client.session.headers["AccessKey"] == "test_key" assert client.session.headers["Content-Type"] == "application/json" def test_create_storage_zone_success(self, bunny_client, mock_response): """Test successful storage zone creation""" mock_response.json.return_value = { "Id": 12345, "Name": "test-storage", "Password": "test-password", "Region": "DE" } with patch.object(bunny_client.session, 'request', return_value=mock_response): result = bunny_client.create_storage_zone("test-storage", "DE") assert isinstance(result, StorageZoneResult) assert result.id == 12345 assert result.name == "test-storage" assert result.password == "test-password" assert result.region == "DE" def test_create_storage_zone_auth_error(self, bunny_client, mock_response): """Test storage zone creation with auth error""" mock_response.status_code = 401 with patch.object(bunny_client.session, 'request', return_value=mock_response): with pytest.raises(BunnyNetAuthError): bunny_client.create_storage_zone("test-storage", "DE") def test_create_storage_zone_conflict(self, bunny_client, mock_response): """Test storage zone creation with name conflict""" mock_response.status_code = 409 with patch.object(bunny_client.session, 'request', return_value=mock_response): with pytest.raises(BunnyNetResourceConflictError): bunny_client.create_storage_zone("existing-storage", "DE") def test_get_storage_zones_success(self, bunny_client, mock_response): """Test successful retrieval of storage zones""" mock_response.json.return_value = [ {"Id": 1, "Name": "zone1", "Region": "DE"}, {"Id": 2, "Name": "zone2", "Region": "NY"} ] with patch.object(bunny_client.session, 'request', return_value=mock_response): result = bunny_client.get_storage_zones() assert len(result) == 2 assert result[0]["Name"] == "zone1" assert result[1]["Name"] == "zone2" def test_find_storage_zone_by_name_found(self, bunny_client, mock_response): """Test finding an existing storage zone by name""" mock_response.json.return_value = [ {"Id": 1, "Name": "zone1", "Region": "DE", "Password": "pass1"}, {"Id": 2, "Name": "zone2", "Region": "NY", "Password": "pass2"} ] with patch.object(bunny_client.session, 'request', return_value=mock_response): result = bunny_client.find_storage_zone_by_name("zone2") assert result is not None assert result.id == 2 assert result.name == "zone2" assert result.region == "NY" assert result.password == "pass2" def test_find_storage_zone_by_name_not_found(self, bunny_client, mock_response): """Test finding a non-existent storage zone""" mock_response.json.return_value = [ {"Id": 1, "Name": "zone1", "Region": "DE", "Password": "pass1"} ] with patch.object(bunny_client.session, 'request', return_value=mock_response): result = bunny_client.find_storage_zone_by_name("nonexistent") assert result is None def test_create_pull_zone_success(self, bunny_client, mock_response): """Test successful pull zone creation""" mock_response.json.return_value = { "Id": 67890, "Name": "test-cdn", "Hostnames": [{"Value": "test-cdn.b-cdn.net"}] } with patch.object(bunny_client.session, 'request', return_value=mock_response): result = bunny_client.create_pull_zone("test-cdn", 12345) assert isinstance(result, PullZoneResult) assert result.id == 67890 assert result.name == "test-cdn" assert result.hostname == "test-cdn.b-cdn.net" def test_create_pull_zone_no_hostnames(self, bunny_client, mock_response): """Test pull zone creation with no hostnames in response""" mock_response.json.return_value = { "Id": 67890, "Name": "test-cdn" } with patch.object(bunny_client.session, 'request', return_value=mock_response): result = bunny_client.create_pull_zone("test-cdn", 12345) assert result.hostname == "test-cdn.b-cdn.net" def test_create_pull_zone_conflict(self, bunny_client, mock_response): """Test pull zone creation with name conflict""" mock_response.status_code = 409 with patch.object(bunny_client.session, 'request', return_value=mock_response): with pytest.raises(BunnyNetResourceConflictError): bunny_client.create_pull_zone("existing-cdn", 12345) def test_add_custom_hostname_success(self, bunny_client, mock_response): """Test successful custom hostname addition""" mock_response.status_code = 200 with patch.object(bunny_client.session, 'request', return_value=mock_response): result = bunny_client.add_custom_hostname(67890, "www.example.com") assert result is True def test_add_custom_hostname_error(self, bunny_client, mock_response): """Test custom hostname addition with error""" mock_response.status_code = 400 mock_response.json.return_value = {"Message": "Invalid hostname"} with patch.object(bunny_client.session, 'request', return_value=mock_response): with pytest.raises(BunnyNetAPIError): bunny_client.add_custom_hostname(67890, "invalid") def test_request_timeout(self, bunny_client): """Test handling of request timeout""" with patch.object(bunny_client.session, 'request', side_effect=requests.exceptions.Timeout): with pytest.raises(BunnyNetAPIError, match="timeout"): bunny_client.create_storage_zone("test", "DE") def test_request_connection_error(self, bunny_client): """Test handling of connection error""" with patch.object(bunny_client.session, 'request', side_effect=requests.exceptions.ConnectionError): with pytest.raises(BunnyNetAPIError, match="Connection error"): bunny_client.create_storage_zone("test", "DE") def test_api_error_with_message(self, bunny_client, mock_response): """Test API error handling with message in response""" mock_response.status_code = 500 mock_response.json.return_value = {"Message": "Internal server error"} with patch.object(bunny_client.session, 'request', return_value=mock_response): with pytest.raises(BunnyNetAPIError, match="Internal server error"): bunny_client.create_storage_zone("test", "DE") def test_empty_response_handling(self, bunny_client, mock_response): """Test handling of empty response""" mock_response.status_code = 204 mock_response.content = b'' with patch.object(bunny_client.session, 'request', return_value=mock_response): result = bunny_client._make_request("POST", "/test") assert result == {}