Files
homeclaw/tests/geocode-pick.test.ts
T
kimandClaude Opus 5 c4911520f7 fix: 지오코딩 count 10→20 — "New York"이 네브래스카 York로 잡히던 문제
고르는 규칙(나라 명시 우선, 아니면 인구 최다)은 멀쩡했다. 문제는 후보
목록에 뉴욕시가 아예 없었던 것이다. count=10으로 받으면 응답이
York(네브래스카, 7,864명) / Florence(인디애나, 80명) / New York(영국) /
New York(자메이카)로 채워지고 인구 880만의 뉴욕시는 10위 밖으로 밀린다.
그래서 "남은 것 중 최다"가 네브래스카 York를 골랐다 — 에러 없이 조용히.

count=20이면 뉴욕시가 후보에 들어온다. 주요 도시 23곳을 10 vs 20으로
대조했고 바뀐 건 New York 하나뿐이다: York는 영국 요크 유지(뉴욕에
끌려가지 않음), Rome/Paris/London/Cairo/Sydney/Springfield 동일,
Los Angeles·Mexico City·Ho Chi Minh City 등 복합어 10곳도 동일.

후보 선택을 pickGeocodeResult()로 빼서 네트워크 없이 회귀 검증되게 했다.
당시 실제 응답을 픽스처로 넣고, 이 함수의 한계도 테스트로 명시했다 —
목록 밖의 정답은 구제할 수 없으므로 방어선은 호출부의 count이고, 둘을
한 몸으로 봐야 한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 17:34:21 +09:00

71 lines
3.4 KiB
TypeScript

/**
* 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);
});
});