test(auth): promote the §4 tail to @stable — API-driven rewrite after upstream removed the OSS Admin Page (#1542, #1543) - #1545
Merged
Conversation
… upstream removed the OSS Admin Page (#1542, #1543) Validates and promotes the auth directory (13 checklist bullets -> [x], +1 new): autoLogin, login-invalid-credentials, session-expired, admin-password-change, auto-login-off and a rewritten admin-user-management, with spec docs for all six (none existed). Upstream removed the OSS Admin Page in langflow-ai/langflow#14276 (2026-08-05): no menu item (null stub), no admin route. The rewrite drives /api/v1/users/ instead and proves every lifecycle state at the login endpoint — including the product's two distinct refusal branches (never-logged-in inactive: 400 "Waiting for approval"; deactivated after use: 401 "Inactive user", read from the shipped authenticate_user) — and adds a guard pinning the removal itself, so an EE admin surface leaking back into OSS fails by name. The rewrite also buries the hardcoded legacy password "langflow" (refused since 1.11.0.dev29, #510) that made every admin login fail. auto-login-off keeps its two real subjects (login screen + two-way flow isolation), provisions the second user via API, and gains the id-scoped cleanup it never had (it leaked one user and two flows per run). New shared helpers absorb the login endpoint's per-IP rate limit (5/min, fixed window, every attempt counts): login-request.ts for API logins and sign-in-through-form.ts for the browser, both waiting out the server-named retry_after (a string, converted) and never hiding a non-429 verdict. Measured collisions motivated them: logout-flow timed out right after auto-login-off's three form logins, and admin-password-change's own UI login met the window its API calls had just spent. login-invalid-credentials pins its refusal to a 401 status so a hot window cannot pass it for the wrong reason; login-rate-limit.spec.ts stays raw on purpose — there the 429 is the subject. Validated on nightly 1.12.0.dev33: burst 3x back-to-back (windows deliberately hot) 18/18 each at --retries=0 --workers=1, zero flaky, zero backend errors; one absorbed 429 visible as +60s in run 3. Two defect-simulating force-fails red with named messages, then reverted. Unit lane covers the helper (6 cases). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1542. Closes #1543.
Moves the auth tail — the
ROADMAP.mdpool item "ready to date" — to@stable: 13QA-CHECKLIST.md§4.1/§4.2 bullets[-]→[x], plus one new bullet the audit earned. Six spec docs authored (none existed). Everything measured on nightly1.12.0.dev33.The audit that reshaped the batch
Baseline (
--retries=0 --workers=1): 13/17 pass. The 4 reds decomposed into two product facts and one suite defect:nullstub and the SPA router registers no admin path (pages/AdminPage/upstream holds only the/login/adminLoginPage).admin-user-managementandauto-login-offdied at theAdmin Pageclick.admin-user-managementlogged in with hardcoded"langflow", refused since1.11.0.dev29(Harness break: upstream disables legacy default superuser password under AUTO_LOGIN — breaks all manual-login tests #510) —helpers/auth/credentials.tsexists for exactly this and was never imported. Every admin login failed before even reaching the removed page.POST /api/v1/loginis limited to 5/min per client IP, fixed window, counted before authentication.logout-flow(already@stable) timed out onmainpage_titleright afterauto-login-off's three form logins; lateradmin-password-change's UI login met the window its own four API calls had just spent — snapshot shows the form submitted and parked.1. Covered tests (18 in the directory, 13 promoted + 1 new)
admin-user-management.spec.ts/api/v1/users/, proving every state at the login endpoint: created-inactive →400 "Waiting for approval"; activate →200+access_token; deactivate →401 "Inactive user"; rename → new username200, old401. The two refusal branches are the product's own (read from the shippedauthenticate_user: never-logged-in vs deactivated-after-use) and each test pins one — the first run of this rewrite failed on exactly that distinction. New 4th test pins #14276 itself: menu open + noAdmin Pageitem,/adminfalls through with the old page's own marker (Search Username) absent — an EE admin surface leaking back into OSS fails by nameauto-login-off.spec.tsadmin-password-change.spec.tslogout-flow.spec.ts@stable; its 3 raw form logins swapped to the helper — it was the other measured victimautoLogin.spec.tslogin-invalid-credentials.spec.tsError signing intoast for a 429, so the title-only assert could pass for the wrong reason — the refusal is now pinned to a 401 status through the helpersession-expired.spec.tsOut of scope, stated:
login-rate-limit.spec.tsstays[-]—@destructive(instance-global budget),@stableforbidden on it (#1010), bullet carries the lane. It also deliberately does not use the new helpers: there the 429 is the subject.2. How it was built
tests/helpers/auth/):login-request.ts(postLogin— API side) andsign-in-through-form.ts(browser side, response captured viawaitForResponseregistered before the click, so the status read cannot race the navigation). Both absorb only429, waiting out the server-namedretry_after— a string, not a number, converted with a +1 s edge margin — and return every other status untouched, so no credential verdict is ever hidden. Unit-tested (6 cases,npm run test:units).services/auth/service.py::authenticate_user), not guessed: the 400-vs-401 inactive branches above.const H_i = e => null, router path table with noadminentry, zeroSearch Usernameoccurrences) and upstream refs (pages/AdminPage/= LoginPage only onrelease-1.12.0).afterEach/finally; both isolation flows deleted by ids captured from the editor URL. Nothing name-based, nothing delete-all.3. Dependencies
get-auth-token(auto-login endpoint, spends no form budget).--workers=1as the auth directory always has (shared login budget is per-IP); parallel-safe against the rest of the suite.Validation (nightly
1.12.0.dev33,--retries=0 --workers=1)🚨 Backend Error. Run 3 shows +60 s — one absorbed 429, visible and green: the in-vivo proof of the helper, stronger than a mocked one.Expected "Pending review" · Received "Waiting for approval") and the isolation count flip (Expected 1 · Received 0).\rprogress lines.typecheck0 errors ·lint0 errors ·test:unitsgreen (incl. the new helper's 6 cases) · checklist guard + coverage guard +validate:specsgreen.--trace=onskipped deliberately: hangs UI specs on this local Docker VM (Colima) — known constraint; CI captures trace on first retry.🤖 Generated with Claude Code