Make the smoke test sign in; enforce SQLite foreign keys
Closes known issue 3. server/smoketest.py predated the login portal and had no
login step at all, so auth_gate refused every route after /api/health and the
documented way to verify a deploy reported a wall of failures against a healthy
stack.
- Signs in first, holding the session in an http.cookiejar on a shared opener.
urlopen() has no cookie support, which is why the session was dropped.
- Credentials from WP_SMOKE_USER / WP_SMOKE_PASSWORD, or --user/--password, so
a password need not land in shell history. Refuses to start without them
rather than running headlong into 401s.
- Checks the signed-in role up front and warns when it cannot archive or delete
a project, instead of failing six checks later for an unexplained reason.
- New exit code 2 for "could not run" (unreachable, or credentials missing or
rejected), kept distinct from 1 "ran and found problems".
- Also asserts the session is accepted on an authenticated route and refused
after sign-out; signs out at the end so a run on a shared host leaves none.
The working smoke test immediately caught a real bug: SQLite ships with foreign
keys disabled and the pragma is per-connection, so every ondelete="CASCADE" was
silently a no-op on dev while working on Postgres. Deleting a project orphaned its
SOPs, work packages and membership rows; deleting a user orphaned theirs. db.py
now sets PRAGMA foreign_keys=ON for SQLite, so dev matches production.
Enforcing them exposed two things that had been getting away with it:
- create_user adds an account and its ProjectMember rows in one flush, and the
ORM takes flush order from relationship() declarations. models.py has none by
design, so it emitted the child INSERT first and the database rejected it.
Fixed with a db.flush() after the account, and documented at the top of
models.py so the next same-flush pair does not rediscover it. The other three
call sites already commit the parent first.
- A write aimed at a since-deleted project used to leave an orphan row; with FKs
enforced it would have been an IntegrityError surfacing as a 500, which the
browser outbox retries forever (it only retires 4xx). require_project_writable
now refuses a vanished project with 409, like the archived case beside it.
Verified: smoke test 27/27 exit 0 against a live server (the cascade assertion now
passes on SQLite, which is what used to fail); credentials missing and credentials
rejected both abort cleanly with exit 2 and no stray PASS lines; a project_user run
warns up front and fails as described. Scope tests 93/93, live HTTP checks 29/29,
static JS checks 33/33. No orphan rows left in the database afterwards.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -146,20 +146,37 @@ docker compose exec db psql -U wpsuite -d wpsuite -c "select id, name from proje
|
||||
|
||||
### Automated smoke test
|
||||
|
||||
`server/smoketest.py` exercises the whole stack end-to-end (health → project →
|
||||
SOP → Work Package → the AWP issue gate → status → metrics → comments → cascade
|
||||
cleanup). Stdlib only — no pip/jq.
|
||||
`server/smoketest.py` exercises the whole stack end-to-end (health → sign-in →
|
||||
project → SOP → Work Package → the AWP issue gate → status → metrics → comments →
|
||||
archive round trip → cascade cleanup → sign-out). Stdlib only — no pip/jq.
|
||||
|
||||
It **signs in first**, because every `/api/` route except `/api/health` requires a
|
||||
session. Credentials come from the environment so a password stays out of shell
|
||||
history, and the account must be an **admin**: the run creates a project and deletes
|
||||
it again, and archiving or deleting one takes Project Admin on it. The script checks
|
||||
the signed-in role up front and warns if it is too low rather than letting you find
|
||||
out in the cleanup step.
|
||||
|
||||
```bash
|
||||
export WP_SMOKE_USER=<admin-account>
|
||||
export WP_SMOKE_PASSWORD='…'
|
||||
|
||||
# Through the proxy (use --insecure for a self-signed internal cert):
|
||||
python3 server/smoketest.py https://wp-suite.company.local --insecure
|
||||
|
||||
# Or from inside the api container (hits FastAPI directly):
|
||||
docker compose exec api python /app/server/smoketest.py http://localhost:8000
|
||||
# Or from inside the api container (hits FastAPI directly). Pass the vars through:
|
||||
docker compose exec -e WP_SMOKE_USER -e WP_SMOKE_PASSWORD api \
|
||||
python /app/server/smoketest.py http://localhost:8000
|
||||
|
||||
# Add --keep to leave a demo project in the DB so you can open it in the UI.
|
||||
# --user / --password override the environment if you'd rather be explicit.
|
||||
```
|
||||
|
||||
Exit codes: **0** all checks passed · **1** one or more checks failed · **2** the run
|
||||
could not start (host unreachable, or credentials missing or rejected). The last is
|
||||
kept separate on purpose — "I could not test this" is a different answer from "this is
|
||||
broken", and automation should not treat them alike.
|
||||
|
||||
Exit code 0 and "ALL PASS" means the API, the Python logic, and SQL are all
|
||||
working. It cleans up after itself (the test project and its SOP/WPs are
|
||||
deleted via cascade); a single tagged test comment remains (there's no comment
|
||||
|
||||
Reference in New Issue
Block a user