please the coverage gods

This commit is contained in:
Maximilian Hils 2021-08-20 14:32:12 +02:00
parent 8561a620c8
commit 2945ba925b
8 changed files with 256 additions and 82 deletions

View File

@ -137,7 +137,8 @@ class Dumper:
human.format_address(flow.client_conn.peername)
)
)
else:
else: # pragma: no cover
# this should not happen, but we're defensive here.
client = ""
pushed = ' PUSH_PROMISE' if 'h2-pushed-stream' in flow.metadata else ''

View File

@ -224,22 +224,26 @@ async def test_load(tmpdir):
def test_resolve():
v = view.View()
with taddons.context() as tctx:
f = tft(method="get")
assert tctx.command(v.resolve, "@all") == []
assert tctx.command(v.resolve, "@focus") == []
assert tctx.command(v.resolve, "@shown") == []
assert tctx.command(v.resolve, "@hidden") == []
assert tctx.command(v.resolve, "@marked") == []
assert tctx.command(v.resolve, "@unmarked") == []
assert tctx.command(v.resolve, f"@{f.id}") == []
assert tctx.command(v.resolve, "~m get") == []
v.request(tft(method="get"))
v.request(f)
assert len(tctx.command(v.resolve, "~m get")) == 1
assert len(tctx.command(v.resolve, "@focus")) == 1
assert len(tctx.command(v.resolve, "@all")) == 1
assert len(tctx.command(v.resolve, "@shown")) == 1
assert len(tctx.command(v.resolve, "@unmarked")) == 1
assert len(tctx.command(v.resolve, f"@{f.id}")) == 1
assert tctx.command(v.resolve, "@hidden") == []
assert tctx.command(v.resolve, "@marked") == []
v.request(tft(method="put"))
assert len(tctx.command(v.resolve, f"@{f.id}")) == 1
assert len(tctx.command(v.resolve, "@focus")) == 1
assert len(tctx.command(v.resolve, "@shown")) == 2
assert len(tctx.command(v.resolve, "@all")) == 2

View File

