Commit graph

7 commits

Author SHA1 Message Date
Till JS
286e273b18 test(geocoding): add unit tests + end-to-end smoke test script
**Unit tests (`bun test`, 42 checks, 0 deps)**

- `src/lib/__tests__/category-map.test.ts` locks in the Pelias→
  PlaceCategory priority resolution. Covers the ambiguous multi-category
  case (food beats retail for restaurants, transit beats professional
  for car rentals, transport:rail still maps to transit, …), the simple
  single-category paths, the layer-hint fallback, and regression cases
  from real Konstanz/Stuttgart/Köln venues observed during deploy
  verification.
- `src/lib/__tests__/cache.test.ts` covers LRU eviction order, TTL
  expiry, move-to-end on get (so frequently-read entries survive
  eviction), size tracking, and typed-value storage.

**Smoke test (`./scripts/smoke-test.sh` or `bun run test:smoke`)**

End-to-end curls against a running service, aimed at post-deploy
verification. Health endpoints, forward (venue + street fallback),
focus biasing, reverse geocoding, cache hit. 9 checks total.

Wired up as `test:smoke` in package.json so it runs alongside the
unit tests. Verified working: 42/42 unit tests green locally, 9/9
smoke checks green against the live Mac Mini deployment.

CLAUDE.md Testing section rewritten to reflect the new test layers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 20:21:18 +02:00
Till JS
69ce4c2c25 feat(geocoding): fall back to Pelias /search when /autocomplete is empty
Pelias /autocomplete deliberately excludes the address layer as a
performance optimization, so queries like "Marktstätte Konstanz"
(street + locality) return 0 venue matches even though they're clearly
in the index. /search covers all layers including addresses and streets.

Query /autocomplete first (fast, fuzzy, great for venue names), and if
it returns nothing, try /search. Best of both worlds: quick matches for
"Konzil Restaurant" plus reliable matches for street addresses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 17:54:57 +02:00
Till JS
020f327503 fix(geocoding): drop unused Pelias services, raise Bun idleTimeout
Two production follow-ups surfaced after the deploy:

1. Pelias API was emitting continuous `ENOTFOUND placeholder`, `pip`,
   `interpolation` errors because we declared those services in
   pelias.json but never actually run them (we don't need WOF
   admin lookup or street interpolation for the DACH use case).
   Removed the stale entries — Pelias degrades cleanly to
   libpostal-only parsing, which is what we want.

2. Bun.serve's default idleTimeout is 10s, which is too tight for
   cold Pelias queries hitting Elasticsearch. Raise to 60s so
   first-query-after-idle doesn't get cut off.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 17:41:57 +02:00
Till JS
c47ce83e83 fix(geocoding): proxy Pelias health through wrapper for monitoring
blackbox-exporter can't resolve host.docker.internal on Colima, so
probes of host.docker.internal:4000 and :9200 always fail. Instead,
add a /health/pelias endpoint on the Hono wrapper that proxies to
the Pelias API, and update prometheus.yml to probe the wrapper's
proxied health endpoint.

Also simplifies the status page friendly_name() now that we don't
need to display the host.docker.internal targets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:45:43 +02:00
Till JS
e82b5c1449 feat(geocoding): auto-categorize places via Pelias taxonomy
Pelias hides the 'category' field from API responses unless the
caller filters by categories=... explicitly — a default intended for
keyword search that strips category metadata from address queries.

Patch the Pelias API's geojsonify_place_details.js so the category
array is returned on every feature (food, retail, transport, …),
mounted into the container as a read-only volume override.

Rewrite category-map.ts to map Pelias' OSM taxonomy to our 7
PlaceCategories using a priority-ordered list so a restaurant
tagged ['food','retail','nightlife'] resolves to 'food' (the most
specific), not 'shopping'.

Verified with Konstanz test queries:
  Konzil Restaurant        → food
  Bahnhof Konstanz         → transit
  Physiotherapie-Schule    → work
  MX-Park                  → leisure

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 15:48:24 +02:00
Till JS
1943a1d13c fix(geocoding): Pelias config for DACH-only import + single-country filter
After importing 22M OSM objects for the DACH extract:
- Disable adminLookup (no WOF data needed for address search)
- Configure leveldb path inside the data volume
- Specify planet-latest.osm.pbf as the import filename
- Convert libpostal service config from string to object form
- Drop boundary.country default — Pelias only accepts a single
  country value, and our index only contains DACH data anyway

Verified forward + reverse geocoding work end-to-end for Konstanz
test queries via the mana-geocoding wrapper on port 3018.

Known limitation: OSM category/type (amenity:restaurant etc.) is
not yet populated in Pelias responses — will require whitelisting
those tags in the importer config and re-running the import.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 04:58:55 +02:00
Till JS
a47a7bfdba feat(places): add self-hosted geocoding with Pelias (DACH)
New mana-geocoding service (port 3018) wraps a self-hosted Pelias
instance with LRU caching and OSM→PlaceCategory auto-mapping.
All geocoding queries stay within our infrastructure — no user
location data leaves the network.

Places module integration:
- Address autocomplete search in ListView (creates place with
  name, coords, address, category in one step)
- Address search + reverse geocoding button in DetailView
- Auto-fill address via reverse geocoding during tracking
- OSM category mapping (amenity:restaurant→food, shop:*→shopping, etc.)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:02:25 +02:00