Blueprint Self-Tests

Deterministic tests for the Create Event workflow, campsite display pricing hierarchy, and large dimensionally-accurate campground maps.

Create Event Workflow

✓ ALL PASS22 passed · 0 failed · 22 total
✓
event types: exactly one supported type (music_festival)
✓
unsupported types never produce a workflow state
✓
template: festival with camping → music_festival_with_camping
✓
template: camping disabled → plain music_festival template
✓
details validation: every required field produces a clear error
✓
multi-day: end date cannot precede start date
✓
time zone persists on the payload
✓
enabled modules persist as a JSON object (explicit OFF values included)
✓
ticketing status: etix/other → configuration_only; none → not_connected
✓
no external integration activity is configured by the payload
✓
payload creates a DRAFT record with legacy-required fields intact
✓
card-show-specific fields are not required or populated
✓
default campsite type built from camping defaults (30 AMP)
✓
campsite-type creation is idempotent on re-merge
✓
no default campsite type when the organizer chooses set-up-later
✓
campground defaults persist as reference JSON
✓
camping events seed a campground-mode starter map config
✓
uploaded property image becomes the map reference image
✓
hours formatting converts 24h inputs to readable range
✓
terminology is contextual and never renames internals
✓
only Blueprint events are identified as such (legacy records untouched)
✓
initial state is fresh per call (no shared mutable objects)

Campsite Pricing & Large Maps

✓ ALL PASS25 passed · 0 failed · 25 total
✓
pricing: blank is NOT zero — nothing set anywhere resolves to no price
✓
pricing: event default applies when the type has no price
✓
pricing: campsite-type default beats the event default
✓
pricing: explicit campsite_defaults.default_price beats the legacy booth-type price
✓
pricing: individual campsite override beats everything stored
✓
pricing: clearing the override restores inheritance
✓
pricing: external price wins ONLY when an active sync exists (never by default)
✓
pricing: zero is a real FREE price and formats as $0 (blank never does)
✓
pricing: currency resolves from the event field with USD fallback
✓
pricing: formatted prices are currency-aware
✓
pricing: organizer source labels exist for every level
✓
pricing: missing-price setting defaults to omit and supports contact
✓
wizard: event default campsite price persists (blank stays blank, never 0)
✓
wizard: blank campsite-type price stays blank in the type (never 0)
✓
wizard: campground defaults carry the pricing settings
✓
pricing: no payment or booking action is created by the payload
✓
maps: dimensions above 500 ft validate and are never clamped
✓
maps: invalid dimensions are rejected, never silently clamped
✓
maps: 2,000 × 1,500 round-trips through save/load unchanged
✓
maps: campsites beyond 500 ft keep exact geometry on round-trip
✓
maps: existing 500-and-under maps round-trip byte-identical
✓
maps: adaptive grid picks readable, bounded intervals at every scale
✓
maps: Fit Area keeps the SVG element container-sized for huge areas
✓
maps: Zoom to Selection centers a small campsite in a huge area
✓
maps: booth labels hide below a readable zoom threshold without changing geometry

Editor Save Transaction

✓ ALL PASS30 passed · 0 failed · 30 total
✓
save diff: 500 unchanged + 10 new → saves only the 10 new records
✓
save diff: one changed campsite appears exactly once in updates
✓
save diff: deleted campsite lands in deletes; a new site discarded before save does not
✓
save diff: no changes at all → completely empty diff (zero campsite writes)
✓
save diff: road-linked sites keep their campground_road_id in the create payload
✓
save diff: an unknown-id record NOT flagged new is treated as an idempotent update, never a duplicate create
✓
configs: identical configuration strings are clean; any change is dirty
✓
persistence keys: stable across save attempts — never regenerated
✓
paste leak: a pasted site sharing a saved source's key is re-keyed before save (source untouched)
✓
paste leak: two pasted copies sharing one key each get distinct keys
✓
retry keys: a create whose key is owned solely by itself keeps it; a shared one is re-keyed
✓
retry: a verify failure (missing/mismatch record) is retried as a create
✓
verification gate: a failure already reported by the server is never double-listed in the dialog
✓
reconciliation: temp ids become real ids and the record stops being new
✓
reconciliation: a failed create keeps its temp id, geometry, and pending state
✓
retry: processes ONLY failed creates (by key) and failed updates (by id)
✓
partial save keeps the editor dirty (the baseline is NOT updated)
✓
full cycle: partial → retry → success marks the editor clean
✓
repeated Save after a successful save performs zero campsite writes
✓
duplicate prevention: a create re-sent with the same persistence key reconciles, never duplicates
✓
navigation warning: an unsaved diff or a partial save requires confirmation
✓
stage label and success message report the real counts
✓
commit: Commit Sites moves all 10 preview placements into the authoritative campsite state
✓
commit: an empty preview commits nothing — a cleared preview cannot duplicate sites
✓
commit: every committed site receives exactly one stable persistence key before save
✓
commit: the load-time baseline never absorbs unsaved local sites
✓
road delta: countNewRoads reports exactly one new road for a road+sites save
✓
verification gate: 10 of 10 read-after-write verified → success; 9 of 10 → incomplete
✓
verification gate: a success response without server verification is refused
✓
verification gate: partial-save failures keep the save incomplete

