import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { geocodeUsZip } from '../../../lib/pricing/nominatim.js'; describe('nominatim', () => { afterEach(() => { vi.useRealTimers(); vi.restoreAllMocks(); }); it('geocodes a US zip via allowlisted host', async () => { const fetchImpl = vi.fn().mockResolvedValue({ ok: true, json: async () => [{ lat: '40.7128', lon: '-74.0060', display_name: 'New York, NY' }], }); const result = await geocodeUsZip('10001', { fetchImpl }); expect(result).toEqual({ lat: 40.7128, lng: -74.006, displayName: 'New York, NY', }); const calledUrl = fetchImpl.mock.calls[0][0]; expect(calledUrl).toContain('nominatim.openstreetmap.org'); expect(calledUrl).toContain('postalcode=10001'); }); it('returns null when no results', async () => { const fetchImpl = vi.fn().mockResolvedValue({ ok: true, json: async () => [], }); const result = await geocodeUsZip('00000', { fetchImpl }); expect(result).toBeNull(); }); it('enforces minimum 1s between requests', async () => { vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00Z') }); vi.resetModules(); const { geocodeUsZip: geocodeFresh } = await import('../../../lib/pricing/nominatim.js'); const fetchImpl = vi.fn().mockResolvedValue({ ok: true, json: async () => [{ lat: '40.0', lon: '-74.0', display_name: 'Test' }], }); await geocodeFresh('10001', { fetchImpl }); const secondPromise = geocodeFresh('10002', { fetchImpl }); await vi.advanceTimersByTimeAsync(500); expect(fetchImpl).toHaveBeenCalledTimes(1); await vi.advanceTimersByTimeAsync(500); await secondPromise; expect(fetchImpl).toHaveBeenCalledTimes(2); }); });