bug: Parent's child sponsorship toggle bypasses the profile-completion check that admin approval enforces #129

Closed
opened 2026-05-12 00:31:04 +00:00 by spikerj · 1 comment
Owner

Summary

From Profile → Children → (child) → Sponsorship, a parent can flip the "Allow donors to sponsor {child}'s travel" toggle on even when the child's profile is not complete. The corresponding admin flow at /admin/sponsorship-management correctly rejects approval with an error like:

Profile incomplete — missing: Handle, Bio, Sex, Nationality, Phone Number, Government ID Type, Government ID Number, Street Address, City, State / Province, Postal Code, Home Country, At least 1 photo, At least 1 language

The parent-side toggle should enforce the same prerequisite. Otherwise an incomplete child profile can be marked Sponsorship.Enabled = true, which then queues it as a pending profile in the admin's sponsorship management view (e.g. "Bobby Spiker — Awaiting approval") despite not meeting the criteria.

Steps to reproduce

  1. Sign in as a parent.
  2. Open your profile page → Children tab → expand a child whose profile is incomplete (e.g. Bobby Spiker, missing Bio / Phone Number / Address / etc.).
  3. Toggle on "Allow donors to sponsor Bobby's travel".
  4. Observe that the toggle saves successfully and the child appears in /admin/sponsorship-management under Pending Approval.
  5. Compare to the admin's Approve flow on that same profile — it returns Profile incomplete — missing: … and refuses.

Expected behavior

When the parent attempts to enable sponsorship for a child, the backend should run the same ProfileCompletionValidator check that SponsorController.ApproveSponsorProfile runs. If the child profile is incomplete, the toggle request should fail with a 400 and a missingItems payload, and the UI should surface the same

Profile incomplete — missing: …

message so the parent knows what to fix on the child's profile. Disabling sponsorship (enabled = false) should always be allowed regardless of completion state.

Affected code

  • Backend: SpikerSoft.Business/Domain/Profile/Commands/ToggleChildSponsorship/ToggleChildSponsorshipCommandHandler.cs — currently writes Sponsorship.Enabled = request.Enabled with no completion check.
  • Backend (reference): SpikerSoft.Api/Domain/Sponsor/SponsorController.ApproveSponsorProfile — already calls ProfileCompletionValidator.Validate(profile) and returns BadRequest(new { error, missingItems, percentage }).
  • Frontend: projects/spikersoft/src/app/_components/profile/profile.component.ts → toggleChildSponsorship — needs to surface missingItems in the snackbar (mirroring SponsorshipManagementComponent.approveProfile).
## Summary From **Profile → Children → (child) → Sponsorship**, a parent can flip the **"Allow donors to sponsor {child}'s travel"** toggle on even when the child's profile is **not complete**. The corresponding admin flow at `/admin/sponsorship-management` correctly rejects approval with an error like: > Profile incomplete — missing: Handle, Bio, Sex, Nationality, Phone Number, Government ID Type, Government ID Number, Street Address, City, State / Province, Postal Code, Home Country, At least 1 photo, At least 1 language The parent-side toggle should enforce the same prerequisite. Otherwise an incomplete child profile can be marked `Sponsorship.Enabled = true`, which then queues it as a pending profile in the admin's sponsorship management view (e.g. "Bobby Spiker — Awaiting approval") despite not meeting the criteria. ## Steps to reproduce 1. Sign in as a parent. 2. Open your profile page → Children tab → expand a child whose profile is incomplete (e.g. Bobby Spiker, missing Bio / Phone Number / Address / etc.). 3. Toggle on "Allow donors to sponsor Bobby's travel". 4. Observe that the toggle saves successfully and the child appears in `/admin/sponsorship-management` under **Pending Approval**. 5. Compare to the admin's **Approve** flow on that same profile — it returns `Profile incomplete — missing: …` and refuses. ## Expected behavior When the parent attempts to enable sponsorship for a child, the backend should run the same `ProfileCompletionValidator` check that `SponsorController.ApproveSponsorProfile` runs. If the child profile is incomplete, the toggle request should fail with a 400 and a `missingItems` payload, and the UI should surface the same > Profile incomplete — missing: … message so the parent knows what to fix on the child's profile. Disabling sponsorship (`enabled = false`) should always be allowed regardless of completion state. ## Affected code - Backend: `SpikerSoft.Business/Domain/Profile/Commands/ToggleChildSponsorship/ToggleChildSponsorshipCommandHandler.cs` — currently writes `Sponsorship.Enabled = request.Enabled` with no completion check. - Backend (reference): `SpikerSoft.Api/Domain/Sponsor/SponsorController.ApproveSponsorProfile` — already calls `ProfileCompletionValidator.Validate(profile)` and returns `BadRequest(new { error, missingItems, percentage })`. - Frontend: `projects/spikersoft/src/app/_components/profile/profile.component.ts → toggleChildSponsorship` — needs to surface `missingItems` in the snackbar (mirroring `SponsorshipManagementComponent.approveProfile`).
Author
Owner

