Skip to content

Commit dfeb588

Browse files
authored
fix(testing): subprocess client raises exc when fails to start (#4153)
bug(testing): subprocess client raises exc when fails to start (#4021)
1 parent f6a1dbc commit dfeb588

File tree

2 files changed

+43
-6
lines changed

2 files changed

+43
-6
lines changed

litestar/testing/client/subprocess_client.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,18 @@ def _get_available_port() -> int:
2525

2626

2727
@contextmanager
28-
def run_app(workdir: pathlib.Path, app: str) -> Iterator[str]:
29-
"""Launch a litestar application in a subprocess with a random available port."""
28+
def run_app(workdir: pathlib.Path, app: str, retry_count: int = 100, retry_timeout: int = 1) -> Iterator[str]:
29+
"""Launch a litestar application in a subprocess with a random available port.
30+
31+
Args:
32+
workdir: Path to working directory where run command will be executed
33+
app: Path to Litestar application, e.g.: "my_app:application"
34+
retry_count: Number of retries to wait for the application to start
35+
retry_timeout: Timeout in seconds to wait between retries
36+
37+
Raises:
38+
StartupError: If the application fails to start with given retry count and timeout
39+
"""
3040
port = _get_available_port()
3141
with subprocess.Popen(
3242
args=["litestar", "--app", app, "run", "--port", str(port)],
@@ -35,12 +45,20 @@ def run_app(workdir: pathlib.Path, app: str) -> Iterator[str]:
3545
cwd=workdir,
3646
) as proc:
3747
url = f"http://127.0.0.1:{port}"
38-
for _ in range(100): # pragma: no cover
48+
application_started = False
49+
50+
for _ in range(retry_count):
3951
try:
4052
httpx.get(url, timeout=0.1)
53+
application_started = True
4154
break
4255
except httpx.TransportError:
43-
time.sleep(1)
56+
time.sleep(retry_timeout)
57+
58+
if not application_started:
59+
proc.kill()
60+
raise StartupError("Application failed to start")
61+
4462
yield url
4563
proc.kill()
4664

tests/unit/test_testing/test_sub_client/test_subprocess_client.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,44 @@
1212
import pytest
1313

1414
from litestar.testing import subprocess_async_client, subprocess_sync_client
15+
from litestar.testing.client.subprocess_client import StartupError, run_app
1516

1617
if sys.platform == "win32":
1718
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
1819

1920
ROOT = pathlib.Path(__file__).parent
21+
APP = "demo:app"
2022

2123

2224
@pytest.fixture(name="async_client")
2325
async def fx_async_client() -> AsyncIterator[httpx.AsyncClient]:
24-
async with subprocess_async_client(workdir=ROOT, app="demo:app") as client:
26+
async with subprocess_async_client(workdir=ROOT, app=APP) as client:
2527
yield client
2628

2729

2830
@pytest.fixture(name="sync_client")
2931
def fx_sync_client() -> Iterator[httpx.Client]:
30-
with subprocess_sync_client(workdir=ROOT, app="demo:app") as client:
32+
with subprocess_sync_client(workdir=ROOT, app=APP) as client:
3133
yield client
3234

3335

36+
async def test_run_app() -> None:
37+
"""Ensure that method returns application url if started successfully"""
38+
with run_app(workdir=ROOT, app=APP) as url:
39+
assert isinstance(url, str)
40+
assert url.startswith("http://127.0.0.1:")
41+
42+
43+
async def test_run_app_exception() -> None:
44+
"""
45+
Ensure that method throws a StartupError if the application fails to start.
46+
To simulate this, we set retry_count=0, so that we don't check if the application has started.
47+
"""
48+
with pytest.raises(StartupError):
49+
with run_app(workdir=ROOT, app=APP, retry_count=0):
50+
...
51+
52+
3453
async def test_subprocess_async_client(async_client: httpx.AsyncClient) -> None:
3554
"""Demonstrates functionality of the async client with an infinite SSE source that cannot be tested with the
3655
regular async test client.

0 commit comments

Comments
 (0)