phase: 01_runnable_service_scaffold
**Phase 01 final verification pass — all criteria green**
- Verified (nothing left to implement; all 4 task files already in `complete/`): toolchain lock, typed `Settings`+`.env.example`, debugpy bootstrap, app factory, CLI (`--host/--port/--reload/--levels-dir`, `python -m undercooked`), static shell + `/static`, security headers, no-CDN/doc guards, `validate.sh`.
- `uv sync --extra dev` → exit 0; `uv.lock` present (untracked → harness commits it with the phase).
- `uv run undercooked --host 127.0.0.1 --port 8000` → `curl /healthz` = 200 `{"status":"ok","version":"0.1.0","rooms":0}` (superset of the criterion's body — now documented in README); `curl /` = 200 shell HTML; static css/js 200; CSP/nosniff/no-referrer/DENY on 200, 404 and 405 too.
- `DEBUGPY=1`: port 5678 is held on this machine by the user's own podman dev-container debugpy adapter — **no live change made/killed**; verified via `DEBUGPY=1 DEBUGPY_PORT=5679` → listener opens, `/healthz` still 200; the occupied-port case degraded gracefully as designed (warning, server serves). Bad config → one line + exit 2 ✓.
- `bash .agents/validate.sh` (root **and** `.agents/`) → exit 0: `ruff check .` 0 errors, `pyright` 0 errors, levels-CLI notice-skip (matches README prose), **284 passed** full suite incl. 10 Playwright e2e, coverage **100.00%** (floor 90), repo hygiene clean. `uv run pytest -m e2e` → 10 passed.
- Docs fixed: README Status → phase 01 complete + next pending 02 (was pointing at `todo/01…`), documented real `/healthz` body, precedence now "CLI flags > env > .env > default", added the PLAN §13-promised **How to play** section (honest about shell-only today), Layout rows marked what exists vs phases 02/07. AGENTS.md: recorded the `data:`-URL exemption in the no-CDN rule (test allows it) and matched the session-token rule to the browser's WebCrypto/base64url implementation. Also corrected a stale "session-wide chromium" docstring in `tests/e2e/conftest.py` (fixtures are function-scoped).
- Deviations: no container build run (needs an image pull; not a phase criterion — `Containerfile`/`compose.yaml` reviewed and consistent with `docs/DEPLOYMENT.md`); `HEAD /` is 405 (routes are GET-only by design; health checks use GET). No git index/history commands run.
- Next pending phase: `02_level_content_and_validation`.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""Phase-01 task-01 harness smoke tests: package metadata and pytest wiring."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import undercooked
|
||||
|
||||
pytestmark = pytest.mark.unit
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
HARNESS_MARKERS = ("unit", "integration", "e2e", "slow")
|
||||
TEST_SUBDIRS = ("unit", "integration", "sim", "e2e")
|
||||
|
||||
|
||||
def test_package_metadata() -> None:
|
||||
assert undercooked.__version__ == "0.1.0"
|
||||
assert undercooked.__all__ == []
|
||||
|
||||
|
||||
def test_harness_markers_registered(pytestconfig: pytest.Config) -> None:
|
||||
registered = {line.split(":", 1)[0].strip() for line in pytestconfig.getini("markers")}
|
||||
assert set(HARNESS_MARKERS) <= registered
|
||||
|
||||
|
||||
def test_strict_markers_enabled(pytestconfig: pytest.Config) -> None:
|
||||
assert "--strict-markers" in pytestconfig.getini("addopts")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", TEST_SUBDIRS)
|
||||
def test_test_tree_directories_exist_with_readme(name: str) -> None:
|
||||
directory = REPO_ROOT / "tests" / name
|
||||
assert directory.is_dir()
|
||||
readme = directory / "README.md"
|
||||
assert readme.is_file()
|
||||
assert readme.read_text(encoding="utf-8").strip()
|
||||
|
||||
|
||||
def test_tests_dir_is_not_a_package() -> None:
|
||||
assert not (REPO_ROOT / "tests" / "__init__.py").exists()
|
||||
|
||||
|
||||
def test_fixture_levels_dir_exists(isolated_levels_dir: None) -> None:
|
||||
levels_dir = Path(os.environ["UNDERCOOKED_LEVELS_DIR"])
|
||||
assert levels_dir == REPO_ROOT / "tests" / "fixtures" / "levels"
|
||||
assert levels_dir.is_dir()
|
||||
assert not list(levels_dir.glob("*.yaml"))
|
||||
|
||||
|
||||
def test_e2e_tests_follow_the_story_naming_convention() -> None:
|
||||
"""`pytest -m e2e --collect-only` selects only `test_usNN_*.py` story suites.
|
||||
|
||||
Task 01 could only assert "the marker collects nothing yet". Now that the first
|
||||
browser suite exists the durable invariant is the naming contract in
|
||||
`tests/e2e/README.md`: a story suite that is not named for its story is one that
|
||||
nobody can find, and one that cannot be collected is one that never runs.
|
||||
"""
|
||||
result = subprocess.run( # fixed argv, repo's own interpreter
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"pytest",
|
||||
"-m",
|
||||
"e2e",
|
||||
"--collect-only",
|
||||
"-q",
|
||||
"-o",
|
||||
"addopts=", # drop the outer coverage flags: no nested .coverage writes
|
||||
"-p",
|
||||
"no:cacheprovider",
|
||||
],
|
||||
cwd=REPO_ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
timeout=120,
|
||||
)
|
||||
assert result.returncode == pytest.ExitCode.OK, result.stdout + result.stderr
|
||||
assert "error" not in result.stderr.lower(), result.stderr
|
||||
files = sorted({line.split("::")[0] for line in result.stdout.splitlines() if "::" in line})
|
||||
assert files, f"the e2e marker selected nothing:\n{result.stdout}"
|
||||
for path in files:
|
||||
assert path.startswith("tests/e2e/"), path
|
||||
assert re.fullmatch(r"test_us\d{2}_.+\.py", Path(path).name), path
|
||||
Reference in New Issue
Block a user