Skip to content

Merchant Onboarding Seam - Manual Test Plan โ€‹

FieldValue
Branch / PRfeat/admin-venue-creation (session PR #788)
Issue#790 (gap-map Tier-1 #2)
Environmentadmin portal (dev) -> auth-api Cloud Run (dev); reads/writes merchant_applications in lantern-app-dev
Build flags / confignone; requires VITE_AUTH_API_URL set in the admin build
Build (commit)fill after deploy
TesterMechelle
Datefill on run

Summary โ€‹

#ScenarioResultNotes
1Pending applications list[ ] pass [ ] fail [ ] blocked
2Empty state[ ] pass [ ] fail [ ] blocked
3Review & create prefills the form[ ] pass [ ] fail [ ] blocked
4Approve provisions + marks approved + links merchant[ ] pass [ ] fail [ ] blockedend-to-end
5Reject records decision, no account created[ ] pass [ ] fail [ ] blocked
6WIP label is gone[ ] pass [ ] fail [ ] blocked
7Non-admin cannot list/approve[ ] pass [ ] fail [ ] blockedguardrail
8(Post-deploy) auth-api reachable from deployed admin[ ] pass [ ] fail [ ] blockedactivation

Server layers already verified (pre-merge) โ€‹

  • Unit (mocked Firestore, adminMerchantApplications.route.test.js): 7 green - list filter/serialize, approve/reject writes + audit, 404, zod guards.
  • Emulator integration (real Admin SDK vs Firestore emulator, npm run test:merchant-apps:emulator): 4 green - pending-only default, status=all, approve writes status/reviewer/merchant + audit + drops from pending, 404.
  • Admin component (MerchantApplications.test.jsx): 6 green - list, empty, review-navigates-with-prefill, reject-drops-row, confirm-dismiss no-op, load error.

Scenarios below are the browser + data + deployed-config layers.

Setup / preconditions โ€‹

  • Sign in to the dev admin portal as an admin.
  • Nav: Merchants now expands to All Merchants + Applications (/admin/merchants/applications).
  • To create a test application, submit the public merchant apply form (#/merchant signup on the web app) with a throwaway email, OR seed a merchant_applications doc in the Firebase console (status: 'pending', fields: applicantName, applicantEmail, businessName, venueAddress, businessType, phoneNumber, createdAt).
  • Data lives in lantern-app-dev: merchant_applications, merchants, merchantProfiles, users, adminActions.
  • Use a throwaway email for approvals - approving provisions a REAL merchant account (creates an Auth user + sends an invite email unless "Send email invite" is unchecked).

Scenarios โ€‹

1. Pending applications list โ€‹

  • Steps: Go to Merchants -> Applications.
  • Expected: A row per pending application: business, applicant, email, type, venue address, with "Review & create" + "Reject" actions.
  • Verify (data): The rows match merchant_applications docs with status: 'pending'.
Result:    [ ] pass   [ ] fail   [ ] blocked
Actual:
Evidence:
Follow-up:

2. Empty state โ€‹

  • Steps: With no pending applications (reject/approve all, or a clean project), open Applications.
  • Expected: "No pending applications" empty state, no error.
Result:    [ ] pass   [ ] fail   [ ] blocked
Actual:
Evidence:
Follow-up:

3. Review & create prefills the form โ€‹

  • Steps: Click "Review & create" on an application.
  • Expected: Lands on Create Merchant with email/business/contact/phone prefilled from the application, and a banner noting you are reviewing an application. Notes carry the venue address / type / website / message.
Result:    [ ] pass   [ ] fail   [ ] blocked
Actual:
Evidence:
Follow-up:

  • Steps: From the prefilled form (scenario 3), confirm the fields, leave "Send email invite" as desired (uncheck to avoid a real email), click Create Merchant.
  • Expected: Merchant account is created; you land on the merchant detail with the created banner. Back on Applications, the application is gone from the pending list.
  • Verify (data):
    • merchants/{merchantId} created (status pending_setup, ownerUserIds, businessName).
    • The merchant_applications doc: status: 'approved', reviewedBy = your uid, reviewedAt set, merchantId + userId linked.
    • adminActions has a reviewMerchantApplication entry (and the createMerchantUser/promoteToMerchant entry from provisioning).
Result:    [ ] pass   [ ] fail   [ ] blocked
Actual:
Evidence:   (merchantId + application id)
Follow-up:

5. Reject records the decision, no account created โ€‹

  • Steps: Click "Reject" on an application, confirm the dialog.
  • Expected: The row disappears from pending.
  • Verify (data): The application doc status: 'rejected', reviewedBy set; NO new merchants/users doc created for it.
Result:    [ ] pass   [ ] fail   [ ] blocked
Actual:
Evidence:
Follow-up:

6. WIP label is gone โ€‹

  • Steps: Open Create Merchant directly (Merchants -> All Merchants -> Create), not via an application.
  • Expected: No "Work in Progress" notice; the form works as before. (Via an application, the review banner shows instead.)
Result:    [ ] pass   [ ] fail   [ ] blocked
Actual:
Evidence:
Follow-up:

7. Non-admin cannot list or approve (guardrail) โ€‹

  • Steps: As a signed-in non-admin (or no auth), call GET /auth/admin/merchant-applications and POST /auth/admin/merchant-applications/<id>/status on the dev auth-api (curl / devtools).
  • Expected: 403 FORBIDDEN (non-admin) or 401 (no auth); no state change.
  • Verify: HTTP status; application doc unchanged.
Result:    [ ] pass   [ ] fail   [ ] blocked
Actual:
Evidence:
Follow-up:

8. (Post-deploy) auth-api reachable from the deployed admin build โ€‹

  • Steps: On the DEPLOYED dev admin portal, open Merchants -> Applications with no local overrides.
  • Expected: The list loads (or shows the empty state) rather than an error; the request targets the dev auth-api host.
  • Verify: devtools Network shows GET /auth/admin/merchant-applications hitting the real auth-api URL.
Result:    [ ] pass   [ ] fail   [ ] blocked
Actual:   (deploy run id + commit)
Evidence:
Follow-up:

Agent live run (2026-08-04, local stack) โ€‹

Driven headless (Playwright) against a local stack: admin portal (:3001) -> local auth-api (:8084, branch code) -> dev Firebase (Admin SDK via ADC), signed in as Agent Probe admin, App Check bypassed locally.

  • Nav + page render: PASS. Merchants expands to All Merchants + Applications; the Applications page renders (header + Refresh).
  • BUG CAUGHT + FIXED (this is why the live pass matters): the list endpoint first threw 9 FAILED_PRECONDITION: The query requires an index on real dev Firestore. The query was where('status','==',x).orderBy('createdAt','desc'), which needs a composite index that real Firestore does not have; the emulator auto-creates indexes, so the 4 emulator tests + 7 unit tests all passed while the real endpoint would 500. Fix: dropped the DB-level orderBy and sort the (small) pending queue in memory, so no composite index is needed. After the fix the endpoint returns 200 with no index error. Re-verified: 7 unit + 4 emulator green post-fix.
  • List/empty display: the API returns 200 (empty, no pending apps in dev); the screenshot caught the async call mid-load ("Loading applications..."). Endpoint + auth confirmed via logs (200, Agent Probe authenticated).

Scenarios 3-8 (prefill, approve end-to-end, reject, WIP gone, non-admin, post-deploy) remain for the operator; the list endpoint and nav are confirmed live, and the index bug is fixed.

Notes โ€‹

  • Approval is intentionally human-in-the-loop: applications come from a public form, so the server never auto-provisions. The admin reviews, provisions via the proven create path, and the app is marked approved on success.
  • Venue association remains a separate step (now unblocked by Theme 1's venue creation): associate a venue to the new merchant so it can create offers (VENUE_NOT_ASSOCIATED otherwise).

Built with VitePress