/** * Unit tests. No network, no credentials — everything here runs offline against * recorded Mapbox payloads. * * The thing worth testing is the precision mapping. A geocoder always returns * *something*: ask it for a misspelled street and it hands back the city, very * confidently. Treating that as a located address is precisely the bug this * package exists to remove, so "did we get a result" must never be the signal. */ import { describe, expect, it } from 'vitest'; import { GeocodeError, parseResponse, precisionOf } from '../src/index'; /** Shaped like a real v6 feature, trimmed to the fields we read. */ function feature(overrides: { featureType?: string; id?: string; fullAddress?: string; name?: string; placeFormatted?: string; lat?: number; lng?: number; }) { return { properties: { mapbox_id: overrides.id ?? 'dXJuOm1ieGFkcjo=', feature_type: overrides.featureType, full_address: overrides.fullAddress, name: overrides.name, place_formatted: overrides.placeFormatted, coordinates: { longitude: overrides.lng ?? 2.1686, latitude: overrides.lat ?? 41.3874 }, }, }; } describe('precisionOf', () => { it('calls a street address exact', () => { expect(precisionOf('address')).toBe('exact'); expect(precisionOf('secondary_address')).toBe('exact'); }); it('calls anything coarser than a building approximate', () => { for (const t of ['street', 'block', 'postcode', 'neighborhood', 'locality']) { expect(precisionOf(t)).toBe('approximate'); } }); it('calls a city-or-wider result what it is', () => { // The failure mode that matters: a bad query returns the city, and if that // ranked as a location every distance in the product would be a lie. expect(precisionOf('place')).toBe('city'); expect(precisionOf('region')).toBe('city'); expect(precisionOf('country')).toBe('city'); }); it('degrades an unknown feature type rather than trusting it', () => { // Mapbox can add types. A new one must not silently rank as exact. expect(precisionOf('something_new')).toBe('city'); expect(precisionOf(undefined)).toBe('city'); }); }); describe('parseResponse', () => { it('maps a full address feature', () => { const [result] = parseResponse({ features: [ feature({ featureType: 'address', id: 'addr-1', fullAddress: 'Carrer de Sants 12, 08014 Barcelona, Spain', lat: 41.3751, lng: 2.1339, }), ], }); expect(result).toEqual({ providerId: 'addr-1', label: 'Carrer de Sants 12, 08014 Barcelona, Spain', coordinates: { lat: 41.3751, lng: 2.1339 }, precision: 'exact', }); }); it('builds a label from name and place when there is no full address', () => { const [result] = parseResponse({ features: [feature({ featureType: 'street', name: 'Carrer de Sants', placeFormatted: 'Barcelona, Spain' })], }); expect(result?.label).toBe('Carrer de Sants, Barcelona, Spain'); expect(result?.precision).toBe('approximate'); }); it('drops a feature with nothing to show or nothing to re-resolve by', () => { // A row we cannot label is a row the user cannot choose between; a row with // no id is one we could never re-resolve. Both are dropped rather than // rendered as a blank line. const results = parseResponse({ features: [ feature({ featureType: 'address', fullAddress: undefined, name: undefined }), { properties: { feature_type: 'address', coordinates: { longitude: 2, latitude: 41 } } }, ], }); expect(results).toEqual([]); }); it('returns nothing for an empty result set rather than throwing', () => { expect(parseResponse({ features: [] })).toEqual([]); expect(parseResponse({})).toEqual([]); }); it('refuses a payload whose coordinates are missing or malformed', () => { // This is the guard that keeps a NaN out of a geography column. expect(() => parseResponse({ features: [{ properties: { feature_type: 'address' } }] })).toThrow( GeocodeError, ); expect(() => parseResponse({ features: [{ properties: { coordinates: { longitude: 'two', latitude: 41 } } }], }), ).toThrow(GeocodeError); expect(() => parseResponse('not json at all')).toThrow(GeocodeError); }); });