diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/mitmproxy/addons/test_export.py | 141 | ||||
-rw-r--r-- | test/mitmproxy/addons/test_serverplayback.py | 34 | ||||
-rw-r--r-- | test/mitmproxy/contentviews/test_protobuf.py | 6 | ||||
-rw-r--r-- | test/mitmproxy/contentviews/test_protobuf_data/protobuf01.bin (renamed from test/mitmproxy/contentviews/test_protobuf_data/protobuf01) | 0 | ||||
-rw-r--r-- | test/mitmproxy/contentviews/test_protobuf_data/protobuf02-decoded.bin (renamed from test/mitmproxy/contentviews/test_protobuf_data/protobuf02-decoded) | 0 | ||||
-rw-r--r-- | test/mitmproxy/contentviews/test_protobuf_data/protobuf02.bin (renamed from test/mitmproxy/contentviews/test_protobuf_data/protobuf02) | bin | 213 -> 213 bytes | |||
-rw-r--r-- | test/mitmproxy/contentviews/test_protobuf_data/protobuf03-decoded.bin (renamed from test/mitmproxy/contentviews/test_protobuf_data/protobuf03-decoded) | 0 | ||||
-rw-r--r-- | test/mitmproxy/contentviews/test_protobuf_data/protobuf03.bin (renamed from test/mitmproxy/contentviews/test_protobuf_data/protobuf03) | 0 | ||||
-rw-r--r-- | test/mitmproxy/data/dumpfile-010.bin (renamed from test/mitmproxy/data/dumpfile-010) | bin | 2140 -> 2140 bytes | |||
-rw-r--r-- | test/mitmproxy/data/dumpfile-011.bin (renamed from test/mitmproxy/data/dumpfile-011) | 0 | ||||
-rw-r--r-- | test/mitmproxy/data/dumpfile-018.bin (renamed from test/mitmproxy/data/dumpfile-018) | 0 | ||||
-rw-r--r-- | test/mitmproxy/io/test_compat.py | 6 | ||||
-rw-r--r-- | test/mitmproxy/proxy/test_config.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/test_proxy.py | 15 | ||||
-rw-r--r-- | test/release/test_cibuild.py | 64 |
15 files changed, 220 insertions, 48 deletions
diff --git a/test/mitmproxy/addons/test_export.py b/test/mitmproxy/addons/test_export.py index c86e0c7d..b0e5e47e 100644 --- a/test/mitmproxy/addons/test_export.py +++ b/test/mitmproxy/addons/test_export.py @@ -1,4 +1,5 @@ import os +import shlex import pytest import pyperclip @@ -18,6 +19,19 @@ def get_request(): @pytest.fixture +def get_response(): + return tflow.tflow( + resp=tutils.tresp(status_code=404, content=b"Test Response Body")) + + +@pytest.fixture +def get_flow(): + return tflow.tflow( + req=tutils.treq(method=b'GET', content=b'', path=b"/path?a=foo&a=bar&b=baz"), + resp=tutils.tresp(status_code=404, content=b"Test Response Body")) + + +@pytest.fixture def post_request(): return tflow.tflow( req=tutils.treq(method=b'POST', headers=(), content=bytes(range(256)))) @@ -41,51 +55,136 @@ def tcp_flow(): class TestExportCurlCommand: def test_get(self, get_request): - result = """curl -H 'header:qvalue' 'http://address:22/path?a=foo&a=bar&b=baz'""" + result = """curl -H 'header: qvalue' 'http://address:22/path?a=foo&a=bar&b=baz'""" assert export.curl_command(get_request) == result def test_post(self, post_request): - result = "curl -H 'content-length:256' -X POST 'http://address:22/path' --data-binary '{}'".format( - str(bytes(range(256)))[2:-1] - ) + post_request.request.content = b'nobinarysupport' + result = "curl -X POST http://address:22/path -d nobinarysupport" assert export.curl_command(post_request) == result + def test_fails_with_binary_data(self, post_request): + # shlex.quote doesn't support a bytes object + # see https://github.com/python/cpython/pull/10871 + post_request.request.headers["Content-Type"] = "application/json; charset=utf-8" + with pytest.raises(exceptions.CommandError): + export.curl_command(post_request) + def test_patch(self, patch_request): - result = """curl -H 'header:qvalue' -H 'content-length:7' -X PATCH 'http://address:22/path?query=param' --data-binary 'content'""" + result = """curl -H 'header: qvalue' -X PATCH 'http://address:22/path?query=param' -d content""" assert export.curl_command(patch_request) == result def test_tcp(self, tcp_flow): with pytest.raises(exceptions.CommandError): export.curl_command(tcp_flow) + def test_escape_single_quotes_in_body(self): + request = tflow.tflow( + req=tutils.treq( + method=b'POST', + headers=(), + content=b"'&#" + ) + ) + command = export.curl_command(request) + assert shlex.split(command)[-2] == '-d' + assert shlex.split(command)[-1] == "'&#" + + def test_strip_unnecessary(self, get_request): + get_request.request.headers.clear() + get_request.request.headers["host"] = "address" + get_request.request.headers[":authority"] = "address" + get_request.request.headers["accept-encoding"] = "br" + result = """curl --compressed 'http://address:22/path?a=foo&a=bar&b=baz'""" + assert export.curl_command(get_request) == result + class TestExportHttpieCommand: def test_get(self, get_request): - result = """http GET http://address:22/path?a=foo&a=bar&b=baz 'header:qvalue'""" + result = """http GET 'http://address:22/path?a=foo&a=bar&b=baz' 'header: qvalue'""" assert export.httpie_command(get_request) == result def test_post(self, post_request): - result = "http POST http://address:22/path 'content-length:256' <<< '{}'".format( - str(bytes(range(256)))[2:-1] - ) + post_request.request.content = b'nobinarysupport' + result = "http POST http://address:22/path <<< nobinarysupport" assert export.httpie_command(post_request) == result + def test_fails_with_binary_data(self, post_request): + # shlex.quote doesn't support a bytes object + # see https://github.com/python/cpython/pull/10871 + post_request.request.headers["Content-Type"] = "application/json; charset=utf-8" + with pytest.raises(exceptions.CommandError): + export.httpie_command(post_request) + def test_patch(self, patch_request): - result = """http PATCH http://address:22/path?query=param 'header:qvalue' 'content-length:7' <<< 'content'""" + result = """http PATCH 'http://address:22/path?query=param' 'header: qvalue' <<< content""" assert export.httpie_command(patch_request) == result def test_tcp(self, tcp_flow): with pytest.raises(exceptions.CommandError): export.httpie_command(tcp_flow) + def test_escape_single_quotes_in_body(self): + request = tflow.tflow( + req=tutils.treq( + method=b'POST', + headers=(), + content=b"'&#" + ) + ) + command = export.httpie_command(request) + assert shlex.split(command)[-2] == '<<<' + assert shlex.split(command)[-1] == "'&#" + class TestRaw: - def test_get(self, get_request): + def test_req_and_resp_present(self, get_flow): + assert b"header: qvalue" in export.raw(get_flow) + assert b"header-response: svalue" in export.raw(get_flow) + + def test_get_request_present(self, get_request): assert b"header: qvalue" in export.raw(get_request) + def test_get_response_present(self, get_response): + delattr(get_response, 'request') + assert b"header-response: svalue" in export.raw(get_response) + + def test_missing_both(self, get_request): + delattr(get_request, 'request') + delattr(get_request, 'response') + with pytest.raises(exceptions.CommandError): + export.raw(get_request) + + def test_tcp(self, tcp_flow): + with pytest.raises(exceptions.CommandError): + export.raw_request(tcp_flow) + + +class TestRawRequest: + def test_get(self, get_request): + assert b"header: qvalue" in export.raw_request(get_request) + + def test_no_request(self, get_response): + delattr(get_response, 'request') + with pytest.raises(exceptions.CommandError): + export.raw_request(get_response) + + def test_tcp(self, tcp_flow): + with pytest.raises(exceptions.CommandError): + export.raw_request(tcp_flow) + + +class TestRawResponse: + def test_get(self, get_response): + assert b"header-response: svalue" in export.raw_response(get_response) + + def test_no_response(self, get_request): + with pytest.raises(exceptions.CommandError): + export.raw_response(get_request) + def test_tcp(self, tcp_flow): with pytest.raises(exceptions.CommandError): - export.raw(tcp_flow) + export.raw_response(tcp_flow) def qr(f): @@ -97,11 +196,15 @@ def test_export(tmpdir): f = str(tmpdir.join("path")) e = export.Export() with taddons.context(): - assert e.formats() == ["curl", "httpie", "raw"] + assert e.formats() == ["curl", "httpie", "raw", "raw_request", "raw_response"] with pytest.raises(exceptions.CommandError): e.file("nonexistent", tflow.tflow(resp=True), f) - e.file("raw", tflow.tflow(resp=True), f) + e.file("raw_request", tflow.tflow(resp=True), f) + assert qr(f) + os.unlink(f) + + e.file("raw_response", tflow.tflow(resp=True), f) assert qr(f) os.unlink(f) @@ -126,7 +229,7 @@ async def test_export_open(exception, log_message, tmpdir): with taddons.context() as tctx: with mock.patch("mitmproxy.addons.export.open") as m: m.side_effect = exception(log_message) - e.file("raw", tflow.tflow(resp=True), f) + e.file("raw_request", tflow.tflow(resp=True), f) assert await tctx.master.await_log(log_message, level="error") @@ -138,7 +241,11 @@ async def test_clip(tmpdir): e.clip("nonexistent", tflow.tflow(resp=True)) with mock.patch('pyperclip.copy') as pc: - e.clip("raw", tflow.tflow(resp=True)) + e.clip("raw_request", tflow.tflow(resp=True)) + assert pc.called + + with mock.patch('pyperclip.copy') as pc: + e.clip("raw_response", tflow.tflow(resp=True)) assert pc.called with mock.patch('pyperclip.copy') as pc: @@ -153,5 +260,5 @@ async def test_clip(tmpdir): log_message = "Pyperclip could not find a " \ "copy/paste mechanism for your system." pc.side_effect = pyperclip.PyperclipException(log_message) - e.clip("raw", tflow.tflow(resp=True)) + e.clip("raw_request", tflow.tflow(resp=True)) assert await tctx.master.await_log(log_message, level="error") diff --git a/test/mitmproxy/addons/test_serverplayback.py b/test/mitmproxy/addons/test_serverplayback.py index c6a0c1f4..2e42fa03 100644 --- a/test/mitmproxy/addons/test_serverplayback.py +++ b/test/mitmproxy/addons/test_serverplayback.py @@ -1,13 +1,13 @@ import urllib -import pytest -from mitmproxy.test import taddons -from mitmproxy.test import tflow +import pytest import mitmproxy.test.tutils -from mitmproxy.addons import serverplayback from mitmproxy import exceptions from mitmproxy import io +from mitmproxy.addons import serverplayback +from mitmproxy.test import taddons +from mitmproxy.test import tflow def tdump(path, flows): @@ -321,7 +321,7 @@ def test_server_playback_full(): with taddons.context(s) as tctx: tctx.configure( s, - server_replay_refresh = True, + server_replay_refresh=True, ) f = tflow.tflow() @@ -345,7 +345,7 @@ def test_server_playback_kill(): with taddons.context(s) as tctx: tctx.configure( s, - server_replay_refresh = True, + server_replay_refresh=True, server_replay_kill_extra=True ) @@ -357,3 +357,25 @@ def test_server_playback_kill(): f.request.host = "nonexistent" tctx.cycle(s, f) assert f.reply.value == exceptions.Kill + + +def test_server_playback_response_deleted(): + """ + The server playback addon holds references to flows that can be modified by the user in the meantime. + One thing that can happen is that users remove the response object. This happens for example when doing a client + replay at the same time. + """ + sp = serverplayback.ServerPlayback() + with taddons.context(sp) as tctx: + tctx.configure(sp) + f1 = tflow.tflow(resp=True) + f2 = tflow.tflow(resp=True) + + assert not sp.flowmap + + sp.load_flows([f1, f2]) + assert sp.flowmap + + f1.response = f2.response = None + assert not sp.next_flow(f1) + assert not sp.flowmap diff --git a/test/mitmproxy/contentviews/test_protobuf.py b/test/mitmproxy/contentviews/test_protobuf.py index 791045e7..f0a91fd1 100644 --- a/test/mitmproxy/contentviews/test_protobuf.py +++ b/test/mitmproxy/contentviews/test_protobuf.py @@ -8,7 +8,7 @@ datadir = "mitmproxy/contentviews/test_protobuf_data/" def test_view_protobuf_request(tdata): v = full_eval(protobuf.ViewProtobuf()) - p = tdata.path(datadir + "protobuf01") + p = tdata.path(datadir + "protobuf01.bin") with open(p, "rb") as f: raw = f.read() @@ -19,12 +19,12 @@ def test_view_protobuf_request(tdata): v(b'foobar') -@pytest.mark.parametrize("filename", ["protobuf02", "protobuf03"]) +@pytest.mark.parametrize("filename", ["protobuf02.bin", "protobuf03.bin"]) def test_format_pbuf(filename, tdata): path = tdata.path(datadir + filename) with open(path, "rb") as f: input = f.read() - with open(path + "-decoded") as f: + with open(path.replace(".bin", "-decoded.bin")) as f: expected = f.read() assert protobuf.format_pbuf(input) == expected diff --git a/test/mitmproxy/contentviews/test_protobuf_data/protobuf01 b/test/mitmproxy/contentviews/test_protobuf_data/protobuf01.bin index fbfdbff3..fbfdbff3 100644 --- a/test/mitmproxy/contentviews/test_protobuf_data/protobuf01 +++ b/test/mitmproxy/contentviews/test_protobuf_data/protobuf01.bin diff --git a/test/mitmproxy/contentviews/test_protobuf_data/protobuf02-decoded b/test/mitmproxy/contentviews/test_protobuf_data/protobuf02-decoded.bin index 9be61e28..9be61e28 100644 --- a/test/mitmproxy/contentviews/test_protobuf_data/protobuf02-decoded +++ b/test/mitmproxy/contentviews/test_protobuf_data/protobuf02-decoded.bin diff --git a/test/mitmproxy/contentviews/test_protobuf_data/protobuf02 b/test/mitmproxy/contentviews/test_protobuf_data/protobuf02.bin Binary files differindex a47c45d5..a47c45d5 100644 --- a/test/mitmproxy/contentviews/test_protobuf_data/protobuf02 +++ b/test/mitmproxy/contentviews/test_protobuf_data/protobuf02.bin diff --git a/test/mitmproxy/contentviews/test_protobuf_data/protobuf03-decoded b/test/mitmproxy/contentviews/test_protobuf_data/protobuf03-decoded.bin index 3d3392e1..3d3392e1 100644 --- a/test/mitmproxy/contentviews/test_protobuf_data/protobuf03-decoded +++ b/test/mitmproxy/contentviews/test_protobuf_data/protobuf03-decoded.bin diff --git a/test/mitmproxy/contentviews/test_protobuf_data/protobuf03 b/test/mitmproxy/contentviews/test_protobuf_data/protobuf03.bin index 9fb230b3..9fb230b3 100644 --- a/test/mitmproxy/contentviews/test_protobuf_data/protobuf03 +++ b/test/mitmproxy/contentviews/test_protobuf_data/protobuf03.bin diff --git a/test/mitmproxy/data/dumpfile-010 b/test/mitmproxy/data/dumpfile-010.bin Binary files differindex 435795bf..435795bf 100644 --- a/test/mitmproxy/data/dumpfile-010 +++ b/test/mitmproxy/data/dumpfile-010.bin diff --git a/test/mitmproxy/data/dumpfile-011 b/test/mitmproxy/data/dumpfile-011.bin index 936ac0cc..936ac0cc 100644 --- a/test/mitmproxy/data/dumpfile-011 +++ b/test/mitmproxy/data/dumpfile-011.bin diff --git a/test/mitmproxy/data/dumpfile-018 b/test/mitmproxy/data/dumpfile-018.bin index 6a27b5a6..6a27b5a6 100644 --- a/test/mitmproxy/data/dumpfile-018 +++ b/test/mitmproxy/data/dumpfile-018.bin diff --git a/test/mitmproxy/io/test_compat.py b/test/mitmproxy/io/test_compat.py index 4c31e363..341906ca 100644 --- a/test/mitmproxy/io/test_compat.py +++ b/test/mitmproxy/io/test_compat.py @@ -5,7 +5,7 @@ from mitmproxy import exceptions def test_load(tdata): - with open(tdata.path("mitmproxy/data/dumpfile-011"), "rb") as f: + with open(tdata.path("mitmproxy/data/dumpfile-011.bin"), "rb") as f: flow_reader = io.FlowReader(f) flows = list(flow_reader.stream()) assert len(flows) == 1 @@ -13,7 +13,7 @@ def test_load(tdata): def test_load_018(tdata): - with open(tdata.path("mitmproxy/data/dumpfile-018"), "rb") as f: + with open(tdata.path("mitmproxy/data/dumpfile-018.bin"), "rb") as f: flow_reader = io.FlowReader(f) flows = list(flow_reader.stream()) assert len(flows) == 1 @@ -21,7 +21,7 @@ def test_load_018(tdata): def test_cannot_convert(tdata): - with open(tdata.path("mitmproxy/data/dumpfile-010"), "rb") as f: + with open(tdata.path("mitmproxy/data/dumpfile-010.bin"), "rb") as f: flow_reader = io.FlowReader(f) with pytest.raises(exceptions.FlowReadException): list(flow_reader.stream()) diff --git a/test/mitmproxy/proxy/test_config.py b/test/mitmproxy/proxy/test_config.py index 1319d1a9..38a6e1ad 100644 --- a/test/mitmproxy/proxy/test_config.py +++ b/test/mitmproxy/proxy/test_config.py @@ -14,7 +14,7 @@ class TestProxyConfig: def test_invalid_certificate(self, tdata): opts = options.Options() - opts.certs = [tdata.path("mitmproxy/data/dumpfile-011")] + opts.certs = [tdata.path("mitmproxy/data/dumpfile-011.bin")] with pytest.raises(exceptions.OptionsError, match="Invalid certificate format"): ProxyConfig(opts) diff --git a/test/mitmproxy/test_proxy.py b/test/mitmproxy/test_proxy.py index c8cf6c33..f455b0ff 100644 --- a/test/mitmproxy/test_proxy.py +++ b/test/mitmproxy/test_proxy.py @@ -1,20 +1,19 @@ import argparse import platform from unittest import mock + import pytest -from mitmproxy.tools import cmdline -from mitmproxy.tools import main from mitmproxy import options from mitmproxy.proxy import ProxyConfig -from mitmproxy.proxy.server import DummyServer, ProxyServer, ConnectionHandler from mitmproxy.proxy import config - +from mitmproxy.proxy.server import ConnectionHandler, DummyServer, ProxyServer +from mitmproxy.tools import cmdline +from mitmproxy.tools import main from ..conftest import skip_windows class MockParser(argparse.ArgumentParser): - """ argparse.ArgumentParser sys.exits() by default. Make it more testable by throwing an exception instead. @@ -53,11 +52,9 @@ class TestProcessProxyOptions: class TestProxyServer: @skip_windows - @pytest.mark.skipif(platform.mac_ver()[0].split('.')[:2] == ['10', '14'], - reason='Skipping due to macOS Mojave') + @pytest.mark.skipif(platform.system() != "Linux", reason="Linux-only") def test_err(self): - # binding to 0.0.0.0:1 works without special permissions on Windows and - # macOS Mojave + # binding to 0.0.0.0:1 works without special permissions on Windows and macOS Mojave+ conf = ProxyConfig(options.Options(listen_port=1)) with pytest.raises(Exception, match="Error starting proxy server"): ProxyServer(conf) diff --git a/test/release/test_cibuild.py b/test/release/test_cibuild.py index cfa24e63..d4ed32b0 100644 --- a/test/release/test_cibuild.py +++ b/test/release/test_cibuild.py @@ -58,31 +58,60 @@ def test_buildenviron_pr(): ) assert be.is_pull_request - # Mini test for appveyor - be = cibuild.BuildEnviron( - appveyor_pull_request_number="xxxx", + +def test_ci_systems(): + appveyor = cibuild.BuildEnviron( + appveyor_pull_request_number="1234", + appveyor_repo_branch="foo", + appveyor_repo_tag_name="qux", ) - assert be.is_pull_request - assert not be.is_prod_release - assert not be.is_maintenance_branch + assert appveyor.is_pull_request + assert appveyor.branch == "foo" + assert appveyor.tag == "qux" + + travis = cibuild.BuildEnviron( + travis_pull_request="1234", + travis_branch="foo", + travis_tag="foo", + ) + assert travis.is_pull_request + assert travis.branch == "foo" + assert travis.tag == "foo" + + github = cibuild.BuildEnviron( + github_event_name="pull_request", + github_ref="refs/heads/master" + ) + assert github.is_pull_request + assert github.branch == "master" + assert github.tag == "" + + github2 = cibuild.BuildEnviron( + github_event_name="pull_request", + github_ref="refs/tags/qux" + ) + assert github2.is_pull_request + assert github2.branch == "" + assert github2.tag == "qux" def test_buildenviron_commit(): # Simulates an ordinary commit on the master branch. be = cibuild.BuildEnviron( - travis_tag="", - travis_branch="master", - travis_pull_request="false", + github_ref="refs/heads/master", + github_event_name="push", should_build_wheel=True, should_build_pyinstaller=True, should_build_docker=True, docker_username="foo", docker_password="bar", + has_aws_creds=True, ) assert be.docker_tag == "mitmproxy/mitmproxy:dev" assert be.should_upload_docker assert not be.should_upload_pypi assert be.should_upload_docker + assert be.should_upload_aws assert not be.is_prod_release assert not be.is_maintenance_branch @@ -244,3 +273,20 @@ def test_buildenviron_check_version(version, tag, ok, tmpdir): else: with pytest.raises(ValueError): be.check_version() + + +def test_bool_from_env(monkeypatch): + monkeypatch.setenv("FOO", "1") + assert cibuild.bool_from_env("FOO") + + monkeypatch.setenv("FOO", "0") + assert not cibuild.bool_from_env("FOO") + + monkeypatch.setenv("FOO", "false") + assert not cibuild.bool_from_env("FOO") + + monkeypatch.setenv("FOO", "") + assert not cibuild.bool_from_env("FOO") + + monkeypatch.delenv("FOO") + assert not cibuild.bool_from_env("FOO") |