@ -18,7 +18,7 @@ import tornado.testing
from tornado import httpclient
from tornado import websocket
from mitmproxy import options, optmanager
from mitmproxy import certs, options, optmanager
from mitmproxy.test import tflow
from mitmproxy.tools.web import app
from mitmproxy.tools.web import master as webmaster
@ -39,7 +39,81 @@ def get_json(resp: httpclient.HTTPResponse):
return _json.loads(resp.body.decode())
@pytest.mark.usefixtures("no_tornado_logging")
def test_generate_tflow_js(tdata):
tf = tflow.tflow(resp=True, err=True, ws=True)
tf.server_conn.certificate_list = [
certs.Cert.from_pem(
Path(tdata.path("mitmproxy/net/data/verificationcerts/self-signed.pem")).read_bytes()
)
]
tf.request.trailers = Headers(trailer="qvalue")
tf.response.trailers = Headers(trailer="qvalue")
_tflow = app.flow_to_json(tf)
# Set some value as constant, so that _tflow.js would not change every time.
_tflow['id'] = "d91165be-ca1f-4612-88a9-c0f8696f3e29"
_tflow['client_conn']['id'] = "4a18d1a0-50a1-48dd-9aa6-d45d74282939"
_tflow['server_conn']['id'] = "f087e7b2-6d0a-41a8-a8f0-e1a4761395f8"
tflow_json = _json.dumps(_tflow, indent=4, sort_keys=True)
tflow_json = re.sub(
r'( {8}"(address|is_replay|alpn_proto_negotiated)":)',
r" //@ts-ignore\n\1",
tflow_json
).replace(": null", ": undefined")
content = (
"/** Auto-generated by test_app.py:test_generate_tflow_js */\n"
"import {HTTPFlow} from '../../flow';\n"
"export default function(): Required<HTTPFlow> {\n"
f" return {tflow_json}\n"
"}"
)
(Path(__file__).parent / "../../../../web/src/js/__tests__/ducks/_tflow.ts").write_bytes(
content.encode()
)
def test_generate_options_js():
o = options.Options()
m = webmaster.WebMaster(o)
opt: optmanager._Option
def ts_type(t):
if t == bool:
return "boolean"
if t == str:
return "string"
if t == int:
return "number"
if t == typing.Sequence[str]:
return "string[]"
if t == typing.Optional[str]:
return "string | undefined"
raise RuntimeError(t)
with redirect_stdout(io.StringIO()) as s:
print("/** Auto-generated by test_app.py:test_generate_options_js */")
print("export interface OptionsState {")
for _, opt in sorted(m.options.items()):
print(f" {opt.name}: {ts_type(opt.typespec)}")
print("}")
print("")
print("export type Option = keyof OptionsState")
print("")
print("export const defaultState: OptionsState = {")
for _, opt in sorted(m.options.items()):
print(f" {opt.name}: {json.dumps(opt.default)},".replace(": null", ": undefined"))
print("}")
(Path(__file__).parent / "../../../../web/src/js/ducks/_options_gen.ts").write_bytes(
s.getvalue().encode()
)
@pytest.mark.usefixtures("no_tornado_logging", "tdata")
class TestApp(tornado.testing.AsyncHTTPTestCase):
def get_new_ioloop(self):
io_loop = tornado.platform.asyncio.AsyncIOLoop()
@ -51,6 +125,7 @@ class TestApp(tornado.testing.AsyncHTTPTestCase):
m = webmaster.WebMaster(o, with_termlog=False)
f = tflow.tflow(resp=True)
f.id = "42"
f.request.content = b"foo\nbar"
m.view.add([f])
m.view.add([tflow.tflow(err=True)])
m.log.info("test log")
@ -153,7 +228,8 @@ class TestApp(tornado.testing.AsyncHTTPTestCase):
"code": 404,
"headers": [("bar", "baz")],
"content": "resp",
}
},
"marked": ":red_circle:",
}
assert self.put_json("/flows/42", upd).code == 200
assert f.request.method == "PATCH"
@ -252,10 +328,17 @@ class TestApp(tornado.testing.AsyncHTTPTestCase):
assert f.modified()
f.revert()
def test_flow_content_view(self):
def test_flow_contentview(self):
assert get_json(self.fetch("/flows/42/request/content/raw")) == {
"lines": [
[["text", "content"]]
[["text", "foo"]],
[["text", "bar"]]
],
"description": "Raw"
}
assert get_json(self.fetch("/flows/42/request/content/raw?lines=1")) == {
"lines": [
[["text", "foo"]]
],
"description": "Raw"
}
@ -322,70 +405,3 @@ class TestApp(tornado.testing.AsyncHTTPTestCase):
# trigger on_close by opening a second connection.
ws_client2 = yield websocket.websocket_connect(ws_url)
ws_client2.close()
def test_generate_tflow_js(self):
tf = tflow.tflow(resp=True, err=True, ws=True)
tf.request.trailers = Headers(trailer="qvalue")
tf.response.trailers = Headers(trailer="qvalue")
_tflow = app.flow_to_json(tf)
# Set some value as constant, so that _tflow.js would not change every time.
_tflow['id'] = "d91165be-ca1f-4612-88a9-c0f8696f3e29"
_tflow['client_conn']['id'] = "4a18d1a0-50a1-48dd-9aa6-d45d74282939"
_tflow['server_conn']['id'] = "f087e7b2-6d0a-41a8-a8f0-e1a4761395f8"
tflow_json = _json.dumps(_tflow, indent=4, sort_keys=True)
tflow_json = re.sub(
r'( {8}"(address|is_replay|alpn_proto_negotiated)":)',
r" //@ts-ignore\n\1",
tflow_json
).replace(": null", ": undefined")
content = (
"/** Auto-generated by test_app.py:TestApp.test_generate_tflow_js */\n"
"import {HTTPFlow} from '../../flow';\n"
"export default function(): Required<HTTPFlow> {\n"
f" return {tflow_json}\n"
"}"
)
(Path(__file__).parent / "../../../../web/src/js/__tests__/ducks/_tflow.ts").write_bytes(
content.encode()
)
def test_generate_options_js(self):
o = options.Options()
m = webmaster.WebMaster(o)
opt: optmanager._Option
def ts_type(t):
if t == bool:
return "boolean"
if t == str:
return "string"
if t == int:
return "number"
if t == typing.Sequence[str]:
return "string[]"
if t == typing.Optional[str]:
return "string | undefined"
raise RuntimeError(t)
with redirect_stdout(io.StringIO()) as s:
print("/** Auto-generated by test_app.py:TestApp.test_generate_options_js */")
print("export interface OptionsState {")
for _, opt in sorted(m.options.items()):
print(f" {opt.name}: {ts_type(opt.typespec)}")
print("}")
print("")
print("export type Option = keyof OptionsState")
print("")
print("export const defaultState: OptionsState = {")
for _, opt in sorted(m.options.items()):
print(f" {opt.name}: {json.dumps(opt.default)},".replace(": null", ": undefined"))
print("}")
(Path(__file__).parent / "../../../../web/src/js/ducks/_options_gen.ts").write_bytes(
s.getvalue().encode()
)

