Skip to content

test(auth): pin that a 401 answer to logout resolves and signs out - #33

Merged
knittingcode merged 4 commits into
mainfrom
test/fcrm-auto4-logout-401
Oct 29, 2026
Merged

knittingcode merged 4 commits into
mainfrom
test/fcrm-auto4-logout-401

Conversation

@knittingcode

@knittingcode knittingcode commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds frontend/src/auth/AuthProvider.test.tsx, which tests AuthProvider.logout() directly. It pins that a 401 answer to POST /api/v1/auth/logout resolves and leaves the user signed out. This PR changes tests only; there is no behaviour change.

Motivation and technical context

This is a follow-up from the #32 review (Low). AuthProvider.logout() treats a 401 as "the session had already ended": it resolves and sets the state to anonymous with ended: false. Any other failure rejects and keeps the user signed in.

No test pinned the 401 branch:

  • When a request is answered 401, apiRequest calls the global unauthenticated listener, and that listener already sets the state to anonymous.
  • So the App-level test "treats a 401 to the sign-out as a session that has already ended" still reaches the login page if logout() rethrows the 401.
  • The AppShell then unmounts before its failure alert can render, so the test passes either way.

Implementation

frontend/src/auth/AuthProvider.test.tsx renders the AuthProvider with a small probe component. The probe shows the auth state and gives the test the context, so the test can await the promise that logout() returns. The fake API is the existing src/test/fakeApi.ts.

There are three cases:

  • 401: the promise resolves, and the state is anonymous ended=false (signed out by the user, not "your session has ended").
  • 204: the promise resolves, and the state is anonymous ended=false.
  • 500: the promise rejects with status 500, and the state stays authenticated. This is the contrast case for the 401 branch.

Testing performed

Synced with main 1badf76 (2026-10-28)

The branch was brought up to date with a normal merge of origin/main 1badf76 (#38, #42; #39 was already in), head aefca2e5. The merge was clean, and the PR diff against main is still only frontend/src/auth/AuthProvider.test.tsx. AuthProvider.tsx, AppShell.tsx, App.test.tsx, the backend logout controller, security.yaml, RequestOriginGuard and their tests are identical to main.

Local checks on the merged tree (Node 24.13, npm ci --ignore-scripts; the engine field asks for 24.15 or later):

  • Vitest: 5 files, 44 passed (44). tsc --build and eslint --max-warnings=0 .: exit 0.
  • Mutants of AuthProvider.tsx, each reverted afterwards:
    • A 401 also rethrows: only the new 401 test fails (1 failed, 43 passed).
    • Every failure is swallowed: the new 500 test and the 4 App-level "sign-out does not complete" tests fail (5 failed, 39 passed).
    • A completed logout sets ended: true: only the new 401 and 204 tests fail (2 failed, 42 passed).

CI run 40752031731 on aefca2e5 (workflow CI, pull_request): success. Allocator unit 288/288, allocator integration 140/140 (24 scenarios), PHPUnit unit OK (156 tests), integration OK (278 tests, 2537 assertions), 53 of 53 negative controls, backend 18/18 (BE-03 composer audit passed), Vitest 44/44 (frontend 5/5), application stack 124/124, production-like 39/39, container scan 20/20; guard 36/36, 73/73, 87/87, 221/221.

Earlier sync with main 202ea9e (historical)

The branch was brought up to date with a normal merge of origin/main 202ea9e (#39), head 6d729805. The merge was clean, and the PR diff against main is still only frontend/src/auth/AuthProvider.test.tsx.

Local checks on the merged tree (Node 24.13, npm ci --ignore-scripts):

  • Vitest: 5 files, 44 passed (44). tsc --build and eslint --max-warnings=0 .: no errors.
  • Mutants of AuthProvider.tsx, each reverted afterwards:
    • A 401 also rethrows: only the new 401 test fails (1 failed, 43 passed).
    • Every failure is swallowed: the new 500 test and 4 App-level tests fail.
    • A completed logout sets ended: true: only the new 401 and 204 tests fail.

CI run 40669321115 on 6d729805: failure, not caused by this PR. Repository guard passed (36/36, 73/73, 46/46). Dev environment failed in Backend checks at BE-03 composer audit, because advisory PKSA-qhjm-rpqc-qkn6 (GHSA-9vw2-f4vp-x7gf) was published on 2026-10-27 for phpunit/phpunit >=13.4.0,<13.4.4. Main locks 13.4.3. The rest of that step passed: unit OK (156 tests), integration OK (278 tests, 2537 assertions), 53 of 53 negative controls, backend TOTAL 18 passed=17 failed=1. Because of that failure, the Frontend checks, Application stack, Production-like stack and Trivy steps were skipped. A green run needs the phpunit update on main first, followed by another sync.

Original run (main e03d151 plus the test)

All checks ran in the dev web image (flowcrm-1d1ca1-web, read-only mounts, --network none).

On this branch:

  • tsc --build: TSC_OK
  • eslint --max-warnings=0 .: ESLINT_OK
  • Vitest: 5 files, 44 passed (44)
  • vite build: BUILD_OK

Against a deliberately broken copy: AuthProvider.tsx was changed so that a 401 also rethrows (one added throw error; after the 401 check). The rest of the source was this branch, run in a scratch copy:

  • Vitest: 1 failed, 43 passed (44). The failure is the new test "resolves on a 401, as the session has already ended on the server, and is signed out", with promise rejected "ApiProblem: unauthenticated …" instead of resolving.
  • Every pre-existing test, including the App-level 401 sign-out test, still passed against that broken copy. That confirms the gap this test closes.

CI run 40192849583 on f33745b: success. It reported unit 230/230, integration 140/140, backend 18/18, Vitest 44/44 (5 files; frontend 5/5), application stack 114/114, production-like 38/38, container scan 14/14, and guard 36/36 and 73/73.

Security considerations

This PR changes tests only. The test pins the #32 rule that only a completed logout (204) or an already-ended session (401) signs the user out in the SPA. Every other failure keeps the user signed in and shows an alert.

Known limitations

  • The 401 case is defensive. backend/openapi.yaml lists 204, 403, 415 and 500 for logout, not 401, but the SPA handles a 401 the same way as everywhere else.
  • No Playwright change.

The global 401 listener also ends the state, so the App tests passed
whether AuthProvider.logout() resolved or rejected on a 401. Test the
provider's promise directly: a 401 and a 204 resolve and leave the state
anonymous (signed out, not "session ended"), a 500 rejects and keeps
the user signed in.
@knittingcode
knittingcode merged commit ac41bea into main Oct 29, 2026
2 checks passed
@knittingcode
knittingcode deleted the test/fcrm-auto4-logout-401 branch October 29, 2026 17:52
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.

1 participant