tavalidate-0.0.6/0000755000076500000240000000000014001530205015114 5ustar douglasliustaff00000000000000tavalidate-0.0.6/PKG-INFO0000644000076500000240000000641414001530205016216 0ustar douglasliustaff00000000000000Metadata-Version: 2.1 Name: tavalidate Version: 0.0.6 Summary: Utilities to help you validate and save Tavern response. Home-page: https://github.com/sohoffice/tavalidate Author: Douglas Liu Author-email: douglas@sohoffice.com License: UNKNOWN Description: tavalidate, utilities to help you validate [Tavern](https://tavern.readthedocs.io/en/latest/) response. Installation ------------ Tavalidate can be installed through pip. ``` pip install tavalidate ``` XML Validate ---- Tavern has great built-in Json support, but things are difficult when it comes to XML. Use tavalidate.xmlv package to validate XML response. XML validation example: ``` response: status_code: 200 verify_response_with: function: tavalidate:assert_xml extra_kwargs: expected: | !anyint ``` Simply put, pass the expected xml as an argument to the `tavalidate.xmlv.validate` function. The function will validate the xml structure, node value and attribute value. ### extra_kwargs Below are the supported extra kwargs of `tavalidate.xmlv.validate` function. #### expected This is the expected XML string. You may use some (not all) of the tavern magic values to match data of your specified type: - !anything: This matches value of any type. - !anystr: Matches any string - !anyint: Matches any integer - !anyfloat: Matches any float - !anybool: Matches any boolean #### strict Use `strict: True` if you want to make sure there's no extra tag in the response. #### array Since XML do not have the concept of array. When validating an array-like element, the same number of children of the same tag must exist. Regardless whether strict mode is used or not. All element in corresponding order must match for the container to match. XML Save ---- Use tavalidate.xmlv package to save XML response. It allows you to use [XPath](https://en.wikipedia.org/wiki/XPath) to specify the value to save inside the xml document. If the XPath somehow selects multiple nodes, tavalidate will print a warning, but the first value will still be saved. XML save example: ``` response: save: $ext: function: tavalidate:save_xml extra_kwargs: variables: bar: '/foo/bar/text()' at1: '/foo/@at1' ``` Logging ------- Configure the logger `tavalidate` so you can see the response body and other logs in DEBUG level. Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Description-Content-Type: text/markdown tavalidate-0.0.6/README.md0000644000076500000240000000417614001530020016376 0ustar douglasliustaff00000000000000tavalidate, utilities to help you validate [Tavern](https://tavern.readthedocs.io/en/latest/) response. Installation ------------ Tavalidate can be installed through pip. ``` pip install tavalidate ``` XML Validate ---- Tavern has great built-in Json support, but things are difficult when it comes to XML. Use tavalidate.xmlv package to validate XML response. XML validation example: ``` response: status_code: 200 verify_response_with: function: tavalidate:assert_xml extra_kwargs: expected: | !anyint ``` Simply put, pass the expected xml as an argument to the `tavalidate.xmlv.validate` function. The function will validate the xml structure, node value and attribute value. ### extra_kwargs Below are the supported extra kwargs of `tavalidate.xmlv.validate` function. #### expected This is the expected XML string. You may use some (not all) of the tavern magic values to match data of your specified type: - !anything: This matches value of any type. - !anystr: Matches any string - !anyint: Matches any integer - !anyfloat: Matches any float - !anybool: Matches any boolean #### strict Use `strict: True` if you want to make sure there's no extra tag in the response. #### array Since XML do not have the concept of array. When validating an array-like element, the same number of children of the same tag must exist. Regardless whether strict mode is used or not. All element in corresponding order must match for the container to match. XML Save ---- Use tavalidate.xmlv package to save XML response. It allows you to use [XPath](https://en.wikipedia.org/wiki/XPath) to specify the value to save inside the xml document. If the XPath somehow selects multiple nodes, tavalidate will print a warning, but the first value will still be saved. XML save example: ``` response: save: $ext: function: tavalidate:save_xml extra_kwargs: variables: bar: '/foo/bar/text()' at1: '/foo/@at1' ``` Logging ------- Configure the logger `tavalidate` so you can see the response body and other logs in DEBUG level. tavalidate-0.0.6/setup.cfg0000644000076500000240000000004614001530205016735 0ustar douglasliustaff00000000000000[egg_info] tag_build = tag_date = 0 tavalidate-0.0.6/setup.py0000644000076500000240000000137514001527202016637 0ustar douglasliustaff00000000000000import setuptools with open("README.md", "r") as fh: long_description = fh.read() setuptools.setup( name="tavalidate", version="0.0.6", author="Douglas Liu", author_email="douglas@sohoffice.com", description="Utilities to help you validate and save Tavern response.", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/sohoffice/tavalidate", packages=setuptools.find_packages(), install_requires=[ 'lxml>=4.0.0' ], classifiers=( "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent" ), ) tavalidate-0.0.6/tavalidate/0000755000076500000240000000000014001530205017232 5ustar douglasliustaff00000000000000tavalidate-0.0.6/tavalidate/__init__.py0000644000076500000240000000020513627166017021363 0ustar douglasliustaff00000000000000from tavalidate.xmls import do_save_xml from tavalidate.xmlv import do_assert_xml save_xml = do_save_xml assert_xml = do_assert_xml tavalidate-0.0.6/tavalidate/log.py0000644000076500000240000000007113627221614020401 0ustar douglasliustaff00000000000000import logging logger = logging.getLogger('tavalidate') tavalidate-0.0.6/tavalidate/test_xmls.py0000644000076500000240000000177613627220170021653 0ustar douglasliustaff00000000000000from unittest import TestCase from tavalidate.test_xmlv import DummyResponse from tavalidate.xmls import save_xml class Test(TestCase): def test_save_xml(self): resp = DummyResponse(""" str """) x = save_xml(resp, variables={ 'bar': '/foo/bar/text()', 'at1': '/foo/@at1', 'at2': '/foo/bar/@at2' }) assert x['bar'] == 'str' assert x['at1'] == '1' assert x['at2'] == '2' def test_save_xml_with_encoding_declaration(self): resp = DummyResponse( """ S""") with self.assertRaises(AssertionError): save_xml(resp, variables={ 'notFound': '/not/found' }) x = save_xml(resp, variables={ 'status': '/foo/statusCode/text()' }) assert x['status'] == 'S' tavalidate-0.0.6/tavalidate/test_xmlv.py0000644000076500000240000000772014001530131021635 0ustar douglasliustaff00000000000000from unittest import TestCase from tavalidate.xmlv import assert_xml class Test(TestCase): def test_assert_xml_root(self): resp = DummyResponse(""" """) assert_xml(resp, expected=""" """) # Different only in whitespaces assert_xml(resp, expected="""""") assert_xml(DummyResponse(""""""), expected=""" """) # Expect extra child with self.assertRaises(AssertionError): assert_xml(resp, expected=""" """) # Expect different root tag with self.assertRaises(AssertionError): assert_xml(resp, expected=""" """) # Expect less attribute with self.assertRaises(AssertionError): assert_xml(resp, expected=""" """, strict=True) # Expect extra attribute with self.assertRaises(AssertionError): assert_xml(resp, expected=""" """) # Expect different attribute value with self.assertRaises(AssertionError): assert_xml(resp, expected=""" """) def test_assert_xml_child(self): resp = DummyResponse(""" text """) assert_xml(resp, expected=""" text """) with self.assertRaises(AssertionError): assert_xml(resp, expected=""" text """) with self.assertRaises(AssertionError): assert_xml(resp, expected=""" Different text """) with self.assertRaises(AssertionError): assert_xml(resp, expected=""" """) with self.assertRaises(AssertionError): assert_xml(resp, expected=""" """) assert_xml(resp, expected=""" !anything """) resp2 = DummyResponse(""" True """) assert_xml(resp2, expected=""" !anybool """) def test_assert_xml_array(self): resp = DummyResponse(""" text1 text2 """) assert_xml(resp, expected=""" text1 text2 """) with self.assertRaises(AssertionError): assert_xml(resp, expected=""" text2 text1 """) with self.assertRaises(AssertionError): assert_xml(resp, expected=""" text1 text2 text3 """) class DummyResponse(object): def __init__(self, text): self.text = text tavalidate-0.0.6/tavalidate/xmls.py0000644000076500000240000000212513627220623020604 0ustar douglasliustaff00000000000000from io import BytesIO from lxml import etree from typing import Mapping from box import Box from tavalidate.log import logger def do_save_xml(resp, **kwargs): """ Extract data from response xml body :param resp: :param kwargs.variables: variable name -> xpath mapping. :return: """ source_xml = resp.text logger.debug("Response body: {}".format(source_xml)) variables: Mapping = kwargs['variables'] source_et = etree.parse(BytesIO(bytes(source_xml, "UTF-8"))) assert source_et is not None, "Can not parse response body" data = dict() for var in variables.items(): it = _extract(source_et, var[1]) assert it is not None, "Can not read {} from response".format(var[1]) data[var[0]] = it logger.debug("{} => {}".format(var[0], it)) return Box(data) def _extract(root, xp): ar = root.xpath(xp) if len(ar) <= 0: return None if len(ar) > 1: logger.info("XPath {} produces more than 1 results, the first is used." .format(xp)) return ar[0] save_xml = do_save_xml tavalidate-0.0.6/tavalidate/xmlv.py0000644000076500000240000001012414001527712020601 0ustar douglasliustaff00000000000000import xml.etree.ElementTree as ET from tavalidate.log import logger def do_assert_xml(resp, **kwargs): """ Assert the xml structure in response body is the same as the expected xml. :param resp: :param expected: The expected xml string :param strict: The response xml should not have extra attributes or children :return: """ expected_xml = kwargs['expected'] strict = kwargs.get('strict', False) source_xml = resp.text logger.debug("Response body: {}".format(source_xml)) source_et = ET.fromstring(source_xml) expected_et = ET.fromstring(expected_xml) assert source_et is not None, "Can not parse response body" assert expected_et is not None, "Can not parse expected body" _assert_node(source_et, expected_et, strict) def _assert_node(source, expected, strict): assert source.tag == expected.tag, \ "Tag {} is different from expected {}".format(source.tag, expected.tag) for attr in expected.attrib: assert attr in source.attrib, \ "Attribute {} not found in {}".format(attr, source.tag) assert _assert_value(source.attrib[attr], expected.attrib[attr]), \ "Expecting attribute {}, but get {}".format(expected.attrib[attr], source.attrib[attr]) if strict: for attr in source.attrib: assert attr in expected.attrib, \ "Attribute {} in {} is unexpected and we're in strict mode".format( attr, expected.tag) assert _assert_value(source.text, expected.text), \ "Node value {} not equal to expected {} in {}".format( source.text, expected.text, expected.tag) validated_child_tags = set() for child in expected: if child.tag not in validated_child_tags: validated_child_tags.add(child.tag) expected_children = expected.iter(child.tag) source_children = source.iter(child.tag) expected_list = list(expected_children) source_list = list(source_children) assert source_children is not None, \ "Tag {} not found in {}".format(child.tag, expected.tag) for idx, expected_child in enumerate(expected_list): assert idx < len(source_list), \ "Tag {} inside {} should have {} occurrences, " \ "but {} is found".format(child.tag, source.tag, len(expected_list), len(source_list)) source_child = source_list[idx] _assert_node(source_child, expected_child, strict) if strict: for child in source: expected_child = source.find(child.tag) assert expected_child is not None, \ "Tag {} in {} is unexpected and we're in strict mode.".format( child, expected.tag) def _assert_value(source: str, expected: str): if source is None and expected is None: return True if expected is None and source is not None and not source.strip(): return True if expected is None: return False expected_striped = expected.strip() if source is None and not expected_striped: return True if expected_striped == "!anything": return True elif expected_striped == "!anyint": try: int(source.strip()) return True except ValueError: assert False, "{} is not a integer".format(source) elif expected_striped == "!anyfloat": try: float(source.strip()) return True except ValueError: assert False, "{} is not a float".format(source) elif expected_striped == "!anystr": return True elif expected_striped == "!anybool": try: bool(source.strip()) return True except ValueError: assert False, "{} is not a bool".format(source) if source is None: return False return source.strip() == expected_striped assert_xml = do_assert_xml tavalidate-0.0.6/tavalidate.egg-info/0000755000076500000240000000000014001530205020724 5ustar douglasliustaff00000000000000tavalidate-0.0.6/tavalidate.egg-info/PKG-INFO0000644000076500000240000000641414001530205022026 0ustar douglasliustaff00000000000000Metadata-Version: 2.1 Name: tavalidate Version: 0.0.6 Summary: Utilities to help you validate and save Tavern response. Home-page: https://github.com/sohoffice/tavalidate Author: Douglas Liu Author-email: douglas@sohoffice.com License: UNKNOWN Description: tavalidate, utilities to help you validate [Tavern](https://tavern.readthedocs.io/en/latest/) response. Installation ------------ Tavalidate can be installed through pip. ``` pip install tavalidate ``` XML Validate ---- Tavern has great built-in Json support, but things are difficult when it comes to XML. Use tavalidate.xmlv package to validate XML response. XML validation example: ``` response: status_code: 200 verify_response_with: function: tavalidate:assert_xml extra_kwargs: expected: | !anyint ``` Simply put, pass the expected xml as an argument to the `tavalidate.xmlv.validate` function. The function will validate the xml structure, node value and attribute value. ### extra_kwargs Below are the supported extra kwargs of `tavalidate.xmlv.validate` function. #### expected This is the expected XML string. You may use some (not all) of the tavern magic values to match data of your specified type: - !anything: This matches value of any type. - !anystr: Matches any string - !anyint: Matches any integer - !anyfloat: Matches any float - !anybool: Matches any boolean #### strict Use `strict: True` if you want to make sure there's no extra tag in the response. #### array Since XML do not have the concept of array. When validating an array-like element, the same number of children of the same tag must exist. Regardless whether strict mode is used or not. All element in corresponding order must match for the container to match. XML Save ---- Use tavalidate.xmlv package to save XML response. It allows you to use [XPath](https://en.wikipedia.org/wiki/XPath) to specify the value to save inside the xml document. If the XPath somehow selects multiple nodes, tavalidate will print a warning, but the first value will still be saved. XML save example: ``` response: save: $ext: function: tavalidate:save_xml extra_kwargs: variables: bar: '/foo/bar/text()' at1: '/foo/@at1' ``` Logging ------- Configure the logger `tavalidate` so you can see the response body and other logs in DEBUG level. Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Description-Content-Type: text/markdown tavalidate-0.0.6/tavalidate.egg-info/SOURCES.txt0000644000076500000240000000047214001530205022613 0ustar douglasliustaff00000000000000README.md setup.py tavalidate/__init__.py tavalidate/log.py tavalidate/test_xmls.py tavalidate/test_xmlv.py tavalidate/xmls.py tavalidate/xmlv.py tavalidate.egg-info/PKG-INFO tavalidate.egg-info/SOURCES.txt tavalidate.egg-info/dependency_links.txt tavalidate.egg-info/requires.txt tavalidate.egg-info/top_level.txttavalidate-0.0.6/tavalidate.egg-info/dependency_links.txt0000644000076500000240000000000114001530205024772 0ustar douglasliustaff00000000000000 tavalidate-0.0.6/tavalidate.egg-info/requires.txt0000644000076500000240000000001414001530205023317 0ustar douglasliustaff00000000000000lxml>=4.0.0 tavalidate-0.0.6/tavalidate.egg-info/top_level.txt0000644000076500000240000000001314001530205023450 0ustar douglasliustaff00000000000000tavalidate