고르는 규칙(나라 명시 우선, 아니면 인구 최다)은 멀쩡했다. 문제는 후보 목록에 뉴욕시가 아예 없었던 것이다. 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>
71 lines
3.4 KiB
TypeScript
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);
|
|
});
|
|
});
|