Skip to content

Admin Venue Creation - Manual Test Plan โ€‹

FieldValue
Branch / PRfeat/admin-venue-creation (session PR)
Issue#786 (Tier-1 #1, alpha-readiness gap map)
Environmentadmin portal (dev) -> venue-api Cloud Run (dev); writes hit live dev Firebase lantern-app-dev
Build flags / confignone (additive admin-only surface); requires VITE_VENUE_API_URL set in the admin build
Build (commit)fill after deploy
TesterMechelle
Datefill on run

Summary โ€‹

#ScenarioResultNotes
1Create a venue (happy path)[ ] pass [ ] fail [ ] blocked
2Create with blank address -> reverse-geocode autofill[ ] pass [ ] fail [ ] blocked
3Paste coordinates / Google Maps link fills lat+lng[ ] pass [ ] fail [ ] blocked
4Near-duplicate blocked, then "Create anyway"[ ] pass [ ] fail [ ] blocked
5Edit an existing venue's core fields[ ] pass [ ] fail [ ] blocked
6Validation guardrails (bad name / coords)[ ] pass [ ] fail [ ] blocked
7New venue is lightable in the user app[ ] pass [ ] fail [ ] blockedend-to-end supply proof
8Non-admin cannot reach the endpoint[ ] pass [ ] fail [ ] blockedguardrail
9(Post-deploy) VITE_VENUE_API_URL shipped in the admin build[ ] pass [ ] fail [ ] blockedactivation

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-computed geohash + nameLower, activeLanternCount:0, createdAt.
  • Reverse-geocode fills a blank address; a geocode failure falls back to lat, lng without failing the create.
  • Near-duplicate (same name within ~50m) -> DUPLICATE_VENUE; force:true overrides; same name >50m away is allowed.
  • Edit recomputes geohash/nameLower on change and never touches activeLanternCount/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-dev Firestore, venues collection. 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,lng from 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:
    1. Go to Platform -> Venues.
    2. Click Add venue.
    3. Enter a name, paste coordinates (or type lat/lng), enter an address, pick a category (e.g. Bar).
    4. 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-dev venues, a new doc exists with source:'manual', nameLower = lowercased name, a geohash, activeLanternCount:0, and createdAt.
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:
    1. Add venue, enter a name + valid San Diego coordinates, leave Address blank, pick a category.
    2. Click Create venue.
  • Expected: Venue is created.
  • Verify (data): The new doc's address is a real street address (not just the lat, lng fallback), and addressComponents / addressCity are populated. (If Nominatim was briefly unreachable, address may be the lat, lng fallback - that's the documented graceful degrade, note it.)
Result:    [ ] pass   [ ] fail   [ ] blocked
Actual:
Evidence:
Follow-up:

  • Goal: The paste helper parses both a plain pair and a Maps URL.
  • Steps:
    1. Add venue. In Paste coordinates or a Google Maps link, paste 32.7157, -117.1611. Confirm lat/lng fill.
    2. Clear it, paste a full Google Maps URL containing @32.7157,-117.1611,15z. Confirm lat/lng fill.
  • 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:
    1. Add venue with the same name and coordinates within ~50m of scenario 1's venue. Submit.
    2. Observe the duplicate warning + Create anyway button.
    3. Click Create anyway.
  • Expected: First submit shows "already exists nearby"; Create anyway then creates the second doc.
  • Verify (data): Two venues docs share that nameLower after 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:
    1. In the Venues list, click Edit on a venue.
    2. Change the name and nudge the coordinates; Save changes.
  • Expected: The list reflects the new name.
  • Verify (data): The doc's name/nameLower and geohash changed; activeLanternCount and source are unchanged; an updatedAt is 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:
    1. Add venue, leave name blank (or spaces only), enter valid coords, submit.
    2. 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:
    1. Create a venue near a location you can spoof to (or a real one you can visit) in dev.
    2. 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): activeLanternCount on the venue increments; a lanterns doc 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:
    1. As a signed-in non-admin (or with no auth), call POST /venues/admin/venues on the dev venue-api with a valid body (curl / devtools).
  • Expected: 403 FORBIDDEN (non-admin) or 401 (no auth); no venue created.
  • Verify: HTTP status; no new venues doc.
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:
    1. On the deployed dev admin portal, open Platform -> Venues and create a venue with NO local overrides.
    2. If it fails, check devtools Network for the POST /venues/admin/venues request 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 dev venues with source:'manual', server geohash (9mudjgt), activeLanternCount:0, and a reverse-geocoded address ("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.

Built with VitePress