Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .coverage
Binary file not shown.
310 changes: 310 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
"""Test module for json2xml.utils functionality."""
import json
import tempfile
from typing import TYPE_CHECKING
from unittest.mock import Mock, patch

import pytest

from json2xml.utils import (
InvalidDataError,
JSONReadError,
StringReadError,
URLReadError,
readfromjson,
readfromstring,
readfromurl,
)

if TYPE_CHECKING:
from _pytest.capture import CaptureFixture

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'CaptureFixture' is not used.

Copilot Autofix

AI 7 months ago

To fix the issue, we will remove the unused import of CaptureFixture from the TYPE_CHECKING block. This will clean up the code and eliminate the unnecessary dependency. No other changes are required, as this does not affect the functionality of the code.


Suggested changeset 1
tests/test_utils.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -19,3 +19,2 @@
 if TYPE_CHECKING:
-    from _pytest.capture import CaptureFixture
     from _pytest.fixtures import FixtureRequest
EOF
@@ -19,3 +19,2 @@
if TYPE_CHECKING:
from _pytest.capture import CaptureFixture
from _pytest.fixtures import FixtureRequest
Copilot is powered by AI and may make mistakes. Always verify output.
from _pytest.fixtures import FixtureRequest

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'FixtureRequest' is not used.

Copilot Autofix

AI 7 months ago

To fix the issue, the unused import of FixtureRequest should be removed from the TYPE_CHECKING block. This will eliminate the unnecessary dependency and improve code readability. The change is localized to the TYPE_CHECKING block, and no other parts of the code need to be modified.

Suggested changeset 1
tests/test_utils.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -20,3 +20,2 @@
     from _pytest.capture import CaptureFixture
-    from _pytest.fixtures import FixtureRequest
     from _pytest.logging import LogCaptureFixture
EOF
@@ -20,3 +20,2 @@
from _pytest.capture import CaptureFixture
from _pytest.fixtures import FixtureRequest
from _pytest.logging import LogCaptureFixture
Copilot is powered by AI and may make mistakes. Always verify output.
from _pytest.logging import LogCaptureFixture

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'LogCaptureFixture' is not used.

Copilot Autofix

AI 7 months ago

To fix the problem, the unused import statement for LogCaptureFixture should be removed from the file. This involves deleting the specific line where LogCaptureFixture is imported. No other changes are necessary since this import is not referenced anywhere else in the file.

Suggested changeset 1
tests/test_utils.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -21,3 +21,3 @@
     from _pytest.fixtures import FixtureRequest
-    from _pytest.logging import LogCaptureFixture
+
     from _pytest.monkeypatch import MonkeyPatch
EOF
@@ -21,3 +21,3 @@
from _pytest.fixtures import FixtureRequest
from _pytest.logging import LogCaptureFixture

from _pytest.monkeypatch import MonkeyPatch
Copilot is powered by AI and may make mistakes. Always verify output.
from _pytest.monkeypatch import MonkeyPatch

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'MonkeyPatch' is not used.

Copilot Autofix

AI 7 months ago

To fix the issue, the unused import statement for MonkeyPatch should be removed from the file. This involves deleting the line from _pytest.monkeypatch import MonkeyPatch (line 23). No other changes are required since this import is not referenced anywhere in the code.

Suggested changeset 1
tests/test_utils.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -22,3 +22,3 @@
     from _pytest.logging import LogCaptureFixture
-    from _pytest.monkeypatch import MonkeyPatch
+
     from pytest_mock.plugin import MockerFixture
EOF
@@ -22,3 +22,3 @@
from _pytest.logging import LogCaptureFixture
from _pytest.monkeypatch import MonkeyPatch

from pytest_mock.plugin import MockerFixture
Copilot is powered by AI and may make mistakes. Always verify output.
from pytest_mock.plugin import MockerFixture

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'MockerFixture' is not used.

Copilot Autofix

AI 7 months ago

