Skip to content

Commit 9d0e36e

Browse files
committed
feat: add tests for utils as well
1 parent 818f76b commit 9d0e36e

File tree

2 files changed

+315
-0
lines changed

2 files changed

+315
-0
lines changed

.coverage

0 Bytes
Binary file not shown.

tests/test_utils.py

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
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+
def test_readfromjson_permission_error(self) -> None:
92+
"""Test reading a file with permission issues."""
93+
# Create a temporary file and then make it unreadable
94+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
95+
json.dump({"test": "data"}, f)
96+
temp_filename = f.name
97+
98+
try:
99+
import os
100+
# Make file unreadable (this might not work on all systems)
101+
os.chmod(temp_filename, 0o000)
102+
103+
with pytest.raises(JSONReadError, match="Invalid JSON File"):
104+
readfromjson(temp_filename)
105+
finally:
106+
# Restore permissions and cleanup
107+
import os
108+
try:
109+
os.chmod(temp_filename, 0o644)
110+
os.unlink(temp_filename)
111+
except (OSError, PermissionError):
112+
pass # Best effort cleanup
113+
114+
115+
class TestReadFromUrl:
116+
"""Test readfromurl function."""
117+
118+
@patch('json2xml.utils.urllib3.PoolManager')
119+
def test_readfromurl_success(self, mock_pool_manager: Mock) -> None:
120+
"""Test successful URL reading."""
121+
# Mock response
122+
mock_response = Mock()
123+
mock_response.status = 200
124+
mock_response.data = b'{"key": "value", "number": 42}'
125+
126+
# Mock PoolManager
127+
mock_http = Mock()
128+
mock_http.request.return_value = mock_response
129+
mock_pool_manager.return_value = mock_http
130+
131+
result = readfromurl("http://example.com/data.json")
132+
133+
assert result == {"key": "value", "number": 42}
134+
mock_pool_manager.assert_called_once()
135+
mock_http.request.assert_called_once_with("GET", "http://example.com/data.json", fields=None)
136+
137+
@patch('json2xml.utils.urllib3.PoolManager')
138+
def test_readfromurl_success_with_params(self, mock_pool_manager: Mock) -> None:
139+
"""Test successful URL reading with parameters."""
140+
# Mock response
141+
mock_response = Mock()
142+
mock_response.status = 200
143+
mock_response.data = b'{"result": "success"}'
144+
145+
# Mock PoolManager
146+
mock_http = Mock()
147+
mock_http.request.return_value = mock_response
148+
mock_pool_manager.return_value = mock_http
149+
150+
params = {"param1": "value1", "param2": "value2"}
151+
result = readfromurl("http://example.com/api", params=params)
152+
153+
assert result == {"result": "success"}
154+
mock_http.request.assert_called_once_with("GET", "http://example.com/api", fields=params)
155+
156+
@patch('json2xml.utils.urllib3.PoolManager')
157+
def test_readfromurl_http_error(self, mock_pool_manager: Mock) -> None:
158+
"""Test URL reading with HTTP error status."""
159+
# Mock response with error status
160+
mock_response = Mock()
161+
mock_response.status = 404
162+
163+
# Mock PoolManager
164+
mock_http = Mock()
165+
mock_http.request.return_value = mock_response
166+
mock_pool_manager.return_value = mock_http
167+
168+
with pytest.raises(URLReadError, match="URL is not returning correct response"):
169+
readfromurl("http://example.com/nonexistent.json")
170+
171+
@patch('json2xml.utils.urllib3.PoolManager')
172+
def test_readfromurl_server_error(self, mock_pool_manager: Mock) -> None:
173+
"""Test URL reading with server error status."""
174+
# Mock response with server error status
175+
mock_response = Mock()
176+
mock_response.status = 500
177+
178+
# Mock PoolManager
179+
mock_http = Mock()
180+
mock_http.request.return_value = mock_response
181+
mock_pool_manager.return_value = mock_http
182+
183+
with pytest.raises(URLReadError, match="URL is not returning correct response"):
184+
readfromurl("http://example.com/error.json")
185+
186+
@patch('json2xml.utils.urllib3.PoolManager')
187+
def test_readfromurl_invalid_json_response(self, mock_pool_manager: Mock) -> None:
188+
"""Test URL reading with invalid JSON response."""
189+
# Mock response with invalid JSON
190+
mock_response = Mock()
191+
mock_response.status = 200
192+
mock_response.data = b'invalid json content'
193+
194+
# Mock PoolManager
195+
mock_http = Mock()
196+
mock_http.request.return_value = mock_response
197+
mock_pool_manager.return_value = mock_http
198+
199+
with pytest.raises(json.JSONDecodeError):
200+
readfromurl("http://example.com/invalid.json")
201+
202+
203+
class TestReadFromString:
204+
"""Test readfromstring function."""
205+
206+
def test_readfromstring_valid_json(self) -> None:
207+
"""Test reading valid JSON string."""
208+
json_string = '{"key": "value", "number": 42, "boolean": true}'
209+
result = readfromstring(json_string)
210+
assert result == {"key": "value", "number": 42, "boolean": True}
211+
212+
def test_readfromstring_empty_object(self) -> None:
213+
"""Test reading empty JSON object."""
214+
json_string = '{}'
215+
result = readfromstring(json_string)
216+
assert result == {}
217+
218+
def test_readfromstring_complex_object(self) -> None:
219+
"""Test reading complex JSON object."""
220+
json_string = '{"users": [{"name": "John", "age": 30}, {"name": "Jane", "age": 25}], "total": 2}'
221+
result = readfromstring(json_string)
222+
expected = {
223+
"users": [
224+
{"name": "John", "age": 30},
225+
{"name": "Jane", "age": 25}
226+
],
227+
"total": 2
228+
}
229+
assert result == expected
230+
231+
def test_readfromstring_invalid_type_int(self) -> None:
232+
"""Test reading with integer input."""
233+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
234+
readfromstring(123) # type: ignore[arg-type]
235+
236+
def test_readfromstring_invalid_type_list(self) -> None:
237+
"""Test reading with list 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_dict(self) -> None:
242+
"""Test reading with dict input."""
243+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
244+
readfromstring({"not": "a string"}) # type: ignore[arg-type]
245+
246+
def test_readfromstring_invalid_type_none(self) -> None:
247+
"""Test reading with None input."""
248+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
249+
readfromstring(None) # type: ignore[arg-type]
250+
251+
def test_readfromstring_invalid_json_syntax(self) -> None:
252+
"""Test reading string with invalid JSON syntax."""
253+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
254+
readfromstring('{"invalid": json, syntax}')
255+
256+
def test_readfromstring_invalid_json_incomplete(self) -> None:
257+
"""Test reading incomplete JSON string."""
258+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
259+
readfromstring('{"incomplete":')
260+
261+
def test_readfromstring_invalid_json_extra_comma(self) -> None:
262+
"""Test reading JSON string with trailing comma."""
263+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
264+
readfromstring('{"key": "value",}')
265+
266+
def test_readfromstring_invalid_json_single_quotes(self) -> None:
267+
"""Test reading JSON string with single quotes."""
268+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
269+
readfromstring("{'key': 'value'}")
270+
271+
def test_readfromstring_empty_string(self) -> None:
272+
"""Test reading empty string."""
273+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
274+
readfromstring("")
275+
276+
def test_readfromstring_plain_text(self) -> None:
277+
"""Test reading plain text."""
278+
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
279+
readfromstring("this is just plain text")
280+
281+
282+
class TestIntegration:
283+
"""Integration tests combining multiple utilities."""
284+
285+
def test_readfromstring_then_convert_to_xml(self) -> None:
286+
"""Test reading JSON string and converting to XML."""
287+
from json2xml import dicttoxml
288+
289+
json_string = '{"name": "test", "value": 123}'
290+
data = readfromstring(json_string)
291+
xml_result = dicttoxml.dicttoxml(data, attr_type=False, root=False)
292+
293+
assert b"<name>test</name>" in xml_result
294+
assert b"<value>123</value>" in xml_result
295+
296+
@patch('json2xml.utils.urllib3.PoolManager')
297+
def test_readfromurl_then_convert_to_xml(self, mock_pool_manager: Mock) -> None:
298+
"""Test reading from URL and converting to XML."""
299+
from json2xml import dicttoxml
300+
301+
# Mock response
302+
mock_response = Mock()
303+
mock_response.status = 200
304+
mock_response.data = b'{"api": "response", "status": "ok"}'
305+
306+
# Mock PoolManager
307+
mock_http = Mock()
308+
mock_http.request.return_value = mock_response
309+
mock_pool_manager.return_value = mock_http
310+
311+
data = readfromurl("http://example.com/api.json")
312+
xml_result = dicttoxml.dicttoxml(data, attr_type=False, root=False)
313+
314+
assert b"<api>response</api>" in xml_result
315+
assert b"<status>ok</status>" in xml_result

0 commit comments

Comments
 (0)