View File

@ -13,7 +13,8 @@ module.exports = async () => {
],
"coverageDirectory": "./coverage",
"coveragePathIgnorePatterns": [
"<rootDir>/src/js/filt/filt.js"
"<rootDir>/src/js/filt/filt.js",
"<rootDir>/src/js/filt/command.js"
],
"collectCoverageFrom": [
"src/js/**/*.{js,jsx,ts,tsx}"

View File

@ -1,9 +1,6 @@
{
"name": "mitmproxy",
"private": true,
"jest": {
"verbose": true
},
"scripts": {
"test": "tsc --noEmit && jest --coverage",
"build": "gulp prod",

View File

@ -611,6 +611,121 @@ exports[`FlowView 4`] = `
</tr>
</tbody>
</table>
<h4>
Server Certificate
</h4>
<table
class="certificate-table"
>
<tbody>
<tr>
<td>
Type
</td>
<td>
RSA, 2048 bits
</td>
</tr>
<tr>
<td>
SHA256 digest
</td>
<td>
e5f62a1175031b6feb959bc8e6dd0f8e2546dbbf7c32da39534309d8aa92967c
</td>
</tr>
<tr>
<td>
Valid from
</td>
<td>
2020-11-03 06:03:27
</td>
</tr>
<tr>
<td>
Valid to
</td>
<td>
2040-10-29 06:03:27
</td>
</tr>
<tr>
<td>
Subject Alternative Names
</td>
<td>
example.mitmproxy.org
</td>
</tr>
<tr>
<td>
Subject
</td>
<td>
<dl
class="cert-attributes"
>
<dt>
C
</dt>
<dd>
AU
</dd>
<dt>
ST
</dt>
<dd>
Some-State
</dd>
<dt>
O
</dt>
<dd>
Internet Widgits Pty Ltd
</dd>
</dl>
</td>
</tr>
<tr>
<td>
Issuer
</td>
<td>
<dl
class="cert-attributes"
>
<dt>
C
</dt>
<dd>
AU
</dd>
<dt>
ST
</dt>
<dd>
Some-State
</dd>
<dt>
O
</dt>
<dd>
Internet Widgits Pty Ltd
</dd>
</dl>
</td>
</tr>
<tr>
<td>
Serial
</td>
<td>
247170098335718583458667965517443538258472437317
</td>
</tr>
</tbody>
</table>
</section>
</div>
</DocumentFragment>

View File

@ -1,4 +1,4 @@
/** Auto-generated by test_app.py:TestApp.test_generate_tflow_js */
/** Auto-generated by test_app.py:test_generate_tflow_js */
import {HTTPFlow} from '../../flow';
export default function(): Required<HTTPFlow> {
return {
@ -90,7 +90,47 @@ export default function(): Required<HTTPFlow> {
22
],
"alpn": undefined,
"cert": undefined,
"cert": {
"altnames": [
"example.mitmproxy.org"
],
"issuer": [
[
"C",
"AU"
],
[
"ST",
"Some-State"
],
[
"O",
"Internet Widgits Pty Ltd"
]
],
"keyinfo": [
"RSA",
2048
],
"notafter": 2235103407,
"notbefore": 1604383407,
"serial": "247170098335718583458667965517443538258472437317",
"sha256": "e5f62a1175031b6feb959bc8e6dd0f8e2546dbbf7c32da39534309d8aa92967c",
"subject": [
[
"C",
"AU"
],
[
"ST",
"Some-State"
],
[
"O",
"Internet Widgits Pty Ltd"
]
]
},
"cipher": undefined,
"id": "f087e7b2-6d0a-41a8-a8f0-e1a4761395f8",
"peername": [

View File

@ -1,4 +1,4 @@
/** Auto-generated by test_app.py:TestApp.test_generate_options_js */
/** Auto-generated by test_app.py:test_generate_options_js */
export interface OptionsState {
add_upstream_certs_to_client_chain: boolean
allow_hosts: string[]