328 lines
8.7 KiB
Python
328 lines
8.7 KiB
Python
import json
|
|
import subprocess
|
|
|
|
from pathlib import Path
|
|
from typing import List, Optional, Tuple
|
|
|
|
import pytest
|
|
|
|
from sanic_routing import __version__ as __routing_version__
|
|
|
|
from sanic import __version__
|
|
|
|
|
|
def capture(command: List[str]):
|
|
proc = subprocess.Popen(
|
|
command,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
cwd=Path(__file__).parent,
|
|
)
|
|
try:
|
|
out, err = proc.communicate(timeout=10)
|
|
except subprocess.TimeoutExpired:
|
|
proc.kill()
|
|
out, err = proc.communicate()
|
|
return out, err, proc.returncode
|
|
|
|
|
|
def starting_line(lines: List[str]):
|
|
for idx, line in enumerate(lines):
|
|
if line.strip().startswith(b"Sanic v"):
|
|
return idx
|
|
return 0
|
|
|
|
|
|
def read_app_info(lines: List[str]):
|
|
for line in lines:
|
|
if line.startswith(b"{") and line.endswith(b"}"):
|
|
return json.loads(line)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"appname,extra",
|
|
(
|
|
("fake.server.app", None),
|
|
("fake.server:create_app", "--factory"),
|
|
("fake.server.create_app()", None),
|
|
),
|
|
)
|
|
def test_server_run(appname: str, extra: Optional[str]):
|
|
command = ["sanic", appname]
|
|
if extra:
|
|
command.append(extra)
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
firstline = lines[starting_line(lines) + 1]
|
|
|
|
assert exitcode != 1
|
|
assert firstline == b"Goin' Fast @ http://127.0.0.1:8000"
|
|
|
|
|
|
def test_server_run_factory_with_args():
|
|
command = [
|
|
"sanic",
|
|
"fake.server.create_app_with_args",
|
|
"--factory",
|
|
]
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
|
|
assert exitcode != 1, lines
|
|
assert b"module=fake.server.create_app_with_args" in lines
|
|
|
|
|
|
def test_server_run_factory_with_args_arbitrary():
|
|
command = [
|
|
"sanic",
|
|
"fake.server.create_app_with_args",
|
|
"--factory",
|
|
"--foo=bar",
|
|
]
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
|
|
assert exitcode != 1, lines
|
|
assert b"foo=bar" in lines
|
|
|
|
|
|
def test_error_with_function_as_instance_without_factory_arg():
|
|
command = ["sanic", "fake.server.create_app"]
|
|
out, err, exitcode = capture(command)
|
|
assert b"try: \nsanic fake.server.create_app --factory" in err
|
|
assert exitcode != 1
|
|
|
|
|
|
def test_error_with_path_as_instance_without_simple_arg():
|
|
command = ["sanic", "./fake/"]
|
|
out, err, exitcode = capture(command)
|
|
assert (
|
|
b"Please use --simple if you are passing a directory to sanic." in err
|
|
)
|
|
assert exitcode != 1
|
|
|
|
|
|
@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: Tuple[str]):
|
|
command = ["sanic", "fake.server.app", *cmd, "-p=9999", "--debug"]
|
|
out, err, exitcode = capture(command)
|
|
assert exitcode != 1
|
|
lines = out.split(b"\n")
|
|
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: Tuple[str]):
|
|
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")
|
|
|
|
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: Tuple[str]):
|
|
command = ["sanic", "fake.server.app", *cmd]
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
expected = b"Goin' Fast @ http://localhost:9999"
|
|
|
|
assert exitcode != 1
|
|
assert expected in lines, f"Lines found: {lines}\nErr output: {err}"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"cmd",
|
|
(
|
|
("--host=127.0.0.127", "--port=9999"),
|
|
("-H", "127.0.0.127", "-p", "9999"),
|
|
),
|
|
)
|
|
def test_host_port_ipv4(cmd: Tuple[str]):
|
|
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"
|
|
|
|
assert exitcode != 1
|
|
assert expected in lines, f"Lines found: {lines}\nErr output: {err}"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"cmd",
|
|
(
|
|
("--host=::", "--port=9999"),
|
|
("-H", "::", "-p", "9999"),
|
|
),
|
|
)
|
|
def test_host_port_ipv6_any(cmd: Tuple[str]):
|
|
command = ["sanic", "fake.server.app", *cmd]
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
expected = b"Goin' Fast @ http://[::]:9999"
|
|
|
|
assert exitcode != 1
|
|
assert expected in lines, f"Lines found: {lines}\nErr output: {err}"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"cmd",
|
|
(
|
|
("--host=::1", "--port=9999"),
|
|
("-H", "::1", "-p", "9999"),
|
|
),
|
|
)
|
|
def test_host_port_ipv6_loopback(cmd: Tuple[str]):
|
|
command = ["sanic", "fake.server.app", *cmd]
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
expected = b"Goin' Fast @ http://[::1]:9999"
|
|
|
|
assert exitcode != 1
|
|
assert expected in lines, f"Lines found: {lines}\nErr output: {err}"
|
|
|
|
|
|
@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: int, cmd: Tuple[str]):
|
|
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: str):
|
|
command = ["sanic", "fake.server.app", cmd]
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
info = read_app_info(lines)
|
|
|
|
assert info["debug"] is True, f"Lines found: {lines}\nErr output: {err}"
|
|
assert (
|
|
info["auto_reload"] is False
|
|
), f"Lines found: {lines}\nErr output: {err}"
|
|
assert "dev" not in info, f"Lines found: {lines}\nErr output: {err}"
|
|
|
|
|
|
@pytest.mark.parametrize("cmd", ("--dev", "-d"))
|
|
def test_dev(cmd: str):
|
|
command = ["sanic", "fake.server.app", cmd]
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
info = read_app_info(lines)
|
|
|
|
assert info["debug"] is True, f"Lines found: {lines}\nErr output: {err}"
|
|
assert (
|
|
info["auto_reload"] is True
|
|
), f"Lines found: {lines}\nErr output: {err}"
|
|
|
|
|
|
@pytest.mark.parametrize("cmd", ("--auto-reload", "-r"))
|
|
def test_auto_reload(cmd: str):
|
|
command = ["sanic", "fake.server.app", cmd]
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
info = read_app_info(lines)
|
|
|
|
assert info["debug"] is False, f"Lines found: {lines}\nErr output: {err}"
|
|
assert (
|
|
info["auto_reload"] is True
|
|
), f"Lines found: {lines}\nErr output: {err}"
|
|
assert "dev" not in info, f"Lines found: {lines}\nErr output: {err}"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"cmd,expected", (("--access-log", True), ("--no-access-log", False))
|
|
)
|
|
def test_access_logs(cmd: str, expected: bool):
|
|
command = ["sanic", "fake.server.app", cmd]
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
info = read_app_info(lines)
|
|
|
|
assert (
|
|
info["access_log"] is expected
|
|
), f"Lines found: {lines}\nErr output: {err}"
|
|
|
|
|
|
@pytest.mark.parametrize("cmd", ("--version", "-v"))
|
|
def test_version(cmd: str):
|
|
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: str, expected: bool):
|
|
command = ["sanic", "fake.server.app", cmd]
|
|
out, err, exitcode = capture(command)
|
|
lines = out.split(b"\n")
|
|
info = read_app_info(lines)
|
|
|
|
assert (
|
|
info["noisy_exceptions"] is expected
|
|
), f"Lines found: {lines}\nErr output: {err}"
|