diff --git a/tests/test_device_client.py b/tests/test_device_client.py new file mode 100644 index 0000000..ab02b6a --- /dev/null +++ b/tests/test_device_client.py @@ -0,0 +1,78 @@ +import pytest +from unittest.mock import AsyncMock, MagicMock, patch + +from tplinkcloud.device_client import TPLinkDeviceClient + + +class TestPassThroughRequest: + + @pytest.mark.asyncio + async def test_pass_through_request_handles_string_response_data(self): + """Test that string responseData is parsed as JSON""" + client = TPLinkDeviceClient( + host='http://test.example.com', + token='test_token' + ) + + mock_response = MagicMock() + mock_response.successful = True + mock_response.result = { + 'responseData': '{"system": {"get_sysinfo": {"relay_state": 1}}}' + } + + with patch.object(client, '_request_post', new_callable=AsyncMock) as mock_post: + mock_post.return_value = mock_response + result = await client.pass_through_request( + 'device123', + {'system': {'get_sysinfo': {}}} + ) + + assert result == {'system': {'get_sysinfo': {'relay_state': 1}}} + + @pytest.mark.asyncio + async def test_pass_through_request_handles_dict_response_data(self): + """ + Test that dict responseData is returned as-is. + Some devices (e.g., Archer routers) return responseData as a dict + instead of a JSON string. Regression test for issue #65. + """ + client = TPLinkDeviceClient( + host='http://test.example.com', + token='test_token' + ) + + mock_response = MagicMock() + mock_response.successful = True + # responseData is already a dict, not a JSON string + mock_response.result = { + 'responseData': {'system': {'get_sysinfo': {'relay_state': 1}}} + } + + with patch.object(client, '_request_post', new_callable=AsyncMock) as mock_post: + mock_post.return_value = mock_response + result = await client.pass_through_request( + 'device123', + {'system': {'get_sysinfo': {}}} + ) + + assert result == {'system': {'get_sysinfo': {'relay_state': 1}}} + + @pytest.mark.asyncio + async def test_pass_through_request_returns_none_on_failure(self): + """Test that None is returned when request fails""" + client = TPLinkDeviceClient( + host='http://test.example.com', + token='test_token' + ) + + mock_response = MagicMock() + mock_response.successful = False + + with patch.object(client, '_request_post', new_callable=AsyncMock) as mock_post: + mock_post.return_value = mock_response + result = await client.pass_through_request( + 'device123', + {'system': {'get_sysinfo': {}}} + ) + + assert result is None diff --git a/tplinkcloud/device_client.py b/tplinkcloud/device_client.py index a32a347..e62444c 100644 --- a/tplinkcloud/device_client.py +++ b/tplinkcloud/device_client.py @@ -62,6 +62,11 @@ async def pass_through_request(self, device_id, request_data): } response = await self._request_post(body) if response.successful: - return json.loads(response.result.get('responseData')) + response_data = response.result.get('responseData') + # Some devices (e.g., Archer routers) return responseData as a dict + # while others return it as a JSON string + if isinstance(response_data, str): + return json.loads(response_data) + return response_data return None