Event Dashboard & Manage Event

✓ ALL PASS31 passed · 0 failed · 31 total
✓
facts: Music Festival with camping uses campsite vocabulary
✓
facts: chunked road fields are counted (no full config roads)
✓
facts: dashboard summary scalars build the same facts (no campsite records needed)
✓
facts: generic fallback for a non-camping festival
✓
legacy: record without event_type is not a Blueprint event
✓
modules: legacy enabled_modules JSON parses with unknown keys preserved
✓
resolver: complete published festival is 100%
✓
resolver: draft reduces the percentage by exactly the publish step
✓
resolver: optional overview is never required when not enabled
✓
resolver: recommended next action follows step priority order
✓
publish: blockers vs warnings are separated
✓
publish: map-only event without ticketing can still publish
✓
publish: zero public campsites blocks a camping event with areas
✓
publish: within-area duplicate campsite numbers block publishing
✓
publish: invalid overview hotspot links block
✓
readiness label: separates publish readiness from setup completion
✓
campsite border: derived from fill color, never near-black
✓
campsite border: unparseable fill falls back to neutral slate
✓
coordinator entry: Blueprint owners land on /organizer-events
✓
coordinator entry: legacy-only organizers keep Dashboard V2
✓
nav: camping-enabled festival shows Campsites; disabled check-in is hidden
✓
nav: parking module adds the Parking tab
✓
nav: ticketing appears even when admissions is off but a provider is configured
✓
nav: Property Overview tab shows when overview enabled even without camping
✓
map editor: Blueprint editor route preserves the event and the return workspace
✓
routing: a Blueprint event opened on /manageshow redirects to Manage Event with the right tab
✓
timing: past vs upcoming computed from dates
✓
details patch: editing the name never drops the stored timezone
✓
campsites: 500+ rows filter, sort, and paginate without losing rows
✓
campsites: resolved price chain is site → type → event
✓
resolver and readiness are pure — the input facts are never mutated

Campsite Number Uniqueness

✓ ALL PASS21 passed · 0 failed · 21 total
✓
uniqueness: normalization trims, collapses whitespace, uppercases
✓
uniqueness: numeric padding is NOT normalized (A-01 ≠ A-1)
✓
uniqueness: two keyless creates with the same number still conflict (batch-internal)
✓
uniqueness: keyless creates with distinct numbers pass
✓
uniqueness: same number in same area rejected
✓
uniqueness: same number in different areas allowed
✓
uniqueness: case/whitespace variants of an existing number rejected
✓
uniqueness: batch-internal conflicts detected
✓
uniqueness: conflicts against persisted records (existing dups present)
✓
uniqueness: update retains its own number (legacy duplicates compatible)
✓
uniqueness: existing duplicates do not block unrelated edits (move/amenities)
✓
uniqueness: update renumbered onto another site's number rejected
✓
uniqueness: persistence-key retry remains idempotent
✓
uniqueness: deletes free numbers within the same save
✓
uniqueness: guard applies only to campground saves (standard compatibility)
✓
generation: conflicts detected against persisted and batch-internal numbers
✓
numbering: skip-existing comparison is normalized, display formatting preserved
✓
list: duplicateNumberGroups groups by area + normalized number
✓
list: duplicate filter shows only duplicated rows
✓
list: aggregate counts per-area duplicates (normalized)
✓
publish: within-area duplicate campsite numbers block publishing