Merchant Venue Association in Provisioning - Manual Test Plan โ
| Field | Value |
|---|---|
| Branch / PR | feat/autopilot-merchant-venue (session PR #792) |
| Issue | #793 (builds on #786 venue creation, #790 application review) |
| Environment | admin portal (dev) -> auth-api Cloud Run (dev); writes merchants, venues, adminActions in lantern-app-dev |
| Build flags / config | none; requires VITE_AUTH_API_URL + VITE_VENUE_API_URL set in the admin build |
| Build (commit) | fill after deploy |
| Tester | Mechelle (+ agent live run below) |
| Date | fill on run |
Summary โ
| # | Scenario | Result | Notes |
|---|---|---|---|
| 1 | Create merchant, then associate a venue in the same flow | [ ] pass [ ] fail [ ] blocked | happy path |
| 2 | Approve application, associate venue, end-to-end | [ ] pass [ ] fail [ ] blocked | end-to-end |
| 3 | Skip venue association (optional step) | [ ] pass [ ] fail [ ] blocked | regression |
| 4 | Venue already claimed by another merchant is surfaced, non-blocking | [ ] pass [ ] fail [ ] blocked | guardrail |
| 5 | Associating the same venue twice is idempotent | [ ] pass [ ] fail [ ] blocked | |
| 6 | Non-admin cannot associate | [ ] pass [ ] fail [ ] blocked | guardrail |
| 7 | Data + audit verification | [ ] pass [ ] fail [ ] blocked | data |
| 8 | Offer create is unblocked after association | [ ] pass [ ] fail [ ] blocked | the payoff |
Server layers already verified (pre-existing) โ
The POST /auth/admin/merchants/:merchantId/venues endpoint (associateVenueWithMerchant) already exists, is transaction-wrapped, and returns MERCHANT_NOT_FOUND / VENUE_NOT_FOUND (404), VENUE_ALREADY_CLAIMED (409), ALREADY_ASSOCIATED (200), ASSOCIATED (201 + adminActions entry). This change is the admin-UI wiring that calls it inside the provisioning flow, so the scenarios below are the browser + data + integration layers of the new flow.
Setup / preconditions โ
- Sign in to the dev admin portal as an admin (Agent Probe for the agent run).
- At least one unclaimed venue exists in dev
venues(create one via Merchants/Platform -> Venues -> Add venue, #786, if needed). Note its name + id. - Use a throwaway email for provisioning: creating a merchant is a real account (Auth user + optional invite email).
- Data lives in
lantern-app-dev:merchants,venues,adminActions,users,merchant_applications.
Scenarios โ
1. Create merchant, then associate a venue in the same flow (happy path) โ
- Steps: Merchants -> All Merchants -> Create. Fill email/business/contact, uncheck "Send email invite", Create Merchant. On the venue step, search the unclaimed venue by name, associate it, continue to detail.
- Expected: Merchant is created; the venue step appears with the new merchant's id; after associating, the merchant detail shows the venue in its venues list.
- Verify (data):
merchants/{id}.venueIdscontains the venue id;venues/{id}.merchantId == {merchantId}.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence: (merchantId + venueId)
Follow-up:2. Approve an application, associate a venue, end-to-end โ
- Steps: Merchants -> Applications -> Review & create on a pending application -> confirm prefilled fields -> Create Merchant -> associate a venue on the venue step -> continue.
- Expected: Account provisioned, application flips to
approvedand linksmerchantId/userId, venue associated, detail shows the venue. - Verify (data): application
status: approved+merchantId;merchants/{id}.venueIdshas the venue;adminActionshas bothreviewMerchantApplicationandassociateVenue.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:3. Skip venue association (the step is optional) โ
- Steps: Create a merchant. Note the venue step's hint that offers are blocked without a venue, then choose "Skip for now".
- Expected: The venue step shows the "without a venue, every offer is blocked" hint; choosing "Skip for now" lands on merchant detail with no venue linked and no error.
- Verify (data):
merchants/{id}.venueIdsis empty/absent.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:4. Venue already claimed by another merchant is surfaced, non-blocking โ
- Steps: Provision merchant A and associate venue V. Provision merchant B and try to associate the same venue V.
- Expected: A clear "already associated with another merchant" message (409
VENUE_ALREADY_CLAIMED); merchant B is still created; the admin can pick a different venue or skip. The whole flow does NOT read as a failed create. - Verify (data):
venues/V.merchantIdstill points at A; B has no venue.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:5. Associating the same venue twice is idempotent โ
- Steps: Associate venue V to merchant A, then attempt to associate V to A again.
- Expected: No error, no duplicate;
200 alreadyAssociated: true.venueIdshas V exactly once.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:6. Non-admin cannot associate (guardrail) โ
- Steps: As a signed-in non-admin (or no auth), call
POST /auth/admin/merchants/<id>/venueson the dev auth-api (curl / devtools). - Expected:
403 FORBIDDEN(non-admin) or401(no auth); no state change.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:7. Data + audit verification โ
- Steps: After scenario 1 or 2, inspect Firestore.
- Expected / Verify:
merchants/{id}.venueIdsincludes the venue;venues/{id}.merchantIdequals the merchant id;venues/{id}.updatedAtbumped;adminActionshas anassociateVenuedoc withperformedBy= your uid.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:8. Offer create is unblocked after association (the payoff) โ
- Steps: As the newly-provisioned + venue-associated merchant (or an admin acting on their behalf), attempt to create an offer.
- Expected: Offer create no longer returns
400 VENUE_NOT_ASSOCIATED; it proceeds (subject to the normal offer validation). - Verify: the offer create request succeeds / advances past the venue-association gate.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:Agent live run โ
To be filled by the agent browser pass (local admin stack -> local/branch auth-api -> dev Firebase, Agent Probe admin, App Check bypassed locally). Record what the run actually showed or the specific blocker here.
Notes โ
- The associate endpoint is transactional and pre-existing; the risk in this change is entirely in the admin-UI flow (state handling after create, non-blocking error surface, the optional-skip path).
- If the alpha venue does not yet exist, create it first via the #786 venue form; this flow associates an existing venue, it does not create one inline (kept out of scope to stay focused).