Files
undercooked/tests/unit/test_clock.py
T
ducoterra f51ba919af phase: 05_anonymous_lobbies_and_room_lifecycle
All green. Final report:

**Phase 05 final verification pass — complete, 100% green.**

**Verified (all 5 tasks already in `complete/`):**
- `RoomSupervisor` import, `POST /api/rooms` → 5-char Crockford code (`VBE8Y`), `GET /api/rooms/{code}` → seat list, join/session/404 flows — all live over HTTP
- `docs/ROOMS.md` present with FSM diagram, crash-isolation and "What is lost on restart" sections
- Phase tests all pass: lifecycle 4-seat `WON`→`LOBBY` (7 tests), crash isolation, GC/idle via `FakeClock` (no sleeps), FSM, tick cadence, supervisor, sessions

**Results:**
- `uv run pytest` → **1506 passed, 0 failed**; coverage **98.89%** (>90% floor; `room.py` 99%, `rooms.py` 97%, `sessions.py` 99% — all ≥95% per-module gate)
- `uv run ruff check .` → clean; `uv run pyright` → 0 errors; `uv run undercooked-levels validate` → content OK; server boots, `/healthz` 200, client at `/` 200
- e2e US-01 suite re-run after fixes: 10 passed

**Documentation — inconsistencies found and fixed:**
- `README.md`: status now lists phases 01–05 complete, next pending `06_realtime_websocket_protocol`; dropped "(phase 05, in progress)"; 3 stale "Not implemented until phase 05." references corrected
- `app/static/js/app.js` + `tests/e2e/test_us01_landing_surface.py`: placeholder string updated in lockstep to "Not implemented until phase 07." (phase 07 task 02 wires the buttons), comments updated to reflect phase-05 completion
- `AGENTS.md`: reviewed in full — §6 already documents all phase-05 modules accurately; no changes needed

**Decisions:** no code behavior changes beyond the user-visible placeholder string; no live-infrastructure touch (only a local loopback server, stopped).

**Next pending phase:** `06_realtime_websocket_protocol`.
2026-10-08 05:17:21 -04:00

58 lines
1.7 KiB
Python

"""Unit: `Clock`, `SystemClock` and `FakeClock` — the injectable time contract.
`FakeClock` is the test clock every time-dependent server object is driven
with (phase 05: "no test sleeps — all time via `FakeClock`"); these tests
lock its behaviour so that contract is honest.
"""
from __future__ import annotations
import pytest
from undercooked.server.clock import Clock, FakeClock, SystemClock
pytestmark = pytest.mark.unit
def test_system_clock_reads_a_monotonic_clock() -> None:
clock = SystemClock()
first = clock.now()
second = clock.now()
assert second >= first
def test_system_clock_accepts_a_substitute_time_function() -> None:
values = iter([1.0, 2.5, 7.25])
clock = SystemClock(lambda: next(values))
assert clock.now() == 1.0
assert clock.now() == 2.5
assert clock.now() == 7.25
def test_fake_clock_starts_at_the_given_instant() -> None:
assert FakeClock().now() == 0.0
assert FakeClock(start=12.5).now() == 12.5
def test_fake_clock_advances_and_reports_the_new_instant() -> None:
clock = FakeClock(start=10.0)
assert clock.advance(5.0) == 15.0
assert clock.now() == 15.0
assert clock.advance(0.5) == 15.5
assert clock.advance(0) == 15.5
def test_fake_clock_never_goes_backwards() -> None:
clock = FakeClock()
with pytest.raises(ValueError, match="cannot go backwards"):
clock.advance(-1.0)
assert clock.now() == 0.0
def test_both_clocks_satisfy_the_protocol() -> None:
"""The `Clock` protocol is a structural contract: both implementations
must be usable anywhere a `Clock` is demanded."""
clocks: list[Clock] = [SystemClock(), FakeClock()]
for clock in clocks:
assert isinstance(clock.now(), float)