Skip to content

fix(usersui): distinguish a missing profile from a failed lookup [JDWLABS-492] - #226

Merged
jdwillmsen merged 2 commits into
mainfrom
fix/JDWLABS-492-usersui-profile-error-handling
Sep 6, 2026
Merged

fix(usersui): distinguish a missing profile from a failed lookup [JDWLABS-492]#226
jdwillmsen merged 2 commits into
mainfrom
fix/JDWLABS-492-usersui-profile-error-handling

Conversation

@jdwillmsen

Copy link
Copy Markdown
Member

Summary

  • ProfilesService.handleError ended every failure with return EMPTY, so subscribers never received an error notification. profile.component.ts's error.status == 404 branch was dead code, and the page fell back to its field initializer's type = 'Add' for any failed lookup — a routing 404, a 401, a 500 — not just a genuine no-profile response.
  • handleError now rethrows via throwError (matching the sibling data-access services' "always snackbar" convention, just propagating instead of swallowing). getProfile distinguishes a genuine "no profile" 404 — the profile-service contract's ResourceNotFoundException text/plain body — from any other 404 via the new isProfileNotFoundError helper, so only that case reaches profile.component as Add; everything else now surfaces a load-error state instead of a blank create form.
  • Audited every other ProfilesService consumer for the same swallowed-error assumption: profiles-action-button-cell-renderer, user.component (deleteProfile/deleteAddress/deleteIcon), address.component, icon.component, and profiles.component all subscribed without an error handler, which is now unhandled instead of silently dropped. Added no-op error handlers there (the service already raises a snackbar on failure), and profiles.component's handler also clears its loading flag.
  • Fixed the e2e "Profile Form › renders in create mode" spec, which had been passing only because the swallowed-error bug happened to leave the page in its default Add state when the (absent, in this suite) backend was unreachable — it now mocks the genuine no-profile 404 explicitly.

Test plan

  • npx nx run-many -t lint test -p frontend-usersui-data-access frontend-usersui-feature-core — green (30 + 51 tests, 0 lint errors)
  • npx nx format:check --all — clean except pre-existing, untouched apps/backend/usersrole/build/** artifacts
  • npx nx e2e platform-e2e — 77/77 passing (includes the updated Profile Form specs)
  • TDD: added specs verified red against the pre-fix code (stashed the implementation, confirmed 4 failures), then green after restoring the fix

Specs added

  • profiles.service.spec.ts: getProfile — no-profile 404 (no snackbar), routing 404 with a different body (snackbar + error), 500 (snackbar + error); handleError — propagates the original error instead of completing; isProfileNotFoundError — text/plain 404 true, other-body 404 false, non-404 false, non-HttpErrorResponse false
  • profile.component.spec.ts: "shows the Edit form when a profile already exists", "shows the Add form when the user has no profile yet", "shows an error state for a routing 404 with a different body", "shows an error state for a 500"
  • users.spec.ts (e2e): "Profile Form › renders in create mode" now mocks the genuine no-profile 404 instead of relying on an unreachable backend

🤖 Generated with Claude Code

https://claude.ai/code/session_01FvYBM6o7wARm2v9jmDp1yY

jdwillmsen and others added 2 commits September 6, 2026 04:57
ProfilesService.handleError swallowed every failure into EMPTY, so
subscribers never got an error notification. profile.component's
error.status == 404 branch was dead code, and the page fell back to
its field initializer's Add form for any failed lookup — a routing
404, a 401, a 500 — not just a genuine no-profile response.

handleError now rethrows via throwError so callers can react to a
real failure, matching the sibling data-access services' snackbar
convention. getProfile distinguishes a genuine "no profile" 404 (the
service's ResourceNotFoundException text/plain body) from any other
404 via the new isProfileNotFoundError helper, so only the former
reaches profile.component as "Add" — everything else now surfaces a
load error instead of a blank create form.

Audited every other ProfilesService consumer for the same
swallowed-error assumption: profiles-action-button-cell-renderer,
user.component (deleteProfile/deleteAddress/deleteIcon),
address.component and icon.component all subscribed without an
error handler, which is now unhandled instead of silently dropped.
Added no-op error handlers there, since the service already raises
a snackbar on failure.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvYBM6o7wARm2v9jmDp1yY
Three gaps from PR review on #226:

- profiles.service.spec.ts's error-path tests used
  `subscribe({ next: () => fail(), error: (e) => expect(...) })`, but if
  handleError ever regresses to EMPTY, the observable completes without
  calling either callback and the assertions inside `error` never run —
  the suite stays green with the bug back. Added `expect.assertions(n)`
  to each of the 10 affected tests so a future regression fails loudly
  instead of silently. Verified by temporarily reintroducing the EMPTY
  return: 9 of the 10 now fail red (the 10th tests the no-profile bypass
  path, which doesn't call handleError and is correctly unaffected).

- profiles.component.ts's new `error: () => { loading = false }` handler
  had no coverage. Added a spec that fails `getProfiles` and asserts
  `loading` clears without an unhandled error.

- user.component.ts's three new delete error handlers
  (deleteProfile/deleteAddress/deleteIcon) had no coverage. A real
  `throwError(...)` reports unhandled errors asynchronously, which a
  synchronous `not.toThrow()` around the call site can't observe, so
  each spec instead captures the exact argument passed to `subscribe`
  and asserts it carries a real `error` function that runs without
  throwing and does not trigger a reload. Verified by temporarily
  reverting the handlers to bare next-only subscribes: all three new
  specs fail red.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvYBM6o7wARm2v9jmDp1yY
@jdwillmsen
jdwillmsen merged commit 39af868 into main Sep 6, 2026
22 checks passed
@jdwillmsen
jdwillmsen deleted the fix/JDWLABS-492-usersui-profile-error-handling branch September 6, 2026 05:19
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