115 lines
3.4 KiB
Python
115 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
import socket
|
|
import subprocess # noqa: S404
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
from urllib.error import URLError
|
|
from urllib.request import urlopen
|
|
|
|
import pytest
|
|
|
|
from control_plane.host_commands import build_host_command_env
|
|
from control_plane.runtime_plans import DjangoApplicationLaunchConfig
|
|
from control_plane.runtime_plans import build_django_server_command
|
|
|
|
|
|
def _find_free_port() -> int:
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
|
|
probe.bind(("127.0.0.1", 0))
|
|
probe.listen(1)
|
|
return int(probe.getsockname()[1])
|
|
|
|
|
|
def _wait_for_http_ready(url: str, *, timeout_seconds: float = 15.0) -> str:
|
|
deadline = time.monotonic() + timeout_seconds
|
|
while time.monotonic() < deadline:
|
|
try:
|
|
with urlopen(url, timeout=1) as response: # noqa: S310
|
|
return response.read().decode("utf-8")
|
|
except URLError:
|
|
time.sleep(0.25)
|
|
|
|
msg = f"Timed out waiting for {url}"
|
|
raise TimeoutError(msg)
|
|
|
|
|
|
def _write_sentinel_urls(project_dir: Path, project_name: str) -> None:
|
|
urls_file = project_dir / project_name / "urls.py"
|
|
urls_file.write_text(
|
|
"from django.http import HttpResponse\n"
|
|
"from django.urls import path\n\n"
|
|
"def sentinel_view(request):\n"
|
|
" return HttpResponse('sentinel-ok')\n\n"
|
|
"urlpatterns = [\n"
|
|
" path('sentinel/', sentinel_view),\n"
|
|
"]\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
|
|
@pytest.mark.host_smoke
|
|
def test_temp_django_project_serves_content(
|
|
host_smoke_enabled: bool,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""Opt-in smoke test should create a temp Django project and serve a sentinel view."""
|
|
if not host_smoke_enabled:
|
|
pytest.skip("Set TUSSILAGO_RUN_HOST_SMOKE=1 to run host smoke coverage.")
|
|
|
|
workspace_root = Path(__file__).resolve().parents[1]
|
|
project_name = "guestsite"
|
|
project_dir = tmp_path / project_name
|
|
child_process_env = build_host_command_env()
|
|
startproject_command = [
|
|
sys.executable,
|
|
"-m",
|
|
"django",
|
|
"startproject",
|
|
project_name,
|
|
str(project_dir),
|
|
]
|
|
|
|
subprocess.run( # noqa: S603
|
|
startproject_command,
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
cwd=workspace_root,
|
|
env=child_process_env,
|
|
timeout=30,
|
|
)
|
|
_write_sentinel_urls(project_dir, project_name)
|
|
|
|
port = _find_free_port()
|
|
command = build_django_server_command(
|
|
DjangoApplicationLaunchConfig(
|
|
wsgi_module=f"{project_name}.wsgi:application",
|
|
bind_host="127.0.0.1",
|
|
port=port,
|
|
workers=1,
|
|
python_executable=Path(sys.executable),
|
|
),
|
|
)
|
|
|
|
process = subprocess.Popen( # noqa: S603
|
|
command,
|
|
cwd=project_dir,
|
|
env=child_process_env,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
)
|
|
try:
|
|
response_body = _wait_for_http_ready(f"http://127.0.0.1:{port}/sentinel/")
|
|
except TimeoutError as error:
|
|
process.terminate()
|
|
stdout, stderr = process.communicate(timeout=10)
|
|
pytest.fail(f"{error}\nstdout:\n{stdout}\nstderr:\n{stderr}")
|
|
finally:
|
|
if process.poll() is None:
|
|
process.terminate()
|
|
process.wait(timeout=10)
|
|
|
|
assert response_body == "sentinel-ok"
|