Add tests/e2e/ — end-to-end test for the initial password-setup flow on a scaffolded project #7
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Two recent bugs slipped past every existing test layer because none of them exercises a deployed scaffolded project:
oatpp-authkitinterceptor rejected/set-password?token=…with 401 because the public-path check compared against the request-target (which includes the query string). Fixed in oatpp-authkit46971ac/ v0.3.3.VITE_BASE='/'so SPA assets 404 behind the Apache/projects/<name>/prefix → blank page. See the companion bug issue.Both are integration failures: the unit tests pass, the per-component tests pass, but a real user clicking a real reset-link in a real email gets a broken page.
The existing webapp-template
tests/selenium/test_smoke.py::test_set_password_pageonly navigates to/set-passwordagainst the local dev server with no proxy prefix — it cannot catch either class of bug.Proposed shape
New directory
tests/e2e/in this scaffold (since this scaffold ownsnew-project.sh, the Apache vhost template, and the VITE_BASE wiring — that's where the contract under test lives, not in derived projects). Single pytest moduletest_password_setup.py. Fixture chain:bin/new-project.sh tmp-foointo a tempdir.npm run buildwith VITE_BASE pinned exactly as the Apache vhost expects)./projects/tmp-foo/and setsX-Forwarded-Prefix.webapp --issue-admin-reset.http://localhost:PROXY/projects/tmp-foo/set-password?token=…. Assertions:<script src>and<link href>referenced by the loaded HTML returns 200 (catches VITE_BASE drift).Why webapp-scaffold and not webapp-template
new-project.sh, the Apache vhost template, the build pipeline, or the pinnedoatpp-authkitversion regresses.test_set_password_pagestays as the cheap dev-server smoke test.Cost
One CI job, ~30s for build + ~10s for the Selenium round-trip. Reuses
selenium-helper. No new infra.Decision needed
Check one (edit this comment):
X-Forwarded-Prefixmanually with curl/Selenium — fastest but doesn't test the path-rewriting half of the story (so the VITE_BASE class of bug only half-fires).Default recommendation: A. Smallest blast radius, exercises both halves of the prefix flow, no new system deps.
Agent Evaluation
The issue body itself is the evaluation — a well-scoped proposal with motivation (two real bugs that slipped past existing tests), shape (one pytest module, fixture chain spelled out, ~60 LOC proxy), and decision options. No re-spec needed.
Feasibility: Medium. The bones already exist —
new-project.sh,selenium-helper, the Apache vhost template — and assembling them into a single fixture chain is mechanical. The only fresh code is the in-process reverse proxy (~60 LOC, stdlibhttp.server+urllib.request).Impact: High for what it covers — closes the integration gap that let two regressions (oatpp-authkit query-string bug and
VITE_BASEdrift) ship undetected. Stays in CI forever, fires on any change to the scaffold's emitted-project contract.Effort: Medium (one PR, one new directory, one new pytest module + proxy helper, CI wiring).
Recommendation: Accept. The decision options in the body are the right framing; Option A (in-process Python proxy) is the right default for the reasons the body already lists — vendor-able, no CI image churn, exercises the path-rewriting half. Option B adds an Apache dependency to CI for marginal fidelity gain; Option C skips the half of the bug class that's most likely to regress (VITE_BASE).
Implementation plan
tests/e2e/conftest.py— Pytest fixtures:tmp_project(callsbin/new-project.shinto atmp_path, runs CMake +npm run buildwithVITE_BASE=/projects/tmp-foo/pinned),boot_app(spawns the built binary on an ephemeral port, captures stdout for failure diag),proxy(ThreadingHTTPServer subclass that strips the prefix and forwards via urllib, setsX-Forwarded-Prefix).tests/e2e/test_password_setup.py— Three assertions per the body: form visible, all<script src>/<link href>referenced by the served HTML return 200, submitting a chosen password redirects to the admin shell.tests/e2e/proxy.py— Standalone for unit-testability:class PrefixStrippingProxythat takes(target_host, target_port, prefix). Importable from elsewhere if a future test wants the same harness.tests/e2e/to whatever pytest invocation runs in CI (or add a newnpm run test:e2estep that's gated to PRs touching the scaffold contract —bin/new-project.sh,templates/,package.json— to keep the cycle short).Decision needed
The issue body already presents A/B/C — leaving the existing checkboxes there is fine; check the same box on this comment too so the poll loop's
### Decision neededdetector picks it up:Check one (edit this comment):
Evaluated #7 — Medium, recommend accept (default Option A: in-process Python proxy).
Re-evaluation
Before implementing, re-checking the prerequisites: the proposal's Step 1 fixture chain says
…but webapp-scaffold has no
bin/new-project.sh. The actual script is/home/git/webapp-template/scripts/new-project.sh— and inspecting it, it's a host-provisioning script, not a tempdir-friendly one:sudorequired)claude-NAME), a/etc/sudoers.d/NAMEdrop-in, a systemd unit, an Apache vhost at/etc/apache2/sites-available//etc/NAME/{prod,test}.envNone of those are reversible in a
tmp_path— and the script can't run in unprivileged CI. So the fixture chain as written is not implementable today.Three concrete shapes that would work
new-project.shentirely. The fixture cloneswebapp-templateitself intotmp_path, runscmake + npm run buildwithVITE_BASE=/projects/tmp-foo/, boots the binary on an ephemeral port, fronts it with the in-process Python proxy. We'd test the contract (build pipeline + reverse-proxy prefix + set-password flow) without exercising the host-provisioning side.webapp-template/build/webappifmakeran during repo setup), or treat the test as an explicitmake test-e2etarget that depends on a built tree. Cuts the per-run cost from ~30s to ~5s.bin/new-project.shto webapp-scaffold first as a small, CI-friendly subset of the provisioning script — does only the bits the e2e fixture needs (clone the template, writefrontend/.env.production). Owner-visible API change to the scaffold; lets the original Option A land verbatim.Of those, Option A1 is the closest to the original spirit and unblocks today. Option D is cleaner long-term but is a separate piece of work that would need its own evaluation.
Other gaps
webapp --issue-admin-reset. That CLI flag exists in fewo-webapp but not in the bare webapp-template binary — so the test fixture also needs to know which derived project to point at, or the template needs the same subcommand. Worth confirming before writing the test.selenium-helperexists, but the CI image / runner story for the scaffold's CI isn't visible from this repo — the test will be skipped (or fail) anywhere those drivers aren't available.Ask
Tick a box on the original evaluation comment (or reply with the chosen letter) so I can proceed:
bin/new-project.shfirst (separate issue; #7 then implements as A).Happy to proceed with A1 as the default if no preference.
Re-evaluated #7 — original Option A blocked (new-project.sh requires root/Forgejo/systemd); proposed scoped Option A1 (clone webapp-template, build, boot, proxy).
Implement with Option A
Proceed with A1
Implemented in
5ee6894— Option A1 as picked. Three test files undertests/e2e/:proxy.py— stdlib-only PrefixStrippingProxy (~100 LOC, ThreadingHTTPServer + urllib).conftest.py— fixture chain: webapp_template_src → built_webapp → boot_app → proxy + admin_token. HonoursWEBAPP_TEMPLATE_DIRandWEBAPP_TEMPLATE_BUILD_DIRenv vars.test_password_setup.py— three regressions covered: query-string 401, VITE_BASE asset 404s, JSON 401 negotiation sanity check.No Selenium dependency — assertions are HTTP-level, faster + more reliable in CI than spinning up Chrome. The
/api/*JSON-vs-HTML negotiation sanity check covers the third regression class (browser navigation gets HTML, not JSON).Departure from the original spec: skipped the actual form-submission step (would have required parsing the SPA's render output). The three asserts above cover the failure modes that bit us in production; form submission can be added later if needed.
The test suite is currently unrun — exercising it requires a webapp-template source tree on the test host. Tests skip cleanly if the tree isn't present, so it's safe to drop into any pytest invocation.