import itertools import mock import pytest from vcr import matchers from vcr import request # the dict contains requests with corresponding to its key difference # with 'base' request. REQUESTS = { "base": request.Request("GET", "http://host.com/p?a=b", "", {}), "method": request.Request("POST", "http://host.com/p?a=b", "", {}), "scheme": request.Request("GET", "https://host.com:80/p?a=b", "", {}), "host": request.Request("GET", "http://another-host.com/p?a=b", "", {}), "port": request.Request("GET", "http://host.com:90/p?a=b", "", {}), "path": request.Request("GET", "http://host.com/x?a=b", "", {}), "query": request.Request("GET", "http://host.com/p?c=d", "", {}), } def assert_matcher(matcher_name): matcher = getattr(matchers, matcher_name) for k1, k2 in itertools.permutations(REQUESTS, 2): expecting_assertion_error = matcher_name in {k1, k2} if expecting_assertion_error: with pytest.raises(AssertionError): matcher(REQUESTS[k1], REQUESTS[k2]) else: assert matcher(REQUESTS[k1], REQUESTS[k2]) is None def test_uri_matcher(): for k1, k2 in itertools.permutations(REQUESTS, 2): expecting_assertion_error = {k1, k2} != {"base", "method"} if expecting_assertion_error: with pytest.raises(AssertionError): matchers.uri(REQUESTS[k1], REQUESTS[k2]) else: assert matchers.uri(REQUESTS[k1], REQUESTS[k2]) is None req1_body = ( b"test" b"" b"a1" b"b2" b"" ) req2_body = ( b"test" b"" b"b2" b"a1" b"" ) boto3_bytes_headers = { "X-Amz-Content-SHA256": b"UNSIGNED-PAYLOAD", "Cache-Control": b"max-age=31536000, public", "X-Amz-Date": b"20191102T143910Z", "User-Agent": b"Boto3/1.9.102 Python/3.5.3 Linux/4.15.0-54-generic Botocore/1.12.253 Resource", "Content-MD5": b"GQqjEXsRqrPyxfTl99nkAg==", "Content-Type": b"text/plain", "Expect": b"100-continue", "Content-Length": "21", } @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"} ), ), ( # special case for boto3 bytes headers request.Request("POST", "http://aws.custom.com/", b"123", boto3_bytes_headers), request.Request("POST", "http://aws.custom.com/", b"123", boto3_bytes_headers), ), ], ) def test_body_matcher_does_match(r1, r2): assert matchers.body(r1, r2) is None @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): with pytest.raises(AssertionError): matchers.body(r1, r2) def test_query_matcher(): req1 = request.Request("GET", "http://host.com/?a=b&c=d", "", {}) req2 = request.Request("GET", "http://host.com/?c=d&a=b", "", {}) assert matchers.query(req1, req2) is None req1 = request.Request("GET", "http://host.com/?a=b&a=b&c=d", "", {}) req2 = request.Request("GET", "http://host.com/?a=b&c=d&a=b", "", {}) req3 = request.Request("GET", "http://host.com/?c=d&a=b&a=b", "", {}) assert matchers.query(req1, req2) is None assert matchers.query(req1, req3) is None def test_matchers(): assert_matcher("method") assert_matcher("scheme") assert_matcher("host") assert_matcher("port") assert_matcher("path") assert_matcher("query") def test_evaluate_matcher_does_match(): def bool_matcher(r1, r2): return True def assertion_matcher(r1, r2): assert 1 == 1 r1, r2 = None, None for matcher in [bool_matcher, assertion_matcher]: match, assertion_msg = matchers._evaluate_matcher(matcher, r1, r2) assert match is True assert assertion_msg is None def test_evaluate_matcher_does_not_match(): def bool_matcher(r1, r2): return False def assertion_matcher(r1, r2): # This is like the "assert" statement preventing pytest to recompile it raise AssertionError() r1, r2 = None, None for matcher in [bool_matcher, assertion_matcher]: match, assertion_msg = matchers._evaluate_matcher(matcher, r1, r2) assert match is False assert not assertion_msg def test_evaluate_matcher_does_not_match_with_assert_message(): def assertion_matcher(r1, r2): # This is like the "assert" statement preventing pytest to recompile it raise AssertionError("Failing matcher") r1, r2 = None, None match, assertion_msg = matchers._evaluate_matcher(assertion_matcher, r1, r2) assert match is False assert assertion_msg == "Failing matcher" def test_get_assertion_message(): assert matchers.get_assertion_message(None) is None assert matchers.get_assertion_message("") == "" def test_get_assertion_message_with_details(): assertion_msg = "q1=1 != q2=1" expected = assertion_msg assert matchers.get_assertion_message(assertion_msg) == expected @pytest.mark.parametrize( "r1, r2, expected_successes, expected_failures", [ ( request.Request("GET", "http://host.com/p?a=b", "", {}), request.Request("GET", "http://host.com/p?a=b", "", {}), ["method", "path"], [], ), ( request.Request("GET", "http://host.com/p?a=b", "", {}), request.Request("POST", "http://host.com/p?a=b", "", {}), ["path"], ["method"], ), ( request.Request("GET", "http://host.com/p?a=b", "", {}), request.Request("POST", "http://host.com/path?a=b", "", {}), [], ["method", "path"], ), ], ) def test_get_matchers_results(r1, r2, expected_successes, expected_failures): successes, failures = matchers.get_matchers_results(r1, r2, [matchers.method, matchers.path]) assert successes == expected_successes assert len(failures) == len(expected_failures) for i, expected_failure in enumerate(expected_failures): assert failures[i][0] == expected_failure assert failures[i][1] is not None @mock.patch("vcr.matchers.get_matchers_results") @pytest.mark.parametrize( "successes, failures, expected_match", [(["method", "path"], [], True), (["method"], ["path"], False), ([], ["method", "path"], False)], ) def test_requests_match(mock_get_matchers_results, successes, failures, expected_match): mock_get_matchers_results.return_value = (successes, failures) r1 = request.Request("GET", "http://host.com/p?a=b", "", {}) r2 = request.Request("GET", "http://host.com/p?a=b", "", {}) match = matchers.requests_match(r1, r2, [matchers.method, matchers.path]) assert match is expected_match