To fix the problem, we should remove the unused import from pytest_mock.plugin import MockerFixture on line 24. This will eliminate the unnecessary dependency and improve the code's readability. No other changes are required since this import is not used anywhere in the provided code.

Suggested changeset 1
tests/test_utils.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -23,3 +23,3 @@
     from _pytest.monkeypatch import MonkeyPatch
-    from pytest_mock.plugin import MockerFixture
+
 
EOF
@@ -23,3 +23,3 @@
from _pytest.monkeypatch import MonkeyPatch
from pytest_mock.plugin import MockerFixture


Copilot is powered by AI and may make mistakes. Always verify output.


class TestExceptions:
"""Test custom exception classes."""

def test_json_read_error(self) -> None:
"""Test JSONReadError exception."""
with pytest.raises(JSONReadError) as exc_info:
raise JSONReadError("Test error message")
assert str(exc_info.value) == "Test error message"

Check warning

Code scanning / CodeQL

Unreachable code Warning test

This statement is unreachable.

Copilot Autofix

AI 7 months ago

To fix the issue, the assert statement should be moved into the pytest.raises context manager block. The pytest.raises block is designed to catch the raised exception, and the assert statement can then validate the exception's message. This ensures that the assert statement is executed and serves its intended purpose of verifying the exception's message.


Suggested changeset 1
tests/test_utils.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -33,3 +33,3 @@
             raise JSONReadError("Test error message")
-        assert str(exc_info.value) == "Test error message"
+            assert str(exc_info.value) == "Test error message"
 
EOF
@@ -33,3 +33,3 @@
raise JSONReadError("Test error message")
assert str(exc_info.value) == "Test error message"
assert str(exc_info.value) == "Test error message"

Copilot is powered by AI and may make mistakes. Always verify output.

def test_invalid_data_error(self) -> None:
"""Test InvalidDataError exception."""
with pytest.raises(InvalidDataError) as exc_info:
raise InvalidDataError("Invalid data")
assert str(exc_info.value) == "Invalid data"

Check warning

Code scanning / CodeQL

Unreachable code Warning test

This statement is unreachable.

Copilot Autofix

AI 7 months ago

To fix the issue, the unreachable assert statement should be moved into the with pytest.raises context manager block. This ensures that the assert statement is executed after the exception is raised and caught, allowing the test to verify the exception's message as intended. This change preserves the functionality of the test while eliminating the unreachable code.


Suggested changeset 1
tests/test_utils.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -39,3 +39,3 @@
             raise InvalidDataError("Invalid data")
-        assert str(exc_info.value) == "Invalid data"
+            assert str(exc_info.value) == "Invalid data"
 
EOF
@@ -39,3 +39,3 @@
raise InvalidDataError("Invalid data")
assert str(exc_info.value) == "Invalid data"
assert str(exc_info.value) == "Invalid data"

Copilot is powered by AI and may make mistakes. Always verify output.

def test_url_read_error(self) -> None:
"""Test URLReadError exception."""
with pytest.raises(URLReadError) as exc_info:
raise URLReadError("URL error")
assert str(exc_info.value) == "URL error"

Check warning

Code scanning / CodeQL

Unreachable code Warning test

This statement is unreachable.

Copilot Autofix

AI 7 months ago

To fix the issue, the unreachable assert statement should be moved outside the raise block while preserving its functionality. The pytest.raises context manager already captures the exception, allowing the assert statement to verify the exception message after the exception is raised. This ensures the test remains valid and functional.

The fix involves:

  1. Moving the assert statement to follow the raise statement within the pytest.raises context manager.
  2. Ensuring the test logic remains intact and correctly verifies the exception message.

Suggested changeset 1
tests/test_utils.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -45,2 +45,3 @@
             raise URLReadError("URL error")
+        # Verify the exception message after it is raised
         assert str(exc_info.value) == "URL error"
EOF
@@ -45,2 +45,3 @@
raise URLReadError("URL error")
# Verify the exception message after it is raised
assert str(exc_info.value) == "URL error"
Copilot is powered by AI and may make mistakes. Always verify output.

