Admin Venue Creation - Manual Test Plan โ
| Field | Value |
|---|---|
| Branch / PR | feat/admin-venue-creation (session PR) |
| Issue | #786 (Tier-1 #1, alpha-readiness gap map) |
| Environment | admin portal (dev) -> venue-api Cloud Run (dev); writes hit live dev Firebase lantern-app-dev |
| Build flags / config | none (additive admin-only surface); requires VITE_VENUE_API_URL set in the admin build |
| Build (commit) | fill after deploy |
| Tester | Mechelle |
| Date | fill on run |
Summary โ
| # | Scenario | Result | Notes |
|---|---|---|---|
| 1 | Create a venue (happy path) | [ ] pass [ ] fail [ ] blocked | |
| 2 | Create with blank address -> reverse-geocode autofill | [ ] pass [ ] fail [ ] blocked | |
| 3 | Paste coordinates / Google Maps link fills lat+lng | [ ] pass [ ] fail [ ] blocked | |
| 4 | Near-duplicate blocked, then "Create anyway" | [ ] pass [ ] fail [ ] blocked | |
| 5 | Edit an existing venue's core fields | [ ] pass [ ] fail [ ] blocked | |
| 6 | Validation guardrails (bad name / coords) | [ ] pass [ ] fail [ ] blocked | |
| 7 | New venue is lightable in the user app | [ ] pass [ ] fail [ ] blocked | end-to-end supply proof |
| 8 | Non-admin cannot reach the endpoint | [ ] pass [ ] fail [ ] blocked | guardrail |
| 9 | (Post-deploy) VITE_VENUE_API_URL shipped in the admin build | [ ] pass [ ] fail [ ] blocked | activation |
Server layers already verified (emulator, pre-merge) โ
Covered by npm run test:venue:emulator (real Firestore emulator, Admin SDK) and npx vitest run test/manualVenue.test.js (unit) - green:
- Create writes
source:'manual', server-computedgeohash+nameLower,activeLanternCount:0,createdAt. - Reverse-geocode fills a blank address; a geocode failure falls back to
lat, lngwithout failing the create. - Near-duplicate (same name within ~50m) ->
DUPLICATE_VENUE;force:trueoverrides; same name >50m away is allowed. - Edit recomputes
geohash/nameLoweron change and never touchesactiveLanternCount/source; missing venue ->NOT_FOUND.
The scenarios below are the browser + data + deployed-config layers the operator runs.
Setup / preconditions โ
- Sign in to the dev admin portal as an admin (Agent Probe admin or your admin account).
- New nav item: Platform -> Venues (
/admin/venues). - Data lives in
lantern-app-devFirestore,venuescollection. Have the Firebase console open to that collection. - Use a real but throwaway venue for create tests (e.g. a San Diego coffee shop). Coordinates: grab from Google Maps (right-click -> the lat,lng pair, or copy the
@lat,lngfrom the URL). - Venues cannot be deleted from the portal by design; to clean up a test venue, delete the doc in the Firebase console.
Scenarios โ
1. Create a venue (happy path) โ
- Goal: An admin creates a net-new venue that lands in
venues. - Steps:
- Go to Platform -> Venues.
- Click Add venue.
- Enter a name, paste coordinates (or type lat/lng), enter an address, pick a category (e.g. Bar).
- Click Create venue.
- Expected: The form closes and the new venue appears in the list with its category and lantern count 0.
- Verify (data): In
lantern-app-devvenues, a new doc exists withsource:'manual',nameLower= lowercased name, ageohash,activeLanternCount:0, andcreatedAt.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence: (venue doc id)
Follow-up:2. Create with blank address -> reverse-geocode autofill โ
- Goal: Leaving address blank auto-fills it from the coordinates.
- Steps:
- Add venue, enter a name + valid San Diego coordinates, leave Address blank, pick a category.
- Click Create venue.
- Expected: Venue is created.
- Verify (data): The new doc's
addressis a real street address (not just thelat, lngfallback), andaddressComponents/addressCityare populated. (If Nominatim was briefly unreachable,addressmay be thelat, lngfallback - that's the documented graceful degrade, note it.)
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:3. Paste coordinates / Google Maps link fills lat+lng โ
- Goal: The paste helper parses both a plain pair and a Maps URL.
- Steps:
- Add venue. In Paste coordinates or a Google Maps link, paste
32.7157, -117.1611. Confirm lat/lng fill. - Clear it, paste a full Google Maps URL containing
@32.7157,-117.1611,15z. Confirm lat/lng fill.
- Add venue. In Paste coordinates or a Google Maps link, paste
- Expected: Latitude and Longitude fields populate from both formats.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:4. Near-duplicate blocked, then "Create anyway" โ
- Goal: The dedup guard catches an accidental re-add but can be overridden.
- Preconditions: Scenario 1's venue exists.
- Steps:
- Add venue with the same name and coordinates within ~50m of scenario 1's venue. Submit.
- Observe the duplicate warning + Create anyway button.
- Click Create anyway.
- Expected: First submit shows "already exists nearby"; Create anyway then creates the second doc.
- Verify (data): Two
venuesdocs share thatnameLowerafter the override.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:5. Edit an existing venue's core fields โ
- Goal: Editing name/coords/category updates the doc and recomputes geohash.
- Steps:
- In the Venues list, click Edit on a venue.
- Change the name and nudge the coordinates; Save changes.
- Expected: The list reflects the new name.
- Verify (data): The doc's
name/nameLowerandgeohashchanged;activeLanternCountandsourceare unchanged; anupdatedAtis set.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:6. Validation guardrails (bad name / coords) โ
- Goal: The form refuses invalid input before calling the API.
- Steps:
- Add venue, leave name blank (or spaces only), enter valid coords, submit.
- Enter a valid name but a latitude of
120, submit.
- Expected: Inline errors ("Name is required", "Latitude must be between -90 and 90"); no venue created.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:7. New venue is lightable in the user app (end-to-end supply proof) โ
- Goal: The whole point - a venue created here is usable by a real user.
- Steps:
- Create a venue near a location you can spoof to (or a real one you can visit) in dev.
- In the user app (dev), with location at/near that venue, open the venue and light a lantern.
- Expected: The venue is discoverable and a lantern can be lit there.
- Verify (data):
activeLanternCounton the venue increments; alanternsdoc exists for it.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:8. Non-admin cannot reach the endpoint (guardrail) โ
- Goal: The write path is admin-only.
- Steps:
- As a signed-in non-admin (or with no auth), call
POST /venues/admin/venueson the dev venue-api with a valid body (curl / devtools).
- As a signed-in non-admin (or with no auth), call
- Expected:
403 FORBIDDEN(non-admin) or401(no auth); no venue created. - Verify: HTTP status; no new
venuesdoc.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence:
Follow-up:9. (Post-deploy) VITE_VENUE_API_URL shipped in the admin build โ
- Goal: Prove the deployed admin bundle can actually reach venue-api (the "config shipped" check, not just "code works locally").
- Steps:
- On the deployed dev admin portal, open Platform -> Venues and create a venue with NO local overrides.
- If it fails, check devtools Network for the
POST /venues/admin/venuesrequest target.
- Expected: Create works against deployed venue-api; the request goes to the dev venue-api URL, not
undefined/.... - Verify: Network tab shows the request hitting the real venue-api host; the venue appears in dev
venues.
Result: [ ] pass [ ] fail [ ] blocked
Actual:
Evidence: (deploy run id + commit)
Follow-up:Agent live run (2026-08-04, local stack) โ
Driven headless (Playwright) against a local stack: admin portal (:3001) -> local venue-api (:8080, branch code) -> dev Firebase (Admin SDK via ADC), signed in as Agent Probe admin. App Check bypassed locally (APP_CHECK_DISABLED=true, the sanctioned non-Cloud-Run bypass).
- Scenario 1 (create) + 2 (reverse-geocode autofill): PASS, verified end-to-end. Admin logged in, opened Platform > Venues, "Add venue", entered name + coords (32.7157, -117.1611), left address blank, created. venue-api returned
201; the doc landed in devvenueswithsource:'manual', server geohash (9mudjgt),activeLanternCount:0, and a reverse-geocodedaddress("326 Broadway San Diego California 92101") autofilled from the coordinates. Test venues cleaned up afterward. - Venue list display not exercised locally: the list uses a client-side
onSnapshot, which real dev Firestore blocks under App Check because a headless localhost browser can't mint an App Check token. This is a local-test limitation, not a product issue (a real browser on deployed dev mints a valid token). Left for the operator's deployed pass (scenario 1's list check).
Scenarios 3-9 (paste helper, dedup UI, edit, validation, lightability, non-admin, post-deploy config) remain for the operator; the create + geocode core is confirmed live.
Notes โ
- Server logic is emulator-verified pre-merge (see the section above). Scenarios 1-8 are the browser/data pass; scenario 9 is the post-deploy activation check per the test-plan activation rule.