|
| 1 | +import decimal |
| 2 | +from typing import Any |
| 3 | + |
| 4 | +import pytest |
| 5 | + |
| 6 | +from json2xml import dicttoxml |
| 7 | + |
| 8 | + |
| 9 | +class TestAdditionalCoverage: |
| 10 | + def test_wrap_cdata_handles_cdata_end(self) -> None: |
| 11 | + # Ensure CDATA splitting works for "]]>" sequence |
| 12 | + text = "a]]>b" |
| 13 | + wrapped = dicttoxml.wrap_cdata(text) |
| 14 | + assert wrapped == "<![CDATA[a]]]]><![CDATA[>b]]>" |
| 15 | + |
| 16 | + def test_make_valid_xml_name_with_int_key(self) -> None: |
| 17 | + # Int keys should be converted to n<digits> |
| 18 | + key, attr = dicttoxml.make_valid_xml_name(123, {}) # type: ignore[arg-type] |
| 19 | + assert key == "n123" |
| 20 | + assert attr == {} |
| 21 | + |
| 22 | + def test_make_valid_xml_name_namespace_flat(self) -> None: |
| 23 | + # Namespaced key with @flat suffix should be considered valid as-is |
| 24 | + key_in = "ns:key@flat" |
| 25 | + key_out, attr = dicttoxml.make_valid_xml_name(key_in, {}) |
| 26 | + assert key_out == key_in |
| 27 | + assert attr == {} |
| 28 | + |
| 29 | + def test_dict2xml_str_parent_list_with_attrs_and_no_wrap(self) -> None: |
| 30 | + # When inside list context with list_headers=True and item_wrap=False, |
| 31 | + # attributes belong to the parent element header |
| 32 | + item = {"@attrs": {"a": "b"}, "@val": "X"} |
| 33 | + xml = dicttoxml.dict2xml_str( |
| 34 | + attr_type=False, |
| 35 | + attr={}, |
| 36 | + item=item, |
| 37 | + item_func=lambda _p: "item", |
| 38 | + cdata=False, |
| 39 | + item_name="ignored", |
| 40 | + item_wrap=False, |
| 41 | + parentIsList=True, |
| 42 | + parent="Parent", |
| 43 | + list_headers=True, |
| 44 | + ) |
| 45 | + assert xml == '<Parent a="b">X</Parent>' |
| 46 | + |
| 47 | + def test_dict2xml_str_with_flat_flag_in_item(self) -> None: |
| 48 | + # If @flat=True, the subtree should not be wrapped |
| 49 | + item = {"@val": "text", "@flat": True} |
| 50 | + xml = dicttoxml.dict2xml_str( |
| 51 | + attr_type=False, |
| 52 | + attr={}, |
| 53 | + item=item, |
| 54 | + item_func=lambda _p: "item", |
| 55 | + cdata=False, |
| 56 | + item_name="ignored", |
| 57 | + item_wrap=True, |
| 58 | + parentIsList=False, |
| 59 | + ) |
| 60 | + assert xml == "text" |
| 61 | + |
| 62 | + def test_list2xml_str_returns_subtree_when_list_headers_true(self) -> None: |
| 63 | + # list_headers=True should return subtree directly from convert_list |
| 64 | + xml = dicttoxml.list2xml_str( |
| 65 | + attr_type=False, |
| 66 | + attr={}, |
| 67 | + item=["a"], |
| 68 | + item_func=lambda _p: "item", |
| 69 | + cdata=False, |
| 70 | + item_name="test", |
| 71 | + item_wrap=True, |
| 72 | + list_headers=True, |
| 73 | + ) |
| 74 | + assert xml == "<item>a</item>" |
| 75 | + |
| 76 | + def test_get_xml_type_with_decimal_number(self) -> None: |
| 77 | + # Decimal is a numbers.Number but not int/float |
| 78 | + value = decimal.Decimal("5") |
| 79 | + assert dicttoxml.get_xml_type(value) == "number" |
| 80 | + # And convert_kv should mark it as type="number" |
| 81 | + out = dicttoxml.convert_kv("key", value, attr_type=True) |
| 82 | + assert out == '<key type="number">5</key>' |
| 83 | + |
| 84 | + def test_dicttoxml_cdata_with_cdata_end_sequence(self) -> None: |
| 85 | + data = {"key": "a]]>b"} |
| 86 | + out = dicttoxml.dicttoxml(data, root=False, attr_type=False, cdata=True).decode() |
| 87 | + assert out == "<key><![CDATA[a]]]]><![CDATA[>b]]></key>" |
| 88 | + |
| 89 | + def test_convert_dict_with_ids_adds_id_attributes(self) -> None: |
| 90 | + obj: dict[str, Any] = {"a": 1, "b": 2} |
| 91 | + xml = dicttoxml.convert_dict( |
| 92 | + obj=obj, |
| 93 | + ids=["seed"], |
| 94 | + parent="root", |
| 95 | + attr_type=False, |
| 96 | + item_func=lambda _p: "item", |
| 97 | + cdata=False, |
| 98 | + item_wrap=True, |
| 99 | + ) |
| 100 | + # Both elements should carry some id attribute |
| 101 | + assert xml.count(' id="') == 2 |
0 commit comments