From 33a4fb98c6b21146360095502f8555389b8e428d Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Tue, 14 Jul 2015 20:50:09 -0700 Subject: [PATCH] Update unit tests for body matcher. Simplified logic. --- tests/unit/test_matchers.py | 125 ++++++++++++++++++++++++++++-------- vcr/matchers.py | 44 +++++++++---- 2 files changed, 130 insertions(+), 39 deletions(-) diff --git a/tests/unit/test_matchers.py b/tests/unit/test_matchers.py index d4b32de..5efc507 100644 --- a/tests/unit/test_matchers.py +++ b/tests/unit/test_matchers.py @@ -1,5 +1,7 @@ import itertools +import pytest + from vcr import matchers from vcr import request @@ -35,36 +37,105 @@ def test_uri_matcher(): assert matched -def test_body_matcher(): - # raw - req1 = request.Request('POST', 'http://host.com/', '123', {}) - req2 = request.Request('POST', 'http://another-host.com/', '123', {'Some-Header': 'value'}) - assert matchers.body(req1, req2) +req1_body = (b"test" + b"" + b"a1" + b"b2" + b"") +req2_body = (b"test" + b"" + b"b2" + b"a1" + b"") - # application/x-www-form-urlencoded - req1 = request.Request('POST', 'http://host.com/', 'a=1&b=2', {'Content-Type': 'application/x-www-form-urlencoded'}) - req2 = request.Request('POST', 'http://host.com/', 'b=2&a=1', {'Content-Type': 'application/x-www-form-urlencoded'}) - assert matchers.body(req1, req2) - # application/json - req1 = request.Request('POST', 'http://host.com/', '{"a": 1, "b": 2}', {'Content-Type': 'application/json'}) - req2 = request.Request('POST', 'http://host.com/', '{"b": 2, "a": 1}', {'content-type': 'application/json'}) - assert matchers.body(req1, req2) +@pytest.mark.parametrize("r1, r2", [ + ( + request.Request('POST', 'http://host.com/', '123', {}), + request.Request('POST', 'http://another-host.com/', + '123', {'Some-Header': 'value'}) + ), + ( + request.Request('POST', 'http://host.com/', 'a=1&b=2', + {'Content-Type': 'application/x-www-form-urlencoded'}), + request.Request('POST', 'http://host.com/', 'b=2&a=1', + {'Content-Type': 'application/x-www-form-urlencoded'}) + ), + ( + request.Request('POST', 'http://host.com/', '123', {}), + request.Request('POST', 'http://another-host.com/', '123', {'Some-Header': 'value'}) + ), + ( + request.Request( + 'POST', 'http://host.com/', 'a=1&b=2', + {'Content-Type': 'application/x-www-form-urlencoded'} + ), + request.Request( + 'POST', 'http://host.com/', 'b=2&a=1', + {'Content-Type': 'application/x-www-form-urlencoded'} + ) + ), + ( + request.Request( + 'POST', 'http://host.com/', '{"a": 1, "b": 2}', + {'Content-Type': 'application/json'} + ), + request.Request( + 'POST', 'http://host.com/', '{"b": 2, "a": 1}', + {'content-type': 'application/json'} + ) + ), + ( + request.Request( + 'POST', 'http://host.com/', req1_body, + {'User-Agent': 'xmlrpclib', 'Content-Type': 'text/xml'} + ), + request.Request( + 'POST', 'http://host.com/', req2_body, + {'user-agent': 'somexmlrpc', 'content-type': 'text/xml'} + ) + ), + ( + request.Request( + 'POST', 'http://host.com/', + '{"a": 1, "b": 2}', {'Content-Type': 'application/json'} + ), + request.Request( + 'POST', 'http://host.com/', + '{"b": 2, "a": 1}', {'content-type': 'application/json'} + ) + ) +]) +def test_body_matcher_does_match(r1, r2): + assert matchers.body(r1, r2) - # xmlrpc - req1_body = (b"test" - b"" - b"a1" - b"b2" - b"") - req2_body = (b"test" - b"" - b"b2" - b"a1" - b"") - req1 = request.Request('POST', 'http://host.com/', req1_body, {'User-Agent': 'xmlrpclib', 'Content-Type': 'text/xml'}) - req2 = request.Request('POST', 'http://host.com/', req2_body, {'user-agent': 'somexmlrpc', 'content-type': 'text/xml'}) - assert matchers.body(req1, req2) + +@pytest.mark.parametrize("r1, r2", [ + ( + request.Request('POST', 'http://host.com/', '{"a": 1, "b": 2}', {}), + request.Request('POST', 'http://host.com/', '{"b": 2, "a": 1}', {}), + ), + ( + request.Request( + 'POST', 'http://host.com/', + '{"a": 1, "b": 3}', {'Content-Type': 'application/json'} + ), + request.Request( + 'POST', 'http://host.com/', + '{"b": 2, "a": 1}', {'content-type': 'application/json'} + ) + ), + ( + request.Request( + 'POST', 'http://host.com/', req1_body, {'Content-Type': 'text/xml'} + ), + request.Request( + 'POST', 'http://host.com/', req2_body, {'content-type': 'text/xml'} + ) + ) +]) +def test_body_match_does_not_match(r1, r2): + assert not matchers.body(r1, r2) def test_query_matcher(): diff --git a/vcr/matchers.py b/vcr/matchers.py index 39c5949..34bd4e6 100644 --- a/vcr/matchers.py +++ b/vcr/matchers.py @@ -37,19 +37,39 @@ 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 + + +_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'), json.loads), + (lambda request: _xml_header_checker(request) and _xmlrpc_header_checker(request), xmlrpc_client.loads), +) + + +def _identity(x): + return x + + +def _get_transformer(request): + headers = CaseInsensitiveDict(request.headers) + for checker, transformer in _checker_transformer_pairs: + if checker(headers): return transformer + else: + return _identity + + def body(r1, r2): - r1_body = read_body(r1) - r2_body = read_body(r2) - r1_headers = CaseInsensitiveDict(r1.headers) - r2_headers = CaseInsensitiveDict(r2.headers) - if r1_headers.get('Content-Type') == r2_headers.get('Content-Type') == 'application/x-www-form-urlencoded': - return urllib.parse.parse_qs(r1_body) == urllib.parse.parse_qs(r2_body) - if r1_headers.get('Content-Type') == r2_headers.get('Content-Type') == 'application/json': - return json.loads(r1_body) == json.loads(r2_body) - if ('xmlrpc' in r1_headers.get('User-Agent', '') and 'xmlrpc' in r2_headers.get('User-Agent', '') and - r1_headers.get('Content-Type') == r2_headers.get('Content-Type') == 'text/xml'): - return xmlrpc_client.loads(r1_body) == xmlrpc_client.loads(r2_body) - return r1_body == r2_body + 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):