def test_string_read_error(self) -> None:
"""Test StringReadError exception."""
with pytest.raises(StringReadError) as exc_info:
raise StringReadError("String error")
assert str(exc_info.value) == "String error"

Check warning

Code scanning / CodeQL

Unreachable code Warning test

This statement is unreachable.

Copilot Autofix

AI 7 months ago

To fix the issue, the unreachable assertion on line 52 should be moved inside the pytest.raises block. This ensures that the assertion is executed after the exception is raised and captured by pytest.raises. The fix preserves the functionality of the test while eliminating unreachable code.

Steps:

  1. Move the assertion assert str(exc_info.value) == "String error" inside the pytest.raises block.
  2. Ensure the indentation and formatting align with the rest of the code.

Suggested changeset 1
tests/test_utils.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -51,3 +51,3 @@
             raise StringReadError("String error")
-        assert str(exc_info.value) == "String error"
+            assert str(exc_info.value) == "String error"
 
EOF
@@ -51,3 +51,3 @@
raise StringReadError("String error")
assert str(exc_info.value) == "String error"
assert str(exc_info.value) == "String error"

Copilot is powered by AI and may make mistakes. Always verify output.


class TestReadFromJson:
"""Test readfromjson function."""
Comment on lines +55 to +56
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Add tests for readfromjson with an empty file or a file containing non-object JSON.

