Skip to content

Add e2e + manual QA test harness - #2

Merged
DaveHomeAssist merged 2 commits into
mainfrom
claude/test-harness
Jul 12, 2026
Merged

Add e2e + manual QA test harness#2
DaveHomeAssist merged 2 commits into
mainfrom
claude/test-harness

Conversation

@DaveHomeAssist

Copy link
Copy Markdown
Owner

Why

The merged PR #1 added an in-process unit/integration suite (Starlette TestClient, heavy stack mocked). That never opens a socket or runs uvicorn's lifespan, and it can't touch real audio/model/extension. This PR adds the two layers that fill those gaps.

Layer 1 — Live end-to-end (tests/e2e/, CI-runnable)

Boots the real ASGI app under uvicorn on an ephemeral port in a background thread and drives it with a stdlib HTTP client (no httpx/requests). Proves the contract holds through a real HTTP stack:

  • GET / → 307 → /control; /control serves HTML
  • /status, /voices shapes over the wire
  • /speak dispatch and 422 validation; /stop
  • PATCH /config redacts *** in the response while round-tripping the real key to an actual config file on disk (the unique e2e check)
  • CORS over the wire mirrors the unit policy: allowed origins echoed, hostile/suffix-attack/null origins get no header, never *, preflight allows PATCH from the extension and is blocked for disallowed origins

Audio/model leaf calls (tts_engine.speak/stop_audio) are stubbed, so it needs no torch/kokoro/audio. Tests are marked e2e and skip cleanly if uvicorn isn't installed.

Layer 2 — Manual / dev QA (docs/QA.md, scripts/)

For what CI physically can't do:

  • docs/QA.md — structured checklist: first-run model download, real audio, file save, OpenAI/ElevenLabs with live keys, control panel, the Chrome extension, security smoke (issue 002/003), and the macOS 26 Tk skip (issue 001).
  • scripts/qa_probe.py — zero-dep stdlib driver against a running daemon: status/voices/speak/stop/config/cors. speak plays real audio; cors --origin reports ALLOWED/BLOCKED and warns if a plaintext key ever appears in a response.
  • scripts/smoke.sh — boots the app as a subprocess (isolated HOME), probes /status//voices and asserts CORS rejects a hostile origin.

Plumbing

  • uvicorn added to requirements-dev.txt
  • e2e marker registered in pytest.ini
  • CLAUDE.md Testing section documents all three layers + commands

Testing

python -m pytest72 passed (51 unit + 21 e2e). python -m pytest -m "not e2e" → 51 passed, 21 deselected. scripts/smoke.sh → SMOKE PASSED. Verified locally; CI runs layers 1+2 on 3.10–3.12.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a55173c59e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/e2e/conftest.py

# The e2e layer needs the real server, so skip cleanly if it isn't installed
# (e.g. a minimal `pip install pytest` unit-only environment).
pytest.importorskip("uvicorn")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Move uvicorn skip out of conftest import

In an environment where uvicorn is not installed, this top-level pytest.importorskip runs while pytest is importing tests/e2e/conftest.py, so collection aborts with a skipped-exception error before marker selection; even the documented python -m pytest -m "not e2e" unit-only flow cannot run in a minimal unit-test environment. Put the dependency check in the e2e fixture/test modules or a collection hook that skips only the e2e tests.

Useful? React with 👍 / 👎.

LavaLabs111 and others added 2 commits July 11, 2026 20:25
Two new layers on top of the in-process unit suite:

- tests/e2e/: boots the real ASGI app under uvicorn on an ephemeral port and
  drives it over a real socket (stdlib client). Validates routing/redirects,
  CORS over the wire, /speak dispatch, and PATCH /config redaction with a real
  config-file round-trip. Audio/model leaf calls stubbed (no torch/kokoro).
  Marked 'e2e'; skips cleanly if uvicorn absent.
- docs/QA.md + scripts/qa_probe.py + scripts/smoke.sh: manual/dev layer for
  what CI can't do (real audio, model download, live keys, the extension).
  qa_probe drives a running daemon; smoke.sh boots it as a subprocess.

72 tests pass (51 unit + 21 e2e); 'pytest -m "not e2e"' runs the fast 51.
uvicorn added to requirements-dev.txt; e2e marker registered in pytest.ini.
Since this branch was cut, main hardened the CORS policy: extension
origins are no longer allowed by default and must be configured via
allowed_origins / READOUT_ALLOWED_ORIGINS (see tests/test_server_cors.py,
which configures the same test extension ID). The live-server fixture now
sets READOUT_ALLOWED_ORIGINS to the test extension origin so the
over-the-wire CORS assertions exercise the same contract.
@DaveHomeAssist
DaveHomeAssist merged commit 709b176 into main Jul 12, 2026
6 checks passed
@DaveHomeAssist
DaveHomeAssist deleted the claude/test-harness branch July 12, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants