diff --git a/app/utils/location.test.ts b/app/utils/location.test.ts new file mode 100644 index 0000000..6688249 --- /dev/null +++ b/app/utils/location.test.ts @@ -0,0 +1,68 @@ + +// If using TypeScript, ensure Jest types are available +// This import is only necessary if your environment does not provide 'expect' globally +import { describe, expect, it } from '@jest/globals'; +import { getCoordinatesFromZip, SAN_DIEGO_ZIP_COORDINATES } from './location'; +import { LIBRARY_LOCATIONS as LIBRARY_LOCATIONS_OBJ } from '../api/availability/route'; +// Convert the object to an array of coordinates for testing +const LIBRARY_LOCATIONS = Object.values(LIBRARY_LOCATIONS_OBJ); +const ZIP_COORDS = Object.entries(SAN_DIEGO_ZIP_COORDINATES || {}); + +describe('San Diego ZIP and Library Coordinates', () => { + it('all ZIP code lat/lngs are unique', () => { + const seen = new Map(); + let duplicate = null; + for (const [zip, coord] of ZIP_COORDS) { + const { lat, lng } = coord as { lat: number; lng: number }; + const key = `${lat},${lng}`; + if (seen.has(key)) { + duplicate = { zip, other: seen.get(key), lat, lng }; + break; + } + seen.set(key, zip); + } + if (duplicate) { + throw new Error(`Duplicate ZIP coordinates: ${duplicate.zip} and ${duplicate.other} share (${duplicate.lat}, ${duplicate.lng})`); + } + expect(duplicate).toBe(null); + }); + + it('all library lat/lngs are unique', () => { + const seen = new Map(); + let duplicate = null; + for (const [i, coord] of LIBRARY_LOCATIONS.entries()) { + const key = `${coord.lat},${coord.lng}`; + if (seen.has(key)) { + duplicate = { idx: i, other: seen.get(key), lat: coord.lat, lng: coord.lng }; + break; + } + seen.set(key, i); + } + if (duplicate) { + throw new Error(`Duplicate library coordinates: index ${duplicate.idx} and ${duplicate.other} share (${duplicate.lat}, ${duplicate.lng})`); + } + expect(duplicate).toBe(null); + }); + + it('no ZIP code lat/lng matches any library lat/lng', () => { + const librarySet = new Set(LIBRARY_LOCATIONS.map(c => `${c.lat},${c.lng}`)); + let conflict = null; + for (const [zip, coord] of ZIP_COORDS) { + const { lat, lng } = coord as { lat: number; lng: number }; + const key = `${lat},${lng}`; + if (librarySet.has(key)) { + conflict = { zip, lat, lng }; + break; + } + } + if (conflict) { + throw new Error(`ZIP code ${conflict.zip} shares coordinates (${conflict.lat}, ${conflict.lng}) with a library`); + } + expect(conflict).toBe(null); + }); + + it("'92129' has the expected coordinates", () => { + const coord = getCoordinatesFromZip('92129'); + expect(coord).toEqual({ lat: 32.9584, lng: -117.1253 }); + }); +}); diff --git a/app/utils/location.ts b/app/utils/location.ts index 25ae8b3..0eb64ae 100644 --- a/app/utils/location.ts +++ b/app/utils/location.ts @@ -34,50 +34,50 @@ export async function getCurrentLocation(): Promise<{ lat: number; lng: number } } // San Diego ZIP code validation and coordinates -const SAN_DIEGO_ZIP_COORDINATES: Record = { - '92101': { lat: 32.7150, lng: -117.1600 }, // Downtown (unique) - '92102': { lat: 32.7040, lng: -117.1290 }, // Logan Heights (unique) - '92103': { lat: 32.7338, lng: -117.1471 }, // Core-Columbia (unique) - '92104': { lat: 32.7492, lng: -117.0714 }, // College-Rolando (unique) - '92105': { lat: 32.7099, lng: -117.1252 }, // Southeastern San Diego (unique) - '92106': { lat: 32.7340, lng: -117.1440 }, // Point Loma (unique) - '92107': { lat: 32.7479, lng: -117.1988 }, // Ocean Beach (unique) - '92108': { lat: 32.7480, lng: -117.1990 }, // Mission Valley (unique) - '92109': { lat: 32.7724, lng: -117.1799 }, // Pacific Beach (unique) - '92110': { lat: 32.7734, lng: -117.1799 }, // Mission Bay (unique) - '92111': { lat: 32.8338, lng: -117.2060 }, // Clairemont (unique) - '92113': { lat: 32.6757, lng: -117.0752 }, // Southeastern San Diego (unique) - '92114': { lat: 32.7040, lng: -117.1299 }, // Southeastern San Diego (unique) - '92115': { lat: 32.7421, lng: -117.1055 }, // City Heights (unique) - '92116': { lat: 32.7654, lng: -117.1174 }, // Kensington (unique) - '92117': { lat: 32.8348, lng: -117.2060 }, // Clairemont (unique) - '92118': { lat: 32.6154, lng: -117.0855 }, // Coronado (unique) - '92119': { lat: 32.7492, lng: -117.0724 }, // College Area (unique) - '92120': { lat: 32.7502, lng: -117.0734 }, // College Area (unique) - '92121': { lat: 32.9350, lng: -117.2350 }, // Sorrento Valley (unique) - '92122': { lat: 32.8354, lng: -117.2554 }, // La Jolla (unique) - '92123': { lat: 32.7724, lng: -117.1790 }, // Serra Mesa (unique) - '92124': { lat: 32.7734, lng: -117.1809 }, // Serra Mesa (unique) - '92126': { lat: 32.9296, lng: -117.1321 }, // Carmel Mountain (unique) - '92127': { lat: 33.0210, lng: -117.1166 }, // Rancho Bernardo (unique) - '92128': { lat: 32.9590, lng: -117.1230 }, // Rancho Penasquitos (unique) +export const SAN_DIEGO_ZIP_COORDINATES: Record = { + '92101': { lat: 32.7150, lng: -117.1600 }, // Downtown + '92102': { lat: 32.7040, lng: -117.1290 }, // Logan Heights + '92103': { lat: 32.7338, lng: -117.1471 }, // Core-Columbia + '92104': { lat: 32.7492, lng: -117.0714 }, // College-Rolando + '92105': { lat: 32.7099, lng: -117.1252 }, // Southeastern San Diego + '92106': { lat: 32.7340, lng: -117.1440 }, // Point Loma + '92107': { lat: 32.7479, lng: -117.1988 }, // Ocean Beach + '92108': { lat: 32.7480, lng: -117.1990 }, // Mission Valley + '92109': { lat: 32.7724, lng: -117.1799 }, // Pacific Beach + '92110': { lat: 32.7734, lng: -117.1799 }, // Mission Bay + '92111': { lat: 32.8338, lng: -117.2060 }, // Clairemont + '92113': { lat: 32.6757, lng: -117.0752 }, // Southeastern San Diego + '92114': { lat: 32.7040, lng: -117.1299 }, // Southeastern San Diego + '92115': { lat: 32.7421, lng: -117.1055 }, // City Heights + '92116': { lat: 32.7654, lng: -117.1174 }, // Kensington + '92117': { lat: 32.8348, lng: -117.2060 }, // Clairemont + '92118': { lat: 32.6154, lng: -117.0855 }, // Coronado + '92119': { lat: 32.7492, lng: -117.0724 }, // College Area + '92120': { lat: 32.7502, lng: -117.0734 }, // College Area + '92121': { lat: 32.9360, lng: -117.2360 }, // Sorrento Valley (unique) + '92122': { lat: 32.8354, lng: -117.2554 }, // La Jolla + '92123': { lat: 32.7724, lng: -117.1790 }, // Serra Mesa + '92124': { lat: 32.7734, lng: -117.1809 }, // Serra Mesa + '92126': { lat: 32.9296, lng: -117.1321 }, // Carmel Mountain + '92127': { lat: 33.0210, lng: -117.1166 }, // Rancho Bernardo + '92128': { lat: 32.9590, lng: -117.1230 }, // Rancho Penasquitos '92129': { lat: 32.9584, lng: -117.1253 }, // Rancho Penasquitos (manual) - '92130': { lat: 32.9350, lng: -117.2350 }, // Carmel Valley (unique) - '92131': { lat: 32.9306, lng: -117.1331 }, // Carmel Mountain (unique) - '92132': { lat: 32.8364, lng: -117.2564 }, // La Jolla (unique) - '92133': { lat: 32.7167, lng: -117.1621 }, // Naval Base (unique) - '92134': { lat: 32.7177, lng: -117.1631 }, // Naval Base (unique) - '92135': { lat: 32.7187, lng: -117.1641 }, // Naval Base (unique) - '92136': { lat: 32.7345, lng: -117.1450 }, // Point Loma (unique) - '92137': { lat: 32.7355, lng: -117.1460 }, // Point Loma (unique) - '92138': { lat: 32.6164, lng: -117.0865 }, // Naval Air Station (unique) - '92139': { lat: 32.6757, lng: -117.0762 }, // Paradise Hills (unique) - '92140': { lat: 32.6174, lng: -117.0875 }, // Coronado (unique) - '92145': { lat: 32.7197, lng: -117.1651 }, // Naval Medical Center (unique) - '92147': { lat: 32.7365, lng: -117.1470 }, // Point Loma (unique) - '92154': { lat: 32.5602, lng: -117.0441 }, // San Ysidro (unique) - '92155': { lat: 32.5612, lng: -117.0451 }, // San Ysidro (unique) - '92173': { lat: 32.6791, lng: -117.0210 }, // Skyline Hills (unique) + '92130': { lat: 32.9350, lng: -117.2350 }, // Carmel Valley + '92131': { lat: 32.9306, lng: -117.1331 }, // Carmel Mountain + '92132': { lat: 32.8364, lng: -117.2564 }, // La Jolla + '92133': { lat: 32.7167, lng: -117.1621 }, // Naval Base + '92134': { lat: 32.7177, lng: -117.1631 }, // Naval Base + '92135': { lat: 32.7187, lng: -117.1641 }, // Naval Base + '92136': { lat: 32.7345, lng: -117.1450 }, // Point Loma + '92137': { lat: 32.7355, lng: -117.1460 }, // Point Loma + '92138': { lat: 32.6164, lng: -117.0865 }, // Naval Air Station + '92139': { lat: 32.6757, lng: -117.0762 }, // Paradise Hills + '92140': { lat: 32.6174, lng: -117.0875 }, // Coronado + '92145': { lat: 32.7197, lng: -117.1651 }, // Naval Medical Center + '92147': { lat: 32.7365, lng: -117.1470 }, // Point Loma + '92154': { lat: 32.5602, lng: -117.0441 }, // San Ysidro + '92155': { lat: 32.5612, lng: -117.0451 }, // San Ysidro + '92173': { lat: 32.6791, lng: -117.0210 }, // Skyline Hills }; export function isValidSanDiegoZip(zip: string): boolean { diff --git a/package.json b/package.json index f514145..a1ad331 100644 --- a/package.json +++ b/package.json @@ -4,30 +4,30 @@ "private": true, "scripts": { "dev": "next dev", - "build": "next build", + "build": "npm run test && next build", + "test": "jest", "start": "next start", "lint": "next lint", "deploy": "./deploy.sh", "deploy:nu": "./deploy.nu" }, "dependencies": { - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/object-schema": "^2.0.3", - "glob": "^11.0.3", "leaflet": "^1.9.4", "next": "14.2.5", "react": "^18", "react-dom": "^18", - "react-leaflet": "^4.2.1", - "rimraf": "^6.0.1" + "react-leaflet": "^4.2.1" }, "devDependencies": { + "@types/jest": "^30.0.0", "@types/leaflet": "^1.9.12", - "@types/node": "^20.19.7", - "@types/react": "^18.3.23", + "@types/node": "^20", + "@types/react": "^18", "@types/react-dom": "^18", - "eslint": "^8.57.0", + "eslint": "^8", "eslint-config-next": "14.2.5", - "typescript": "^5.8.3" + "jest": "^30.0.4", + "ts-jest": "^29.4.0", + "typescript": "^5" } }