diff --git a/.chronus/changes/python-xml-test-cases-2026-3-19-22-47-48.md b/.chronus/changes/python-xml-test-cases-2026-3-19-22-47-48.md new file mode 100644 index 00000000000..3d855bfa355 --- /dev/null +++ b/.chronus/changes/python-xml-test-cases-2026-3-19-22-47-48.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-python" +--- + +Add mock API test cases for XML scenarios introduced in https://github.com/microsoft/typespec/pull/10063, covering: renamed property, nested model, renamed nested model, wrapped primitive with custom item names, model array variants (wrapped/unwrapped/renamed), renamed attribute, namespace, and namespace-on-properties. Fix XML serialization/deserialization bugs: unwrapped model array element naming, namespace key mismatch between DPG models and the runtime template, default-namespace propagation semantics, and handling of Python-reserved namespace prefixes. diff --git a/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 index 253b0c1c1d8..111a1585faf 100644 --- a/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 +++ b/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 @@ -622,57 +622,9 @@ class Model(_MyMutableMapping): for rest_field in self._attr_to_rest_field.values() if rest_field._default is not _UNSET } - if args: # pylint: disable=too-many-nested-blocks + if args: if isinstance(args[0], ET.Element): - existed_attr_keys = [] - model_meta = getattr(self, "_xml", {}) - - for rf in self._attr_to_rest_field.values(): - prop_meta = getattr(rf, "_xml", {}) - xml_name = prop_meta.get("name", rf._rest_name) - xml_ns = prop_meta.get("ns", model_meta.get("ns", None)) - if xml_ns: - xml_name = "{" + xml_ns + "}" + xml_name - - # attribute - if prop_meta.get("attribute", False) and args[0].get(xml_name) is not None: - existed_attr_keys.append(xml_name) - dict_to_pass[rf._rest_name] = _deserialize(rf._type, args[0].get(xml_name)) - continue - - # unwrapped element is array - if prop_meta.get("unwrapped", False): - # unwrapped array could either use prop items meta/prop meta - if prop_meta.get("itemsName"): - xml_name = prop_meta.get("itemsName") - xml_ns = prop_meta.get("itemNs") - if xml_ns: - xml_name = "{" + xml_ns + "}" + xml_name - items = args[0].findall(xml_name) # pyright: ignore - if len(items) > 0: - existed_attr_keys.append(xml_name) - dict_to_pass[rf._rest_name] = _deserialize(rf._type, items) - elif not rf._is_optional: - existed_attr_keys.append(xml_name) - dict_to_pass[rf._rest_name] = [] - continue - - # text element is primitive type - if prop_meta.get("text", False): - if args[0].text is not None: - dict_to_pass[rf._rest_name] = _deserialize(rf._type, args[0].text) - continue - - # wrapped element could be normal property or array, it should only have one element - item = args[0].find(xml_name) - if item is not None: - existed_attr_keys.append(xml_name) - dict_to_pass[rf._rest_name] = _deserialize(rf._type, item) - - # rest thing is additional properties - for e in args[0]: - if e.tag not in existed_attr_keys: - dict_to_pass[e.tag] = _convert_element(e) + dict_to_pass.update(self._init_from_xml(args[0])) else: dict_to_pass.update( {k: _create_value(_get_rest_field(self._attr_to_rest_field, k), v) for k, v in args[0].items()} @@ -691,6 +643,69 @@ class Model(_MyMutableMapping): ) super().__init__(dict_to_pass) + def _init_from_xml(self, element: ET.Element) -> dict[str, typing.Any]: + """Deserialize an XML element into a dict mapping rest field names to values. + + :param ET.Element element: The XML element to deserialize from. + :returns: A dictionary of rest_name to deserialized value pairs. + :rtype: dict + """ + result: dict[str, typing.Any] = {} + model_meta = getattr(self, "_xml", {}) + existed_attr_keys: list[str] = [] + + for rf in self._attr_to_rest_field.values(): + prop_meta = getattr(rf, "_xml", {}) + xml_name = prop_meta.get("name", rf._rest_name) + xml_ns = _resolve_xml_ns(prop_meta, model_meta) + if xml_ns: + xml_name = "{" + xml_ns + "}" + xml_name + + # attribute + if prop_meta.get("attribute", False) and element.get(xml_name) is not None: + existed_attr_keys.append(xml_name) + result[rf._rest_name] = _deserialize(rf._type, element.get(xml_name)) + continue + + # unwrapped element is array + if prop_meta.get("unwrapped", False): + # unwrapped array could either use prop items meta/prop meta + _items_name = prop_meta.get("itemsName") + if _items_name: + xml_name = _items_name + _items_ns = prop_meta.get("itemsNs") + if _items_ns is not None: + xml_ns = _items_ns + if xml_ns: + xml_name = "{" + xml_ns + "}" + xml_name + items = element.findall(xml_name) # pyright: ignore + if len(items) > 0: + existed_attr_keys.append(xml_name) + result[rf._rest_name] = _deserialize(rf._type, items) + elif not rf._is_optional: + existed_attr_keys.append(xml_name) + result[rf._rest_name] = [] + continue + + # text element is primitive type + if prop_meta.get("text", False): + if element.text is not None: + result[rf._rest_name] = _deserialize(rf._type, element.text) + continue + + # wrapped element could be normal property or array, it should only have one element + item = element.find(xml_name) + if item is not None: + existed_attr_keys.append(xml_name) + result[rf._rest_name] = _deserialize(rf._type, item) + + # rest thing is additional properties + for e in element: + if e.tag not in existed_attr_keys: + result[e.tag] = _convert_element(e) + + return result + def copy(self) -> "Model": return Model(self.__dict__) @@ -761,7 +776,7 @@ class Model(_MyMutableMapping): model_meta = getattr(cls, "_xml", {}) prop_meta = getattr(discriminator, "_xml", {}) xml_name = prop_meta.get("name", discriminator._rest_name) - xml_ns = prop_meta.get("ns", model_meta.get("ns", None)) + xml_ns = _resolve_xml_ns(prop_meta, model_meta) if xml_ns: xml_name = "{" + xml_ns + "}" + xml_name @@ -1243,6 +1258,58 @@ def serialize_xml(model: Model, exclude_readonly: bool = False) -> str: return ET.tostring(_get_element(model, exclude_readonly), encoding="unicode") # type: ignore +def _get_xml_ns(meta: dict[str, typing.Any]) -> typing.Optional[str]: + """Return the XML namespace from a metadata dict, checking both 'ns' (old-style) and 'namespace' (DPG) keys. + + :param dict meta: The metadata dictionary to extract namespace from. + :returns: The namespace string if 'ns' or 'namespace' key is present, None otherwise. + :rtype: str or None + """ + ns = meta.get("ns") + if ns is None: + ns = meta.get("namespace") + return ns + + +def _resolve_xml_ns( + prop_meta: dict[str, typing.Any], model_meta: typing.Optional[dict[str, typing.Any]] = None +) -> typing.Optional[str]: + """Resolve XML namespace for a property, falling back to model namespace when appropriate. + + Checks the property metadata first; if no namespace is found and the model does not declare + an explicit prefix, falls back to the model-level namespace. + + :param dict prop_meta: The property metadata dictionary. + :param dict model_meta: The model metadata dictionary, used as fallback. + :returns: The resolved namespace string, or None. + :rtype: str or None + """ + ns = _get_xml_ns(prop_meta) + if ns is None and model_meta is not None and not model_meta.get("prefix"): + ns = _get_xml_ns(model_meta) + return ns + + +def _set_xml_attribute( + element: ET.Element, name: str, value: typing.Any, prop_meta: dict[str, typing.Any] +) -> None: + """Set an XML attribute on an element, handling namespace prefix registration. + + :param ET.Element element: The element to set the attribute on. + :param str name: The default attribute name (wire name). + :param any value: The attribute value. + :param dict prop_meta: The property metadata dictionary. + """ + xml_name = prop_meta.get("name", name) + _attr_ns = _get_xml_ns(prop_meta) + if _attr_ns: + _attr_prefix = prop_meta.get("prefix") + if _attr_prefix: + _safe_register_namespace(_attr_prefix, _attr_ns) + xml_name = "{" + _attr_ns + "}" + xml_name + element.set(xml_name, _get_primitive_type_value(value)) + + def _get_element( o: typing.Any, exclude_readonly: bool = False, @@ -1254,10 +1321,16 @@ def _get_element( # if prop is a model, then use the prop element directly, else generate a wrapper of model if wrapped_element is None: + # When serializing as an array item (parent_meta is set), check if the parent has an + # explicit itemsName. This ensures correct element names for unwrapped arrays (where + # the element tag is the property/items name, not the model type name). + _items_name = parent_meta.get("itemsName") if parent_meta is not None else None + element_name = _items_name if _items_name else (model_meta.get("name") or o.__class__.__name__) + _model_ns = _get_xml_ns(model_meta) wrapped_element = _create_xml_element( - model_meta.get("name", o.__class__.__name__), + element_name, model_meta.get("prefix"), - model_meta.get("ns"), + _model_ns, ) readonly_props = [] @@ -1279,7 +1352,9 @@ def _get_element( # additional properties will not have rest field, use the wire name as xml name prop_meta = {"name": k} - # if no ns for prop, use model's + # Propagate model namespace to properties only for old-style "ns"-keyed models. + # DPG-generated models use the "namespace" key and explicitly declare namespace on + # each property that needs it, so propagation is intentionally skipped for them. if prop_meta.get("ns") is None and model_meta.get("ns"): prop_meta["ns"] = model_meta.get("ns") prop_meta["prefix"] = model_meta.get("prefix") @@ -1291,12 +1366,7 @@ def _get_element( # text could only set on primitive type wrapped_element.text = _get_primitive_type_value(v) elif prop_meta.get("attribute", False): - xml_name = prop_meta.get("name", k) - if prop_meta.get("ns"): - ET.register_namespace(prop_meta.get("prefix"), prop_meta.get("ns")) # pyright: ignore - xml_name = "{" + prop_meta.get("ns") + "}" + xml_name # pyright: ignore - # attribute should be primitive type - wrapped_element.set(xml_name, _get_primitive_type_value(v)) + _set_xml_attribute(wrapped_element, k, v, prop_meta) else: # other wrapped prop element wrapped_element.append(_get_wrapped_element(v, exclude_readonly, prop_meta)) @@ -1305,6 +1375,7 @@ def _get_element( return [_get_element(x, exclude_readonly, parent_meta) for x in o] # type: ignore if isinstance(o, dict): result = [] + _dict_ns = _get_xml_ns(parent_meta) if parent_meta else None for k, v in o.items(): result.append( _get_wrapped_element( @@ -1312,7 +1383,7 @@ def _get_element( exclude_readonly, { "name": k, - "ns": parent_meta.get("ns") if parent_meta else None, + "ns": _dict_ns, "prefix": parent_meta.get("prefix") if parent_meta else None, }, ) @@ -1321,13 +1392,16 @@ def _get_element( # primitive case need to create element based on parent_meta if parent_meta: + _items_ns = parent_meta.get("itemsNs") + if _items_ns is None: + _items_ns = _get_xml_ns(parent_meta) return _get_wrapped_element( o, exclude_readonly, { "name": parent_meta.get("itemsName", parent_meta.get("name")), "prefix": parent_meta.get("itemsPrefix", parent_meta.get("prefix")), - "ns": parent_meta.get("itemsNs", parent_meta.get("ns")), + "ns": _items_ns, }, ) @@ -1339,8 +1413,9 @@ def _get_wrapped_element( exclude_readonly: bool, meta: typing.Optional[dict[str, typing.Any]], ) -> ET.Element: + _meta_ns = _get_xml_ns(meta) if meta else None wrapped_element = _create_xml_element( - meta.get("name") if meta else None, meta.get("prefix") if meta else None, meta.get("ns") if meta else None + meta.get("name") if meta else None, meta.get("prefix") if meta else None, _meta_ns ) if isinstance(v, (dict, list)): wrapped_element.extend(_get_element(v, exclude_readonly, meta)) @@ -1361,11 +1436,29 @@ def _get_primitive_type_value(v) -> str: return str(v) +def _safe_register_namespace(prefix: str, ns: str) -> None: + """Register an XML namespace prefix, handling reserved prefix patterns. + + Some prefixes (e.g. 'ns2') match Python's reserved 'ns\\d+' pattern used for + auto-generated prefixes, causing register_namespace to raise ValueError. + Falls back to directly registering in the internal namespace map. + + :param str prefix: The namespace prefix to register. + :param str ns: The namespace URI. + """ + try: + ET.register_namespace(prefix, ns) + except ValueError: + _ns_map = getattr(ET, "_namespace_map", None) + if _ns_map is not None: + _ns_map[ns] = prefix + + def _create_xml_element( tag: typing.Any, prefix: typing.Optional[str] = None, ns: typing.Optional[str] = None ) -> ET.Element: if prefix and ns: - ET.register_namespace(prefix, ns) + _safe_register_namespace(prefix, ns) if ns: return ET.Element("{" + ns + "}" + tag) return ET.Element(tag) diff --git a/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_payload_xml_async.py b/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_payload_xml_async.py index 0cfccaee38a..6cf981a0e4f 100644 --- a/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_payload_xml_async.py +++ b/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_payload_xml_async.py @@ -7,12 +7,21 @@ import pytest from payload.xml.aio import XmlClient from payload.xml.models import ( + Author, + Book, SimpleModel, ModelWithSimpleArrays, ModelWithArrayOfModel, ModelWithAttributes, ModelWithUnwrappedArray, + ModelWithUnwrappedModelArray, ModelWithRenamedArrays, + ModelWithRenamedProperty, + ModelWithRenamedAttribute, + ModelWithRenamedNestedModel, + ModelWithRenamedWrappedModelArray, + ModelWithRenamedUnwrappedModelArray, + ModelWithRenamedWrappedAndItemModelArray, ModelWithOptionalField, ModelWithRenamedFields, ModelWithEmptyArray, @@ -21,6 +30,10 @@ ModelWithEncodedNames, ModelWithEnum, ModelWithDatetime, + ModelWithNamespace, + ModelWithNamespaceOnProperties, + ModelWithNestedModel, + ModelWithWrappedPrimitiveCustomItemNames, ) @@ -37,6 +50,13 @@ async def test_simple_model(client: XmlClient): await client.simple_model_value.put(model) +@pytest.mark.asyncio +async def test_model_with_renamed_property(client: XmlClient): + model = ModelWithRenamedProperty(title="foo", author="bar") + assert await client.model_with_renamed_property_value.get() == model + await client.model_with_renamed_property_value.put(model) + + @pytest.mark.asyncio async def test_model_with_simple_arrays(client: XmlClient): model = ModelWithSimpleArrays(colors=["red", "green", "blue"], counts=[1, 2]) @@ -44,6 +64,13 @@ async def test_model_with_simple_arrays(client: XmlClient): await client.model_with_simple_arrays_value.put(model) +@pytest.mark.asyncio +async def test_model_with_wrapped_primitive_custom_item_names(client: XmlClient): + model = ModelWithWrappedPrimitiveCustomItemNames(tags=["fiction", "classic"]) + assert await client.model_with_wrapped_primitive_custom_item_names_value.get() == model + await client.model_with_wrapped_primitive_custom_item_names_value.put(model) + + @pytest.mark.asyncio async def test_model_with_array_of_model(client: XmlClient): model = ModelWithArrayOfModel( @@ -56,6 +83,54 @@ async def test_model_with_array_of_model(client: XmlClient): await client.model_with_array_of_model_value.put(model) +@pytest.mark.asyncio +async def test_model_with_unwrapped_model_array(client: XmlClient): + model = ModelWithUnwrappedModelArray( + items_property=[ + SimpleModel(name="foo", age=123), + SimpleModel(name="bar", age=456), + ] + ) + assert await client.model_with_unwrapped_model_array_value.get() == model + await client.model_with_unwrapped_model_array_value.put(model) + + +@pytest.mark.asyncio +async def test_model_with_renamed_wrapped_model_array(client: XmlClient): + model = ModelWithRenamedWrappedModelArray( + items_property=[ + SimpleModel(name="foo", age=123), + SimpleModel(name="bar", age=456), + ] + ) + assert await client.model_with_renamed_wrapped_model_array_value.get() == model + await client.model_with_renamed_wrapped_model_array_value.put(model) + + +@pytest.mark.asyncio +async def test_model_with_renamed_unwrapped_model_array(client: XmlClient): + model = ModelWithRenamedUnwrappedModelArray( + items_property=[ + SimpleModel(name="foo", age=123), + SimpleModel(name="bar", age=456), + ] + ) + assert await client.model_with_renamed_unwrapped_model_array_value.get() == model + await client.model_with_renamed_unwrapped_model_array_value.put(model) + + +@pytest.mark.asyncio +async def test_model_with_renamed_wrapped_and_item_model_array(client: XmlClient): + model = ModelWithRenamedWrappedAndItemModelArray( + books=[ + Book(title="The Great Gatsby"), + Book(title="Les Miserables"), + ] + ) + assert await client.model_with_renamed_wrapped_and_item_model_array_value.get() == model + await client.model_with_renamed_wrapped_and_item_model_array_value.put(model) + + @pytest.mark.asyncio async def test_model_with_attributes(client: XmlClient): model = ModelWithAttributes(id1=123, id2="foo", enabled=True) @@ -63,6 +138,13 @@ async def test_model_with_attributes(client: XmlClient): await client.model_with_attributes_value.put(model) +@pytest.mark.asyncio +async def test_model_with_renamed_attribute(client: XmlClient): + model = ModelWithRenamedAttribute(id=123, title="The Great Gatsby", author="F. Scott Fitzgerald") + assert await client.model_with_renamed_attribute_value.get() == model + await client.model_with_renamed_attribute_value.put(model) + + @pytest.mark.asyncio async def test_model_with_unwrapped_array(client: XmlClient): model = ModelWithUnwrappedArray(colors=["red", "green", "blue"], counts=[1, 2]) @@ -84,6 +166,20 @@ async def test_model_with_optional_field(client: XmlClient): await client.model_with_optional_field_value.put(model) +@pytest.mark.asyncio +async def test_model_with_nested_model(client: XmlClient): + model = ModelWithNestedModel(nested=SimpleModel(name="foo", age=123)) + assert await client.model_with_nested_model_value.get() == model + await client.model_with_nested_model_value.put(model) + + +@pytest.mark.asyncio +async def test_model_with_renamed_nested_model(client: XmlClient): + model = ModelWithRenamedNestedModel(author=Author(name="foo")) + assert await client.model_with_renamed_nested_model_value.get() == model + await client.model_with_renamed_nested_model_value.put(model) + + @pytest.mark.asyncio async def test_model_with_renamed_fields(client: XmlClient): model = ModelWithRenamedFields( @@ -141,6 +237,20 @@ async def test_model_with_datetime(client: XmlClient): await client.model_with_datetime_value.put(model) +@pytest.mark.asyncio +async def test_model_with_namespace(client: XmlClient): + model = ModelWithNamespace(id=123, title="The Great Gatsby") + assert await client.model_with_namespace_value.get() == model + await client.model_with_namespace_value.put(model) + + +@pytest.mark.asyncio +async def test_model_with_namespace_on_properties(client: XmlClient): + model = ModelWithNamespaceOnProperties(id=123, title="The Great Gatsby", author="F. Scott Fitzgerald") + assert await client.model_with_namespace_on_properties_value.get() == model + await client.model_with_namespace_on_properties_value.put(model) + + @pytest.mark.asyncio async def test_xml_error_value(client: XmlClient, core_library): with pytest.raises(core_library.exceptions.HttpResponseError) as ex: diff --git a/packages/http-client-python/generator/test/generic_mock_api_tests/test_payload_xml.py b/packages/http-client-python/generator/test/generic_mock_api_tests/test_payload_xml.py index 22f1c6f7c79..e301ff0a1e5 100644 --- a/packages/http-client-python/generator/test/generic_mock_api_tests/test_payload_xml.py +++ b/packages/http-client-python/generator/test/generic_mock_api_tests/test_payload_xml.py @@ -7,12 +7,21 @@ import pytest from payload.xml import XmlClient from payload.xml.models import ( + Author, + Book, SimpleModel, ModelWithSimpleArrays, ModelWithArrayOfModel, ModelWithAttributes, ModelWithUnwrappedArray, + ModelWithUnwrappedModelArray, ModelWithRenamedArrays, + ModelWithRenamedProperty, + ModelWithRenamedAttribute, + ModelWithRenamedNestedModel, + ModelWithRenamedWrappedModelArray, + ModelWithRenamedUnwrappedModelArray, + ModelWithRenamedWrappedAndItemModelArray, ModelWithOptionalField, ModelWithRenamedFields, ModelWithEmptyArray, @@ -21,6 +30,10 @@ ModelWithEncodedNames, ModelWithEnum, ModelWithDatetime, + ModelWithNamespace, + ModelWithNamespaceOnProperties, + ModelWithNestedModel, + ModelWithWrappedPrimitiveCustomItemNames, ) @@ -36,12 +49,24 @@ def test_simple_model(client: XmlClient): client.simple_model_value.put(model) +def test_model_with_renamed_property(client: XmlClient): + model = ModelWithRenamedProperty(title="foo", author="bar") + assert client.model_with_renamed_property_value.get() == model + client.model_with_renamed_property_value.put(model) + + def test_model_with_simple_arrays(client: XmlClient): model = ModelWithSimpleArrays(colors=["red", "green", "blue"], counts=[1, 2]) assert client.model_with_simple_arrays_value.get() == model client.model_with_simple_arrays_value.put(model) +def test_model_with_wrapped_primitive_custom_item_names(client: XmlClient): + model = ModelWithWrappedPrimitiveCustomItemNames(tags=["fiction", "classic"]) + assert client.model_with_wrapped_primitive_custom_item_names_value.get() == model + client.model_with_wrapped_primitive_custom_item_names_value.put(model) + + def test_model_with_array_of_model(client: XmlClient): model = ModelWithArrayOfModel( items_property=[ @@ -53,12 +78,62 @@ def test_model_with_array_of_model(client: XmlClient): client.model_with_array_of_model_value.put(model) +def test_model_with_unwrapped_model_array(client: XmlClient): + model = ModelWithUnwrappedModelArray( + items_property=[ + SimpleModel(name="foo", age=123), + SimpleModel(name="bar", age=456), + ] + ) + assert client.model_with_unwrapped_model_array_value.get() == model + client.model_with_unwrapped_model_array_value.put(model) + + +def test_model_with_renamed_wrapped_model_array(client: XmlClient): + model = ModelWithRenamedWrappedModelArray( + items_property=[ + SimpleModel(name="foo", age=123), + SimpleModel(name="bar", age=456), + ] + ) + assert client.model_with_renamed_wrapped_model_array_value.get() == model + client.model_with_renamed_wrapped_model_array_value.put(model) + + +def test_model_with_renamed_unwrapped_model_array(client: XmlClient): + model = ModelWithRenamedUnwrappedModelArray( + items_property=[ + SimpleModel(name="foo", age=123), + SimpleModel(name="bar", age=456), + ] + ) + assert client.model_with_renamed_unwrapped_model_array_value.get() == model + client.model_with_renamed_unwrapped_model_array_value.put(model) + + +def test_model_with_renamed_wrapped_and_item_model_array(client: XmlClient): + model = ModelWithRenamedWrappedAndItemModelArray( + books=[ + Book(title="The Great Gatsby"), + Book(title="Les Miserables"), + ] + ) + assert client.model_with_renamed_wrapped_and_item_model_array_value.get() == model + client.model_with_renamed_wrapped_and_item_model_array_value.put(model) + + def test_model_with_attributes(client: XmlClient): model = ModelWithAttributes(id1=123, id2="foo", enabled=True) assert client.model_with_attributes_value.get() == model client.model_with_attributes_value.put(model) +def test_model_with_renamed_attribute(client: XmlClient): + model = ModelWithRenamedAttribute(id=123, title="The Great Gatsby", author="F. Scott Fitzgerald") + assert client.model_with_renamed_attribute_value.get() == model + client.model_with_renamed_attribute_value.put(model) + + def test_model_with_unwrapped_array(client: XmlClient): model = ModelWithUnwrappedArray(colors=["red", "green", "blue"], counts=[1, 2]) assert client.model_with_unwrapped_array_value.get() == model @@ -77,6 +152,18 @@ def test_model_with_optional_field(client: XmlClient): client.model_with_optional_field_value.put(model) +def test_model_with_nested_model(client: XmlClient): + model = ModelWithNestedModel(nested=SimpleModel(name="foo", age=123)) + assert client.model_with_nested_model_value.get() == model + client.model_with_nested_model_value.put(model) + + +def test_model_with_renamed_nested_model(client: XmlClient): + model = ModelWithRenamedNestedModel(author=Author(name="foo")) + assert client.model_with_renamed_nested_model_value.get() == model + client.model_with_renamed_nested_model_value.put(model) + + def test_model_with_renamed_fields(client: XmlClient): model = ModelWithRenamedFields( input_data=SimpleModel(name="foo", age=123), @@ -127,6 +214,18 @@ def test_model_with_datetime(client: XmlClient): client.model_with_datetime_value.put(model) +def test_model_with_namespace(client: XmlClient): + model = ModelWithNamespace(id=123, title="The Great Gatsby") + assert client.model_with_namespace_value.get() == model + client.model_with_namespace_value.put(model) + + +def test_model_with_namespace_on_properties(client: XmlClient): + model = ModelWithNamespaceOnProperties(id=123, title="The Great Gatsby", author="F. Scott Fitzgerald") + assert client.model_with_namespace_on_properties_value.get() == model + client.model_with_namespace_on_properties_value.put(model) + + def test_xml_error_value(client: XmlClient, core_library): with pytest.raises(core_library.exceptions.HttpResponseError) as ex: client.xml_error_value.get()