2022-01-16 07:03:04 +00:00
|
|
|
import logging
|
|
|
|
|
|
|
|
from unittest.mock import Mock
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
from sanic import Sanic
|
|
|
|
from sanic.response import text
|
|
|
|
from sanic.server.async_server import AsyncioServer
|
|
|
|
from sanic.signals import Event
|
|
|
|
from sanic.touchup.schemes.ode import OptionalDispatchEvent
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
from unittest.mock import AsyncMock
|
|
|
|
except ImportError:
|
2022-06-27 09:19:26 +01:00
|
|
|
from tests.asyncmock import AsyncMock # type: ignore
|
2022-01-16 07:03:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def app_one():
|
|
|
|
app = Sanic("One")
|
|
|
|
|
|
|
|
@app.get("/one")
|
|
|
|
async def one(request):
|
|
|
|
return text("one")
|
|
|
|
|
|
|
|
return app
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def app_two():
|
|
|
|
app = Sanic("Two")
|
|
|
|
|
|
|
|
@app.get("/two")
|
|
|
|
async def two(request):
|
|
|
|
return text("two")
|
|
|
|
|
|
|
|
return app
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def clean():
|
|
|
|
Sanic._app_registry = {}
|
|
|
|
yield
|
|
|
|
|
|
|
|
|
|
|
|
def test_serve_same_app_multiple_tuples(app_one, run_multi):
|
|
|
|
app_one.prepare(port=23456)
|
|
|
|
app_one.prepare(port=23457)
|
|
|
|
|
|
|
|
logs = run_multi(app_one)
|
|
|
|
assert (
|
|
|
|
"sanic.root",
|
|
|
|
logging.INFO,
|
|
|
|
"Goin' Fast @ http://127.0.0.1:23456",
|
|
|
|
) in logs
|
|
|
|
assert (
|
|
|
|
"sanic.root",
|
|
|
|
logging.INFO,
|
|
|
|
"Goin' Fast @ http://127.0.0.1:23457",
|
|
|
|
) in logs
|
|
|
|
|
|
|
|
|
|
|
|
def test_serve_multiple_apps(app_one, app_two, run_multi):
|
|
|
|
app_one.prepare(port=23456)
|
|
|
|
app_two.prepare(port=23457)
|
|
|
|
|
|
|
|
logs = run_multi(app_one)
|
|
|
|
assert (
|
|
|
|
"sanic.root",
|
|
|
|
logging.INFO,
|
|
|
|
"Goin' Fast @ http://127.0.0.1:23456",
|
|
|
|
) in logs
|
|
|
|
assert (
|
|
|
|
"sanic.root",
|
|
|
|
logging.INFO,
|
|
|
|
"Goin' Fast @ http://127.0.0.1:23457",
|
|
|
|
) in logs
|
|
|
|
|
|
|
|
|
|
|
|
def test_listeners_on_secondary_app(app_one, app_two, run_multi):
|
|
|
|
app_one.prepare(port=23456)
|
|
|
|
app_two.prepare(port=23457)
|
|
|
|
|
|
|
|
before_start = AsyncMock()
|
|
|
|
after_start = AsyncMock()
|
|
|
|
before_stop = AsyncMock()
|
|
|
|
after_stop = AsyncMock()
|
|
|
|
|
|
|
|
app_two.before_server_start(before_start)
|
|
|
|
app_two.after_server_start(after_start)
|
|
|
|
app_two.before_server_stop(before_stop)
|
|
|
|
app_two.after_server_stop(after_stop)
|
|
|
|
|
|
|
|
run_multi(app_one)
|
|
|
|
|
|
|
|
before_start.assert_awaited_once()
|
|
|
|
after_start.assert_awaited_once()
|
|
|
|
before_stop.assert_awaited_once()
|
|
|
|
after_stop.assert_awaited_once()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"events",
|
|
|
|
(
|
|
|
|
(Event.HTTP_LIFECYCLE_BEGIN,),
|
|
|
|
(Event.HTTP_LIFECYCLE_BEGIN, Event.HTTP_LIFECYCLE_COMPLETE),
|
|
|
|
(
|
|
|
|
Event.HTTP_LIFECYCLE_BEGIN,
|
|
|
|
Event.HTTP_LIFECYCLE_COMPLETE,
|
|
|
|
Event.HTTP_LIFECYCLE_REQUEST,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
def test_signal_synchronization(app_one, app_two, run_multi, events):
|
|
|
|
app_one.prepare(port=23456)
|
|
|
|
app_two.prepare(port=23457)
|
|
|
|
|
|
|
|
for event in events:
|
|
|
|
app_one.signal(event)(AsyncMock())
|
|
|
|
|
|
|
|
run_multi(app_one)
|
|
|
|
|
|
|
|
assert len(app_two.signal_router.routes) == len(events) + 1
|
|
|
|
|
|
|
|
signal_handlers = {
|
|
|
|
signal.handler
|
|
|
|
for signal in app_two.signal_router.routes
|
|
|
|
if signal.name.startswith("http")
|
|
|
|
}
|
|
|
|
|
|
|
|
assert len(signal_handlers) == 1
|
|
|
|
assert list(signal_handlers)[0] is OptionalDispatchEvent.noop
|
|
|
|
|
|
|
|
|
|
|
|
def test_warning_main_process_listeners_on_secondary(
|
|
|
|
app_one, app_two, run_multi
|
|
|
|
):
|
|
|
|
app_two.main_process_start(AsyncMock())
|
|
|
|
app_two.main_process_stop(AsyncMock())
|
|
|
|
app_one.prepare(port=23456)
|
|
|
|
app_two.prepare(port=23457)
|
|
|
|
|
|
|
|
log = run_multi(app_one)
|
|
|
|
|
|
|
|
message = (
|
|
|
|
f"Sanic found 2 listener(s) on "
|
|
|
|
"secondary applications attached to the main "
|
|
|
|
"process. These will be ignored since main "
|
|
|
|
"process listeners can only be attached to your "
|
|
|
|
"primary application: "
|
|
|
|
f"{repr(app_one)}"
|
|
|
|
)
|
|
|
|
|
|
|
|
assert ("sanic.error", logging.WARNING, message) in log
|
|
|
|
|
|
|
|
|
|
|
|
def test_no_applications():
|
|
|
|
Sanic._app_registry = {}
|
|
|
|
message = "Did not find any applications."
|
|
|
|
with pytest.raises(RuntimeError, match=message):
|
|
|
|
Sanic.serve()
|
|
|
|
|
|
|
|
|
|
|
|
def test_oserror_warning(app_one, app_two, run_multi, capfd):
|
|
|
|
orig = AsyncioServer.__await__
|
|
|
|
AsyncioServer.__await__ = Mock(side_effect=OSError("foo"))
|
|
|
|
app_one.prepare(port=23456, workers=2)
|
|
|
|
app_two.prepare(port=23457, workers=2)
|
|
|
|
|
|
|
|
run_multi(app_one)
|
|
|
|
|
|
|
|
captured = capfd.readouterr()
|
|
|
|
assert (
|
|
|
|
"An OSError was detected on startup. The encountered error was: foo"
|
|
|
|
) in captured.err
|
|
|
|
|
|
|
|
AsyncioServer.__await__ = orig
|
|
|
|
|
|
|
|
|
|
|
|
def test_running_multiple_offset_warning(app_one, app_two, run_multi, capfd):
|
|
|
|
app_one.prepare(port=23456, workers=2)
|
|
|
|
app_two.prepare(port=23457)
|
|
|
|
|
|
|
|
run_multi(app_one)
|
|
|
|
|
|
|
|
captured = capfd.readouterr()
|
|
|
|
assert (
|
|
|
|
f"The primary application {repr(app_one)} is running "
|
|
|
|
"with 2 worker(s). All "
|
|
|
|
"application instances will run with the same number. "
|
|
|
|
f"You requested {repr(app_two)} to run with "
|
|
|
|
"1 worker(s), which will be ignored "
|
|
|
|
"in favor of the primary application."
|
|
|
|
) in captured.err
|
|
|
|
|
|
|
|
|
|
|
|
def test_running_multiple_secondary(app_one, app_two, run_multi, capfd):
|
|
|
|
app_one.prepare(port=23456, workers=2)
|
|
|
|
app_two.prepare(port=23457)
|
|
|
|
|
|
|
|
before_start = AsyncMock()
|
|
|
|
app_two.before_server_start(before_start)
|
|
|
|
run_multi(app_one)
|
|
|
|
|
|
|
|
before_start.await_count == 2
|