Add tests for empty files (should raise JSONReadError) and for files with valid non-object JSON (should raise an appropriate error, given the function's dict return type).

Suggested change
class TestReadFromJson:
"""Test readfromjson function."""
class TestReadFromJson:
"""Test readfromjson function."""
def test_readfromjson_empty_file(self, tmp_path):
"""Test that reading from an empty file raises JSONReadError."""
empty_file = tmp_path / "empty.json"
empty_file.write_text("")
with pytest.raises(JSONReadError):
readfromjson(str(empty_file))
def test_readfromjson_non_object_json(self, tmp_path):
"""Test that reading from a file with non-object JSON raises InvalidDataError."""
# JSON array
array_file = tmp_path / "array.json"
array_file.write_text('[1, 2, 3]')
with pytest.raises(InvalidDataError):
readfromjson(str(array_file))
# JSON string
string_file = tmp_path / "string.json"
string_file.write_text('"hello"')
with pytest.raises(InvalidDataError):
readfromjson(str(string_file))


def test_readfromjson_valid_file(self) -> None:
"""Test reading a valid JSON file."""
test_data = {"key": "value", "number": 42}

with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
json.dump(test_data, f)
temp_filename = f.name

try:
result = readfromjson(temp_filename)
assert result == test_data
finally:
import os
os.unlink(temp_filename)

def test_readfromjson_invalid_json_content(self) -> None:
"""Test reading a file with invalid JSON content."""
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
f.write('{"invalid": json content}') # Invalid JSON
temp_filename = f.name

try:
with pytest.raises(JSONReadError, match="Invalid JSON File"):
readfromjson(temp_filename)
finally:
import os
os.unlink(temp_filename)
Comment on lines +58 to +84
Copy link

Copilot AI Jun 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider using pytest's tmp_path fixture instead of manual tempfile and cleanup to simplify file management and avoid repetitive os.unlink calls.

Suggested change
def test_readfromjson_valid_file(self) -> None:
"""Test reading a valid JSON file."""
test_data = {"key": "value", "number": 42}
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
json.dump(test_data, f)
temp_filename = f.name
try:
result = readfromjson(temp_filename)
assert result == test_data
finally:
import os
os.unlink(temp_filename)
def test_readfromjson_invalid_json_content(self) -> None:
"""Test reading a file with invalid JSON content."""
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
f.write('{"invalid": json content}') # Invalid JSON
temp_filename = f.name
try:
with pytest.raises(JSONReadError, match="Invalid JSON File"):
readfromjson(temp_filename)
finally:
import os
os.unlink(temp_filename)
def test_readfromjson_valid_file(self, tmp_path) -> None:
"""Test reading a valid JSON file."""
test_data = {"key": "value", "number": 42}
temp_file = tmp_path / "test.json"
temp_file.write_text(json.dumps(test_data))
result = readfromjson(str(temp_file))
assert result == test_data
def test_readfromjson_invalid_json_content(self, tmp_path) -> None:
"""Test reading a file with invalid JSON content."""
temp_file = tmp_path / "invalid.json"
temp_file.write_text('{"invalid": json content}') # Invalid JSON
with pytest.raises(JSONReadError, match="Invalid JSON File"):
readfromjson(str(temp_file))

Copilot uses AI. Check for mistakes.

def test_readfromjson_file_not_found(self) -> None:
"""Test reading a non-existent file."""
with pytest.raises(JSONReadError, match="Invalid JSON File"):
readfromjson("non_existent_file.json")

@patch('builtins.open')
def test_readfromjson_permission_error(self, mock_open: Mock) -> None:
"""Test reading a file with permission issues."""
# Mock open to raise PermissionError
mock_open.side_effect = PermissionError("Permission denied")

with pytest.raises(JSONReadError, match="Invalid JSON File"):
readfromjson("some_file.json")

@patch('builtins.open')
def test_readfromjson_os_error(self, mock_open: Mock) -> None:
"""Test reading a file with OS error."""
# Mock open to raise OSError (covers line 34-35 in utils.py)
mock_open.side_effect = OSError("Device not ready")

with pytest.raises(JSONReadError, match="Invalid JSON File"):
readfromjson("some_file.json")


class TestReadFromUrl:
"""Test readfromurl function."""

Comment on lines +110 to +112
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Enhance TestReadFromUrl with tests for network-level errors.

Add tests that mock mock_http.request to raise exceptions like TimeoutError and NewConnectionError, and verify that readfromurl responds appropriately, such as by raising URLReadError.

Suggested change
class TestReadFromUrl:
"""Test readfromurl function."""
from requests.exceptions import Timeout, ConnectionError
import urllib3
class TestReadFromUrl:
"""Test readfromurl function."""
@patch("json2xml.utils.http")
def test_readfromurl_timeout_error(self, mock_http: Mock) -> None:
"""Test readfromurl raises URLReadError on TimeoutError."""
mock_http.request.side_effect = Timeout("Request timed out")
with pytest.raises(URLReadError, match="URL Read Error"):
readfromurl("http://example.com")
@patch("json2xml.utils.http")
def test_readfromurl_new_connection_error(self, mock_http: Mock) -> None:
"""Test readfromurl raises URLReadError on NewConnectionError."""
mock_http.request.side_effect = urllib3.exceptions.NewConnectionError(
None, "Failed to establish a new connection"
)
with pytest.raises(URLReadError, match="URL Read Error"):
readfromurl("http://example.com")

@patch('json2xml.utils.urllib3.PoolManager')
def test_readfromurl_success(self, mock_pool_manager: Mock) -> None:
"""Test successful URL reading."""
# Mock response
mock_response = Mock()
mock_response.status = 200
mock_response.data = b'{"key": "value", "number": 42}'

# Mock PoolManager
mock_http = Mock()
mock_http.request.return_value = mock_response
mock_pool_manager.return_value = mock_http

result = readfromurl("http://example.com/data.json")

assert result == {"key": "value", "number": 42}
mock_pool_manager.assert_called_once()
mock_http.request.assert_called_once_with("GET", "http://example.com/data.json", fields=None)

@patch('json2xml.utils.urllib3.PoolManager')
def test_readfromurl_success_with_params(self, mock_pool_manager: Mock) -> None:
"""Test successful URL reading with parameters."""
# Mock response
mock_response = Mock()
mock_response.status = 200
mock_response.data = b'{"result": "success"}'

# Mock PoolManager
mock_http = Mock()
mock_http.request.return_value = mock_response
mock_pool_manager.return_value = mock_http

params = {"param1": "value1", "param2": "value2"}
result = readfromurl("http://example.com/api", params=params)

assert result == {"result": "success"}
mock_http.request.assert_called_once_with("GET", "http://example.com/api", fields=params)

@patch('json2xml.utils.urllib3.PoolManager')
def test_readfromurl_http_error(self, mock_pool_manager: Mock) -> None:
"""Test URL reading with HTTP error status."""
# Mock response with error status
mock_response = Mock()
mock_response.status = 404

# Mock PoolManager
mock_http = Mock()
mock_http.request.return_value = mock_response
mock_pool_manager.return_value = mock_http

with pytest.raises(URLReadError, match="URL is not returning correct response"):
readfromurl("http://example.com/nonexistent.json")

@patch('json2xml.utils.urllib3.PoolManager')
def test_readfromurl_server_error(self, mock_pool_manager: Mock) -> None:
"""Test URL reading with server error status."""
# Mock response with server error status
mock_response = Mock()
mock_response.status = 500

# Mock PoolManager
mock_http = Mock()
mock_http.request.return_value = mock_response
mock_pool_manager.return_value = mock_http

with pytest.raises(URLReadError, match="URL is not returning correct response"):
readfromurl("http://example.com/error.json")

@patch('json2xml.utils.urllib3.PoolManager')
def test_readfromurl_invalid_json_response(self, mock_pool_manager: Mock) -> None:
"""Test URL reading with invalid JSON response."""
# Mock response with invalid JSON
mock_response = Mock()
mock_response.status = 200
mock_response.data = b'invalid json content'

# Mock PoolManager
mock_http = Mock()
mock_http.request.return_value = mock_response
mock_pool_manager.return_value = mock_http

with pytest.raises(json.JSONDecodeError):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider consistent error wrapping for JSONDecodeError in readfromurl.

Decide if readfromurl should wrap json.JSONDecodeError in a custom error for consistency with TestReadFromJson. If so, update this test to expect the custom error instead of the raw exception.

Suggested implementation:

        with pytest.raises(JSONReadError):
            readfromurl("http://example.com/invalid.json")

You must also update the implementation of readfromurl (likely in json2xml/utils.py) to catch json.JSONDecodeError and raise JSONReadError instead. For example:

import json

def readfromurl(url):
    ...
    try:
        data = json.loads(response.data.decode("utf-8"))
    except json.JSONDecodeError as e:
        raise JSONReadError("Invalid JSON data") from e
    ...

readfromurl("http://example.com/invalid.json")


class TestReadFromString:
"""Test readfromstring function."""

Comment on lines +198 to +200
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Test readfromstring with valid JSON strings that are not dictionary objects.

Add tests for valid JSON inputs that are not dictionaries (e.g., arrays, null, booleans, numbers, strings) to ensure readfromstring raises an appropriate error for type mismatches.

Suggested change
class TestReadFromString:
"""Test readfromstring function."""
class TestReadFromString:
"""Test readfromstring function."""
@pytest.mark.parametrize(
"json_input",
[
'["array", "of", "values"]',
'null',
'true',
'false',
'123',
'"just a string"',
'3.14'
]
)
def test_readfromstring_non_dict_types(self, json_input):
"""readfromstring should raise InvalidDataError for non-dict JSON inputs."""
with pytest.raises(InvalidDataError):
readfromstring(json_input)

def test_readfromstring_valid_json(self) -> None:
"""Test reading valid JSON string."""
json_string = '{"key": "value", "number": 42, "boolean": true}'
result = readfromstring(json_string)
assert result == {"key": "value", "number": 42, "boolean": True}

def test_readfromstring_empty_object(self) -> None:
"""Test reading empty JSON object."""
json_string = '{}'
result = readfromstring(json_string)
assert result == {}

def test_readfromstring_complex_object(self) -> None:
"""Test reading complex JSON object."""
json_string = '{"users": [{"name": "John", "age": 30}, {"name": "Jane", "age": 25}], "total": 2}'
result = readfromstring(json_string)
expected = {
"users": [
{"name": "John", "age": 30},
{"name": "Jane", "age": 25}
],
"total": 2
}
assert result == expected

def test_readfromstring_invalid_type_int(self) -> None:
"""Test reading with integer input."""
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
readfromstring(123) # type: ignore[arg-type]

def test_readfromstring_invalid_type_list(self) -> None:
"""Test reading with list input."""
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
readfromstring(["not", "a", "string"]) # type: ignore[arg-type]

def test_readfromstring_invalid_type_dict(self) -> None:
"""Test reading with dict input."""
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
readfromstring({"not": "a string"}) # type: ignore[arg-type]

def test_readfromstring_invalid_type_none(self) -> None:
"""Test reading with None input."""
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
readfromstring(None) # type: ignore[arg-type]

def test_readfromstring_invalid_json_syntax(self) -> None:
"""Test reading string with invalid JSON syntax."""
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
readfromstring('{"invalid": json, syntax}')

def test_readfromstring_invalid_json_incomplete(self) -> None:
"""Test reading incomplete JSON string."""
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
readfromstring('{"incomplete":')

def test_readfromstring_invalid_json_extra_comma(self) -> None:
"""Test reading JSON string with trailing comma."""
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
readfromstring('{"key": "value",}')

def test_readfromstring_invalid_json_single_quotes(self) -> None:
"""Test reading JSON string with single quotes."""
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
readfromstring("{'key': 'value'}")

def test_readfromstring_empty_string(self) -> None:
"""Test reading empty string."""
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
readfromstring("")

def test_readfromstring_plain_text(self) -> None:
"""Test reading plain text."""
with pytest.raises(StringReadError, match="Input is not a proper JSON string"):
readfromstring("this is just plain text")


class TestIntegration:
"""Integration tests combining multiple utilities."""

Comment on lines +274 to +279
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Add an integration test for readfromjson chained with dicttoxml.

An integration test for readfromjson with dicttoxml should be added to match the coverage described in the PR.

Suggested change
readfromstring("this is just plain text")
class TestIntegration:
"""Integration tests combining multiple utilities."""
readfromstring("this is just plain text")
class TestIntegration:
"""Integration tests combining multiple utilities."""
def test_readfromjson_chained_with_dicttoxml(self):
"""Test chaining readfromjson with dicttoxml."""
from json2xml.utils import readfromjson
from json2xml import dicttoxml
json_input = '{"foo": "bar", "baz": 123}'
data = readfromjson(json_input)
xml_output = dicttoxml(data)
assert "<foo>bar</foo>" in xml_output
assert "<baz>123</baz>" in xml_output

def test_readfromstring_then_convert_to_xml(self) -> None:
"""Test reading JSON string and converting to XML."""
from json2xml import dicttoxml

json_string = '{"name": "test", "value": 123}'
data = readfromstring(json_string)
xml_result = dicttoxml.dicttoxml(data, attr_type=False, root=False)

assert b"<name>test</name>" in xml_result
assert b"<value>123</value>" in xml_result

@patch('json2xml.utils.urllib3.PoolManager')
def test_readfromurl_then_convert_to_xml(self, mock_pool_manager: Mock) -> None:
"""Test reading from URL and converting to XML."""
from json2xml import dicttoxml

# Mock response
mock_response = Mock()
mock_response.status = 200
mock_response.data = b'{"api": "response", "status": "ok"}'

# Mock PoolManager
mock_http = Mock()
mock_http.request.return_value = mock_response
mock_pool_manager.return_value = mock_http

data = readfromurl("http://example.com/api.json")
xml_result = dicttoxml.dicttoxml(data, attr_type=False, root=False)

assert b"<api>response</api>" in xml_result
assert b"<status>ok</status>" in xml_result
Loading