/** * geocode-pick.test.ts * * Open-Meteo 지오코딩 후보 중 하나를 고르는 규칙. * * 2026-08-16: "New York"이 네브래스카의 York(인구 7,864)로 잡히고 있었다. 고르는 규칙은 * 멀쩡했다 — 문제는 **후보 목록에 뉴욕시가 아예 없었다**는 것이다. count=10으로 받으면 * 응답이 York(NE)·Florence(IN)·New York(영국)·New York(자메이카)로 채워지고 인구 880만의 * 뉴욕시는 10위 밖으로 밀린다. 그래서 "남은 것 중 최다"가 네브래스카를 골랐다. * * count=20으로 올려 고쳤다. 주요 도시 23곳을 10 vs 20으로 대조했을 때 바뀐 건 New York * 하나뿐이었다(York→영국 요크, Rome→로마, Springfield→일리노이 등 전부 그대로). * * 아래 후보 배열은 그때 실제로 받은 응답에서 추린 것이다. 이 테스트는 네트워크를 안 탄다 — * count 회귀 자체는 못 잡지만, "정답이 목록에 있으면 반드시 그걸 고른다"를 고정한다. */ import { test, describe } from 'node:test'; import assert from 'node:assert/strict'; import { pickGeocodeResult } from '../src/tools/weather'; // count=10일 때 실제로 돌아온 목록 — 뉴욕시가 없다. const NY_COUNT10 = [ { name: 'York', country_code: 'US', country: '미국', population: 7864, latitude: 40.87, longitude: -97.59 }, { name: 'Florence', country_code: 'US', country: '미국', population: 80, latitude: 38.78, longitude: -84.92 }, { name: 'New York', country_code: 'GB', country: '영국', latitude: 53.08, longitude: -0.14 }, { name: 'New York', country_code: 'JM', country: '자메이카', latitude: 18.25, longitude: -77.18 }, ]; // count=20이면 뉴욕시가 들어온다. const NY_COUNT20 = [...NY_COUNT10, { name: '뉴욕', country_code: 'US', country: '미국', population: 8804190, latitude: 40.71, longitude: -74.01 }]; describe('pickGeocodeResult', () => { test('정답이 목록에 있으면 인구 최다로 뉴욕시를 고른다', () => { const r = pickGeocodeResult(NY_COUNT20); assert.equal(r.name, '뉴욕'); assert.equal(Math.round(r.latitude), 41); }); test('목록에 없으면 못 고른다 — 이게 count=10일 때의 실제 실패', () => { // 규칙의 한계를 명시해 둔다: 이 함수는 목록 밖의 정답을 구제할 수 없다. // 방어선은 호출부의 count이고, 그래서 둘을 한 몸으로 봐야 한다. const r = pickGeocodeResult(NY_COUNT10); assert.equal(r.name, 'York'); }); test('나라가 명시되면 인구를 제치고 그 나라를 고른다', () => { const r = pickGeocodeResult(NY_COUNT20, 'GB'); assert.equal(r.country_code, 'GB'); }); test('명시된 나라가 목록에 없으면 인구 최다로 물러난다', () => { const r = pickGeocodeResult(NY_COUNT20, 'FR'); assert.equal(r.name, '뉴욕'); }); test('인구 필드가 전부 비어도 첫 후보를 돌려준다', () => { // Open-Meteo는 population을 통째로 비워 보내는 경우가 있다(구미 사건의 원인이기도 했다). const none = [ { name: 'A', latitude: 1, longitude: 1 }, { name: 'B', latitude: 2, longitude: 2 }, ]; assert.equal(pickGeocodeResult(none).name, 'A'); }); test('빈 목록은 null', () => { assert.equal(pickGeocodeResult([]), null); assert.equal(pickGeocodeResult(undefined as any), null); }); });