mirror of
https://github.com/kevin1024/vcrpy.git
synced 2025-12-09 01:03:24 +00:00
A matcher can now return other results than a boolean : - An AssertionError exception meaning that the matcher failed, with the exception we get the assertion failure message. - None, in case we do an assert in the matcher, meaning that the assertion has passed, the matcher is considered as a success then. - Boolean that indicates if a matcher failed or not. If there is no match, a boolean does not give any clue what it is the differences compared to the assertion.
139 lines
3.5 KiB
Python
139 lines
3.5 KiB
Python
import json
|
|
from six.moves import urllib, xmlrpc_client
|
|
from .util import read_body
|
|
import logging
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def method(r1, r2):
|
|
return r1.method == r2.method
|
|
|
|
|
|
def uri(r1, r2):
|
|
return r1.uri == r2.uri
|
|
|
|
|
|
def host(r1, r2):
|
|
return r1.host == r2.host
|
|
|
|
|
|
def scheme(r1, r2):
|
|
return r1.scheme == r2.scheme
|
|
|
|
|
|
def port(r1, r2):
|
|
return r1.port == r2.port
|
|
|
|
|
|
def path(r1, r2):
|
|
return r1.path == r2.path
|
|
|
|
|
|
def query(r1, r2):
|
|
return r1.query == r2.query
|
|
|
|
|
|
def raw_body(r1, r2):
|
|
return read_body(r1) == read_body(r2)
|
|
|
|
|
|
def _header_checker(value, header='Content-Type'):
|
|
def checker(headers):
|
|
return value in headers.get(header, '').lower()
|
|
return checker
|
|
|
|
|
|
def _transform_json(body):
|
|
# Request body is always a byte string, but json.loads() wants a text
|
|
# string. RFC 7159 says the default encoding is UTF-8 (although UTF-16
|
|
# and UTF-32 are also allowed: hmmmmm).
|
|
if body:
|
|
return json.loads(body.decode('utf-8'))
|
|
|
|
|
|
_xml_header_checker = _header_checker('text/xml')
|
|
_xmlrpc_header_checker = _header_checker('xmlrpc', header='User-Agent')
|
|
_checker_transformer_pairs = (
|
|
(_header_checker('application/x-www-form-urlencoded'), urllib.parse.parse_qs),
|
|
(_header_checker('application/json'), _transform_json),
|
|
(lambda request: _xml_header_checker(request) and _xmlrpc_header_checker(request), xmlrpc_client.loads),
|
|
)
|
|
|
|
|
|
def _identity(x):
|
|
return x
|
|
|
|
|
|
def _get_transformer(request):
|
|
for checker, transformer in _checker_transformer_pairs:
|
|
if checker(request.headers):
|
|
return transformer
|
|
else:
|
|
return _identity
|
|
|
|
|
|
def body(r1, r2):
|
|
transformer = _get_transformer(r1)
|
|
r2_transformer = _get_transformer(r2)
|
|
if transformer != r2_transformer:
|
|
transformer = _identity
|
|
return transformer(read_body(r1)) == transformer(read_body(r2))
|
|
|
|
|
|
def headers(r1, r2):
|
|
return r1.headers == r2.headers
|
|
|
|
|
|
def _log_matches(r1, r2, matches):
|
|
differences = [m for m in matches if not m[0]]
|
|
if differences:
|
|
log.debug(
|
|
"Requests {} and {} differ according to "
|
|
"the following matchers: {}".format(r1, r2, differences)
|
|
)
|
|
|
|
|
|
def requests_match(r1, r2, matchers):
|
|
matches = [(m(r1, r2), m) for m in matchers]
|
|
_log_matches(r1, r2, matches)
|
|
return all(m[0] for m in matches)
|
|
|
|
|
|
def _evaluate_matcher(matcher_function, *args):
|
|
"""
|
|
Evaluate the result of a given matcher as a boolean with an assertion error message if any.
|
|
It handles two types of matcher :
|
|
- a matcher returning a boolean value.
|
|
- a matcher that only makes an assert, returning None or raises an assertion error.
|
|
"""
|
|
assertion_message = None
|
|
try:
|
|
match = matcher_function(*args)
|
|
match = True if match is None else match
|
|
except AssertionError as e:
|
|
match = False
|
|
assertion_message = str(e)
|
|
return match, assertion_message
|
|
|
|
|
|
def get_assertion_message(assertion_details, **format_options):
|
|
"""
|
|
Get a detailed message about the failing matcher.
|
|
"""
|
|
msg = ""
|
|
if assertion_details:
|
|
separator = format_options.get("separator", "-")
|
|
title = format_options.get("title", " DETAILS ")
|
|
nb_separator = format_options.get("nb_separator", 40)
|
|
first_title_line = (
|
|
separator * ((nb_separator - len(title)) // 2)
|
|
+ title
|
|
+ separator * ((nb_separator - len(title)) // 2)
|
|
)
|
|
msg += "{}\n{}\n{}\n".format(
|
|
first_title_line, str(assertion_details), separator * nb_separator
|
|
)
|
|
return msg
|