chore: import upstream snapshot with attribution
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
"""Tests for robust JSON parsing utilities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from deeptutor.utils.json_parser import parse_json_response, safe_json_loads
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_json_response — direct parsing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestParseJsonResponseDirect:
|
||||
def test_valid_json_object(self) -> None:
|
||||
assert parse_json_response('{"key": "value"}') == {"key": "value"}
|
||||
|
||||
def test_valid_json_array(self) -> None:
|
||||
assert parse_json_response("[1, 2, 3]") == [1, 2, 3]
|
||||
|
||||
def test_valid_json_string(self) -> None:
|
||||
assert parse_json_response('"hello"') == "hello"
|
||||
|
||||
def test_empty_string_returns_fallback(self) -> None:
|
||||
assert parse_json_response("") == {}
|
||||
|
||||
def test_whitespace_only_returns_fallback(self) -> None:
|
||||
assert parse_json_response(" \n ") == {}
|
||||
|
||||
def test_none_returns_fallback(self) -> None:
|
||||
assert parse_json_response(None) == {} # type: ignore[arg-type]
|
||||
|
||||
def test_explicit_none_fallback(self) -> None:
|
||||
assert parse_json_response("", fallback=None) is None
|
||||
|
||||
def test_custom_fallback(self) -> None:
|
||||
assert parse_json_response("not json", fallback={"default": True}) == {"default": True}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_json_response — markdown extraction
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestParseJsonResponseMarkdown:
|
||||
def test_json_code_block(self) -> None:
|
||||
response = '```json\n{"key": "value"}\n```'
|
||||
assert parse_json_response(response) == {"key": "value"}
|
||||
|
||||
def test_plain_code_block(self) -> None:
|
||||
response = '```\n{"answer": 42}\n```'
|
||||
assert parse_json_response(response) == {"answer": 42}
|
||||
|
||||
def test_code_block_with_surrounding_text(self) -> None:
|
||||
response = 'Here is the result:\n```json\n{"x": 1}\n```\nDone.'
|
||||
assert parse_json_response(response) == {"x": 1}
|
||||
|
||||
def test_nested_backticks_extracts_first(self) -> None:
|
||||
response = '```json\n{"a": 1}\n```\nsome text\n```json\n{"b": 2}\n```'
|
||||
result = parse_json_response(response)
|
||||
assert result == {"a": 1}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_json_response — json-repair
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestParseJsonResponseRepair:
|
||||
def test_trailing_comma_repaired(self) -> None:
|
||||
"""json-repair should handle trailing commas if installed."""
|
||||
response = '{"key": "value",}'
|
||||
result = parse_json_response(response)
|
||||
if result == {}:
|
||||
pytest.skip("json-repair not installed")
|
||||
assert result == {"key": "value"}
|
||||
|
||||
def test_missing_quotes_repaired(self) -> None:
|
||||
response = "{key: value}"
|
||||
result = parse_json_response(response)
|
||||
if result == {}:
|
||||
pytest.skip("json-repair not installed")
|
||||
assert isinstance(result, dict)
|
||||
|
||||
def test_repair_unavailable_returns_fallback(self) -> None:
|
||||
with patch("deeptutor.utils.json_parser.repair_json", None):
|
||||
result = parse_json_response("{bad json", fallback={"err": True})
|
||||
assert result == {"err": True}
|
||||
|
||||
def test_invalid_plain_text_is_not_logged_as_error(self, caplog) -> None:
|
||||
caplog.set_level(logging.ERROR)
|
||||
|
||||
result = parse_json_response("not json", fallback={"default": True})
|
||||
|
||||
assert result == {"default": True}
|
||||
assert not [r for r in caplog.records if r.levelno >= logging.ERROR]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# safe_json_loads
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSafeJsonLoads:
|
||||
def test_valid_json(self) -> None:
|
||||
assert safe_json_loads('{"a": 1}') == {"a": 1}
|
||||
|
||||
def test_invalid_json_returns_default_fallback(self) -> None:
|
||||
assert safe_json_loads("not json") == {}
|
||||
|
||||
def test_invalid_json_returns_custom_fallback(self) -> None:
|
||||
assert safe_json_loads("not json", fallback=[]) == []
|
||||
|
||||
def test_explicit_none_fallback(self) -> None:
|
||||
assert safe_json_loads("bad", fallback=None) is None
|
||||
Reference in New Issue
Block a user