import json import subprocess from pathlib import Path import pytest from pyparsing import line from sanic_routing import __version__ as __routing_version__ from sanic import __version__ def capture(command): proc = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=Path(__file__).parent, ) try: out, err = proc.communicate(timeout=1) except subprocess.TimeoutExpired: proc.kill() out, err = proc.communicate() return out, err, proc.returncode def starting_line(lines): for idx, line in enumerate(lines): if line.strip().startswith(b"Sanic v"): return idx return 0 def read_app_info(lines): for line in lines: if line.startswith(b"{") and line.endswith(b"}"): return json.loads(line) @pytest.mark.parametrize( "appname", ( "fake.server.app", "fake.server:app", "fake.server:create_app()", "fake.server.create_app()", ), ) def test_server_run(appname): command = ["sanic", appname] out, err, exitcode = capture(command) lines = out.split(b"\n") firstline = lines[starting_line(lines) + 1] error_message = f"Lines found: {lines}\nErr output: {err}" assert exitcode != 1 assert lines, error_message assert firstline == b"Goin' Fast @ http://127.0.0.1:8000" @pytest.mark.parametrize( "cmd", ( ( "--cert=certs/sanic.example/fullchain.pem", "--key=certs/sanic.example/privkey.pem", ), ( "--tls=certs/sanic.example/", "--tls=certs/localhost/", ), ( "--tls=certs/sanic.example/", "--tls=certs/localhost/", "--tls-strict-host", ), ), ) def test_tls_options(cmd): command = ["sanic", "fake.server.app", *cmd, "-p=9999", "--debug"] out, err, exitcode = capture(command) assert exitcode != 1 lines = out.split(b"\n") error_message = f"Lines found: {lines}\nErr output: {err}" assert lines, error_message firstline = lines[starting_line(lines) + 1] assert firstline == b"Goin' Fast @ https://127.0.0.1:9999" @pytest.mark.parametrize( "cmd", ( ("--cert=certs/sanic.example/fullchain.pem",), ( "--cert=certs/sanic.example/fullchain.pem", "--key=certs/sanic.example/privkey.pem", "--tls=certs/localhost/", ), ("--tls-strict-host",), ), ) def test_tls_wrong_options(cmd): command = ["sanic", "fake.server.app", *cmd, "-p=9999", "--debug"] out, err, exitcode = capture(command) assert exitcode == 1 assert not out lines = err.decode().split("\n") error_message = f"Lines found: {lines}\nErr output: {err}" assert lines, error_message errmsg = lines[6] assert errmsg == "TLS certificates must be specified by either of:" @pytest.mark.parametrize( "cmd", ( ("--host=localhost", "--port=9999"), ("-H", "localhost", "-p", "9999"), ), ) def test_host_port_localhost(cmd): command = ["sanic", "fake.server.app", *cmd] out, err, exitcode = capture(command) lines = out.split(b"\n") expected = b"Goin' Fast @ http://localhost:9999" error_message = f"Lines found: {lines}\nErr output: {err}" assert exitcode != 1 assert lines, error_message assert expected in lines, error_message @pytest.mark.parametrize( "cmd", ( ("--host=127.0.0.127", "--port=9999"), ("-H", "127.0.0.127", "-p", "9999"), ), ) def test_host_port_ipv4(cmd): command = ["sanic", "fake.server.app", *cmd] out, err, exitcode = capture(command) lines = out.split(b"\n") expected = b"Goin' Fast @ http://127.0.0.127:9999" error_message = f"Lines found: {lines}\nErr output: {err}" assert exitcode != 1 assert lines, error_message assert expected in lines, error_message @pytest.mark.parametrize( "cmd", ( ("--host=::", "--port=9999"), ("-H", "::", "-p", "9999"), ), ) def test_host_port_ipv6_any(cmd): command = ["sanic", "fake.server.app", *cmd] out, err, exitcode = capture(command) lines = out.split(b"\n") expected = b"Goin' Fast @ http://[::]:9999" error_message = f"Lines found: {lines}\nErr output: {err}" assert exitcode != 1 assert lines, error_message assert expected in lines, error_message @pytest.mark.parametrize( "cmd", ( ("--host=::1", "--port=9999"), ("-H", "::1", "-p", "9999"), ), ) def test_host_port_ipv6_loopback(cmd): command = ["sanic", "fake.server.app", *cmd] out, err, exitcode = capture(command) lines = out.split(b"\n") expected = b"Goin' Fast @ http://[::1]:9999" error_message = f"Lines found: {lines}\nErr output: {err}" assert exitcode != 1 assert lines, error_message assert expected in lines, error_message @pytest.mark.parametrize( "num,cmd", ( (1, (f"--workers={1}",)), (2, (f"--workers={2}",)), (4, (f"--workers={4}",)), (1, ("-w", "1")), (2, ("-w", "2")), (4, ("-w", "4")), ), ) def test_num_workers(num, cmd): command = ["sanic", "fake.server.app", *cmd] out, err, exitcode = capture(command) lines = out.split(b"\n") if num == 1: expected = b"mode: production, single worker" else: expected = (f"mode: production, w/ {num} workers").encode() assert exitcode != 1 assert expected in lines, f"Expected {expected}\nLines found: {lines}" @pytest.mark.parametrize("cmd", ("--debug",)) def test_debug(cmd): command = ["sanic", "fake.server.app", cmd] out, err, exitcode = capture(command) lines = out.split(b"\n") info = read_app_info(lines) error_message = f"Lines found: {lines}\nErr output: {err}" assert info, error_message assert info["debug"] is True, error_message assert info["auto_reload"] is False, error_message assert "dev" not in info, error_message @pytest.mark.parametrize("cmd", ("--dev", "-d")) def test_dev(cmd): command = ["sanic", "fake.server.app", cmd] out, err, exitcode = capture(command) lines = out.split(b"\n") info = read_app_info(lines) error_message = f"Lines found: {lines}\nErr output: {err}" assert info, error_message assert info["debug"] is True, error_message assert info["auto_reload"] is True, error_message @pytest.mark.parametrize("cmd", ("--auto-reload", "-r")) def test_auto_reload(cmd): command = ["sanic", "fake.server.app", cmd] out, err, exitcode = capture(command) lines = out.split(b"\n") info = read_app_info(lines) error_message = f"Lines found: {lines}\nErr output: {err}" assert info, error_message assert info["debug"] is False, error_message assert info["auto_reload"] is True, error_message assert "dev" not in info, error_message @pytest.mark.parametrize( "cmd,expected", (("--access-log", True), ("--no-access-log", False)) ) def test_access_logs(cmd, expected): command = ["sanic", "fake.server.app", cmd] out, err, exitcode = capture(command) lines = out.split(b"\n") info = read_app_info(lines) error_message = f"Lines found: {lines}\nErr output: {err}" assert info, error_message assert info["access_log"] is expected, error_message @pytest.mark.parametrize("cmd", ("--version", "-v")) def test_version(cmd): command = ["sanic", cmd] out, err, exitcode = capture(command) version_string = f"Sanic {__version__}; Routing {__routing_version__}\n" assert out == version_string.encode("utf-8") @pytest.mark.parametrize( "cmd,expected", ( ("--noisy-exceptions", True), ("--no-noisy-exceptions", False), ), ) def test_noisy_exceptions(cmd, expected): command = ["sanic", "fake.server.app", cmd] out, err, exitcode = capture(command) lines = out.split(b"\n") info = read_app_info(lines) error_message = f"Lines found: {lines}\nErr output: {err}" assert info, error_message assert info["noisy_exceptions"] is expected, error_message