Fixed.

Backend (spikersoft-backend@4f6cddc)

  • Added ProfileIncompleteException next to ProfileCompletionResult so the validator's structured result can flow up through the command pipeline.
  • ToggleChildSponsorshipCommandHandler now runs ProfileCompletionValidator.Validate(childProfile) whenever Enabled == true and throws ProfileIncompleteException if the child profile fails the same 19-criteria check used by admin approval. Disabling stays unconditional.
  • ProfileController.ToggleChildSponsorship catches the new exception and returns 400 BadRequest with { error, missingItems, percentage } — identical shape to SponsorController.ApproveSponsorProfile, so any other client picks up the same payload contract.
  • Updated ToggleChildSponsorshipCommandHandlerTests (5 tests, all green): replaced the 'enable on empty profile' test with one against a fully-complete child, plus a new test asserting ProfileIncompleteException (with MissingItems populated) when enabling against an incomplete child, plus a test that disabling still works on an incomplete profile.

Frontend (spikersoft-angular@6ecd6a0)

  • profile.component.ts → toggleChildSponsorship error handler now reads err.error.missingItems and surfaces "{Child}'s profile incomplete — missing: …" in the snackbar (same wording pattern as SponsorshipManagementComponent.approveProfile), then calls loadChildren() so the slide toggle reverts to the persisted state.
  • New unit test in profile.component.spec.ts covers the missing-items snackbar message and the children refresh.

Verification

  • dotnet test --filter ToggleChildSponsorshipCommandHandlerTests|ProfileCompletionValidatorTests — 34/34 passed.
  • npm run test-spikersoft — 1754/1761 passed (7 unrelated pre-existing skips), 0 failures.

Closing.

Fixed. **Backend** (`spikersoft-backend@4f6cddc`) - Added `ProfileIncompleteException` next to `ProfileCompletionResult` so the validator's structured result can flow up through the command pipeline. - `ToggleChildSponsorshipCommandHandler` now runs `ProfileCompletionValidator.Validate(childProfile)` whenever `Enabled == true` and throws `ProfileIncompleteException` if the child profile fails the same 19-criteria check used by admin approval. Disabling stays unconditional. - `ProfileController.ToggleChildSponsorship` catches the new exception and returns `400 BadRequest` with `{ error, missingItems, percentage }` — identical shape to `SponsorController.ApproveSponsorProfile`, so any other client picks up the same payload contract. - Updated `ToggleChildSponsorshipCommandHandlerTests` (5 tests, all green): replaced the 'enable on empty profile' test with one against a fully-complete child, plus a new test asserting `ProfileIncompleteException` (with `MissingItems` populated) when enabling against an incomplete child, plus a test that disabling still works on an incomplete profile. **Frontend** (`spikersoft-angular@6ecd6a0`) - `profile.component.ts → toggleChildSponsorship` error handler now reads `err.error.missingItems` and surfaces `"{Child}'s profile incomplete — missing: …"` in the snackbar (same wording pattern as `SponsorshipManagementComponent.approveProfile`), then calls `loadChildren()` so the slide toggle reverts to the persisted state. - New unit test in `profile.component.spec.ts` covers the missing-items snackbar message and the children refresh. **Verification** - `dotnet test --filter ToggleChildSponsorshipCommandHandlerTests|ProfileCompletionValidatorTests` — 34/34 passed. - `npm run test-spikersoft` — 1754/1761 passed (7 unrelated pre-existing skips), 0 failures. Closing.
Sign in to join this conversation.