Skip to content

Commit 6cbdea8

Browse files
authored
feat: add tests for utils as well (#241)
* feat: add tests for utils as well * fix: test failures
1 parent 818f76b commit 6cbdea8

File tree

2 files changed

+310
-0
lines changed

2 files changed

+310
-0
lines changed

.coverage

0 Bytes
Binary file not shown.

tests/test_utils.py

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
"""Test module for json2xml.utils functionality."""
2+
import json
3+
import tempfile
4+
from typing import TYPE_CHECKING
5+
from unittest.mock import Mock, patch
6+
7+
import pytest
8+
9+
from json2xml.utils import (
10+
InvalidDataError,
11+
JSONReadError,
12+
StringReadError,
13+
URLReadError,
14+
readfromjson,
15+
readfromstring,
16+
readfromurl,
17+
)
18+
19+
if TYPE_CHECKING:
20+
from _pytest.capture import CaptureFixture
21+
from _pytest.fixtures import FixtureRequest
22+
from _pytest.logging import LogCaptureFixture
23+
from _pytest.monkeypatch import MonkeyPatch
24+
from pytest_mock.plugin import MockerFixture
25+
26+
27+
class TestExceptions:
28+
"""Test custom exception classes."""
29+
30+
def test_json_read_error(self) -> None:
31+
"""Test JSONReadError exception."""
32+
with pytest.raises(JSONReadError) as exc_info:
33+
raise JSONReadError("Test error message")
34+
assert str(exc_info.value) == "Test error message"
35+
36+
def test_invalid_data_error(self) -> None:
37+
"""Test InvalidDataError exception."""
38+
with pytest.raises(InvalidDataError) as exc_info:
39+
raise InvalidDataError("Invalid data")
40+
assert str(exc_info.value) == "Invalid data"
41+
42+
def test_url_read_error(self) -> None:
43+
"""Test URLReadError exception."""
44+
with pytest.raises(URLReadError) as exc_info:
45+
raise URLReadError("URL error")
46+
assert str(exc_info.value) == "URL error"
47+
48+
def test_string_read_error(self) -> None:
49+
"""Test StringReadError exception."""
50+
with pytest.raises(StringReadError) as exc_info:
51+
raise StringReadError("String error")
52+
assert str(exc_info.value) == "String error"
53+
54+
55+
class TestReadFromJson:
56+
"""Test readfromjson function."""
57+
58+
def test_readfromjson_valid_file(self) -> None:
59+
"""Test reading a valid JSON file."""
60+
test_data = {"key": "value", "number": 42}
61+
62+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
63+
json.dump(test_data, f)
64+
temp_filename = f.name
65+
66+
try:
67+
result = readfromjson(temp_filename)
68+
assert result == test_data
69+
finally:
70+
import os
71+
os.unlink(temp_filename)
72+
73+
def test_readfromjson_invalid_json_content(self) -> None:
74+
"""Test reading a file with invalid JSON content."""
75+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
76+
f.write('{"invalid": json content}') # Invalid JSON
77+
temp_filename = f.name
78+
79+
try:
80+
with pytest.raises(JSONReadError, match="Invalid JSON File"):
81+
readfromjson(temp_filename)
82+
finally:
83+
import os
84+
os.unlink(temp_filename)
85+
86+
def test_readfromjson_file_not_found(self) -> None:
87+
"""Test reading a non-existent file."""
88+
with pytest.raises(JSONReadError, match="Invalid JSON File"):
89+
readfromjson("non_existent_file.json")
90+
91+
@patch('builtins.open')
92+
def test_readfromjson_permission_error(self, mock_open: Mock) -> None:
93+
"""Test reading a file with permission issues."""
94+
# Mock open to raise PermissionError
95+
mock_open.side_effect = PermissionError("Permission denied")
96+
97+
with pytest.raises(JSONReadError, match="Invalid JSON File"):
98+
readfromjson("some_file.json")
99+
100+
@patch('builtins.open')
101+
def test_readfromjson_os_error(self, mock_open: Mock) -> None:
102+
"""Test reading a file with OS error."""
103+
# Mock open to raise OSError (covers line 34-35 in utils.py)
104+
mock_open.side_effect = OSError("Device not ready")
105+
106+
with pytest.raises(JSONReadError, match="Invalid JSON File"):
107+
readfromjson("some_file.json")
108+
109+
110+
class TestReadFromUrl:
111+
"""Test readfromurl function."""
112+
113+
@patch('json2xml.utils.urllib3.PoolManager')
114+
def test_readfromurl_success(self, mock_pool_manager: Mock) -> None:
115+
"""Test successful URL reading."""
116+
# Mock response
117+
mock_response = Mock()
118+
mock_response.status = 200
119+
mock_response.data = b'{"key": "value", "number": 42}'
120+
121+
# Mock PoolManager
122+
mock_http = Mock()
123+
mock_http.request.return_value = mock_response
124+
mock_pool_manager.return_value = mock_http
125+
126+
result = readfromurl("http://example.com/data.json")
127+
128+
assert result == {"key": "value", "number": 42}
129+
mock_pool_manager.assert_called_once()
130+
mock_http.request.assert_called_once_with("GET", "http://example.com/data.json", fields=None)
131+
132+
@patch('json2xml.utils.urllib3.PoolManager')
133+
def test_readfromurl_success_with_params(self, mock_pool_manager: Mock) -> None:
134+
"""Test successful URL reading with parameters."""
135+
# Mock response
136+
mock_response = Mock()
137+
mock_response.status = 200
138+
mock_response.data = b'{"result": "success"}'
139+
140+
# Mock PoolManager
141+
mock_http = Mock()
142+
mock_http.request.return_value = mock_response
143+
mock_pool_manager.return_value = mock_http
144+
145+
params = {"param1": "value1", "param2": "value2"}
146+
result = readfromurl("http://example.com/api", params=params)
147+
148+
assert result == {"result": "success"}
149+
mock_http.request.assert_called_once_with("GET", "http://example.com/api", fields=params)
150+
151+
@patch('json2xml.utils.urllib3.PoolManager')
152+
def test_readfromurl_http_error(self, mock_pool_manager: Mock) -> None:
153+
"""Test URL reading with HTTP error status."""
154+
# Mock response with error status
155+
mock_response = Mock()
156+
mock_response.status = 404
157+
158+
# Mock PoolManager
159+
mock_http = Mock()
160+
mock_http.request.return_value = mock_response
161+
mock_pool_manager.return_value = mock_http
162+
163+
with pytest.raises(URLReadError, match="URL is not returning correct response"):
164+
readfromurl("http://example.com/nonexistent.json")
165+
166+
@patch('json2xml.utils.urllib3.PoolManager')
167+
def test_readfromurl_server_error(self, mock_pool_manager: Mock) -> None:
168+
"""Test URL reading with server error status."""
169+
# Mock response with server error status
170+
mock_response = Mock()
171+
mock_response.status = 500
172+
173+
# Mock PoolManager
174+
mock_http = Mock()
175+
mock_http.request.return_value = mock_response
176+
mock_pool_manager.return_value = mock_http
177+
178+
with pytest.raises(URLReadError, match="URL is not returning correct response"):
179+
readfromurl("http://example.com/error.json")
180+
181+
@patch('json2xml.utils.urllib3.PoolManager')
182+
def test_readfromurl_invalid_json_response(self, mock_pool_manager: Mock) -> None:
183+
"""Test URL reading with invalid JSON response."""
184+
# Mock response with invalid JSON
185+
mock_response = Mock()
186+
mock_response.status = 200
187+
mock_response.data = b'invalid json content'
188+
189+
# Mock PoolManager
190+
mock_http = Mock()
191+
mock_http.request.return_value = mock_response
192+
mock_pool_manager.return_value = mock_http
193+
194+
with pytest.raises(json.JSONDecodeError):
195+
readfromurl("http://example.com/invalid.json")
196+
197+
198+
class TestReadFromString:
199+
"""Test readfromstring function."""
200+
201+
def test_readfromstring_valid_json(self) -> None:
202+
"""Test reading valid JSON string."""
203+
json_string = '{"key": "value", "number": 42, "boolean": true}'
204+
result = readfromstring(json_string)
205+
assert result == {"key": "value", "number": 42, "boolean": True}
206+
207+
def test_readfromstring_empty_object(self) -> None:
208+
"""Test reading empty JSON object."""
209+
json_string = '{}'
210+
result = readfromstring(json_string)
211+
assert result == {}
212+
213+
def test_readfromstring_complex_object(self) -> None:
214+
"""Test reading complex JSON object."""
215+
json_string = '{"users": [{"name": "John", "age": 30}, {"name": "Jane", "age": 25}], "total": 2}'
216+
result = readfromstring(json_string)
217+
expected = {
218+
"users": [
219+
{"name": "John", "age": 30},
220+
{"name": "Jane", "age": 25}
221+
],
222+
"total": 2
223+
}
224+
assert result == expected
225+
226+
def test_readfromstring_invalid_type_int(self) -> None:
227+
"""Test reading with integer input."""
228+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
229+
readfromstring(123) # type: ignore[arg-type]
230+
231+
def test_readfromstring_invalid_type_list(self) -> None:
232+
"""Test reading with list input."""
233+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
234+
readfromstring(["not", "a", "string"]) # type: ignore[arg-type]
235+
236+
def test_readfromstring_invalid_type_dict(self) -> None:
237+
"""Test reading with dict input."""
238+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
239+
readfromstring({"not": "a string"}) # type: ignore[arg-type]
240+
241+
def test_readfromstring_invalid_type_none(self) -> None:
242+
"""Test reading with None input."""
243+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
244+
readfromstring(None) # type: ignore[arg-type]
245+
246+
def test_readfromstring_invalid_json_syntax(self) -> None:
247+
"""Test reading string with invalid JSON syntax."""
248+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
249+
readfromstring('{"invalid": json, syntax}')
250+
251+
def test_readfromstring_invalid_json_incomplete(self) -> None:
252+
"""Test reading incomplete JSON string."""
253+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
254+
readfromstring('{"incomplete":')
255+
256+
def test_readfromstring_invalid_json_extra_comma(self) -> None:
257+
"""Test reading JSON string with trailing comma."""
258+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
259+
readfromstring('{"key": "value",}')
260+
261+
def test_readfromstring_invalid_json_single_quotes(self) -> None:
262+
"""Test reading JSON string with single quotes."""
263+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
264+
readfromstring("{'key': 'value'}")
265+
266+
def test_readfromstring_empty_string(self) -> None:
267+
"""Test reading empty string."""
268+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
269+
readfromstring("")
270+
271+
def test_readfromstring_plain_text(self) -> None:
272+
"""Test reading plain text."""
273+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
274+
readfromstring("this is just plain text")
275+
276+
277+
class TestIntegration:
278+
"""Integration tests combining multiple utilities."""
279+
280+
def test_readfromstring_then_convert_to_xml(self) -> None:
281+
"""Test reading JSON string and converting to XML."""
282+
from json2xml import dicttoxml
283+
284+
json_string = '{"name": "test", "value": 123}'
285+
data = readfromstring(json_string)
286+
xml_result = dicttoxml.dicttoxml(data, attr_type=False, root=False)
287+
288+
assert b"<name>test</name>" in xml_result
289+
assert b"<value>123</value>" in xml_result
290+
291+
@patch('json2xml.utils.urllib3.PoolManager')
292+
def test_readfromurl_then_convert_to_xml(self, mock_pool_manager: Mock) -> None:
293+
"""Test reading from URL and converting to XML."""
294+
from json2xml import dicttoxml
295+
296+
# Mock response
297+
mock_response = Mock()
298+
mock_response.status = 200
299+
mock_response.data = b'{"api": "response", "status": "ok"}'
300+
301+
# Mock PoolManager
302+
mock_http = Mock()
303+
mock_http.request.return_value = mock_response
304+
mock_pool_manager.return_value = mock_http
305+
306+
data = readfromurl("http://example.com/api.json")
307+
xml_result = dicttoxml.dicttoxml(data, attr_type=False, root=False)
308+
309+
assert b"<api>response</api>" in xml_result
310+
assert b"<status>ok</status>" in xml_result

0 commit comments

Comments
 (0)