Files
homeclaw/tests/weather-past.test.ts
kimandClaude Opus 5 d0ddce1361 feat: weather_kma에 과거 날짜 조회(type="past") 추가
"어제 제주 비 얼마나 왔어"를 물었더니 도구에 과거 조회가 없어서 모델이
web_search로 새고, 2026년 달력·기상청 도움말 페이지만 긁어온 뒤 "정확한
수치를 찾아드리지 못해 죄송합니다"로 끝났다. 검색 실패가 아니라 도구
공백이었다. ASOS 일자료는 한 번의 호출로 그 값을 준다(제주 8/15 = 13.9mm).

date는 "어제"·"그저께"·"3일 전"·"2026-08-15"·"8월 15일"·"08-15"·"20260815"를
받는다. 기준은 KST — 서버가 UTC라 new Date()로 날짜를 뽑으면 한국 시각
09시 이전에 하루 어긋난다. endDate를 주면 일별 + 기간 합계를 낸다.

모델 실수 두 가지를 코드에서 흡수한다:
 - date만 주고 type을 빠뜨리는 호출이 흔해서, date가 있으면 past로 본다.
 - 오늘 날짜를 넣으면 그냥 실패시키지 않고 "일자료는 전날까지, 오늘은
   type=current" 라고 안내한다. 맨 실패로 두면 또 web_search로 샌다.

sumRn의 빈 값은 결측이 아니라 그날 비가 안 왔다는 뜻이다(시간자료 rn과
같은 규칙, 서울 8/10~8/13이 빈 값이고 그 기간 무강수인 것으로 확인).
처음엔 "관측값 없음"으로 내보냈는데 그대로 뒀으면 맑았던 날마다 모델이
"자료가 없습니다"라고 답했을 것이라 0으로 말하되 미량("0.0")과 구분한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 14:24:20 +09:00

76 lines
3.2 KiB
TypeScript

/**
* weather-past.test.ts
*
* weather_kma의 과거 관측 조회(type="past"). 2026-08-16에 붙였다.
*
* 계기: "어제 제주 비 얼마나 왔어"를 물었더니 도구에 과거 조회가 없어서 모델이
* web_search로 새고, 기상청 도움말 페이지·2026년 달력 따위만 긁어온 뒤
* "정확한 수치를 찾아드리지 못해 죄송합니다"로 끝났다. ASOS 일자료는 한 번의 호출로
* 그 값을 준다(제주 2026-08-15 = 13.9mm).
*/
import { test, describe } from 'node:test';
import assert from 'node:assert/strict';
import { parseKstDate, parseAsosDaily } from '../src/tools/weather';
// 2026-08-16 12:00 KST = 2026-08-16 03:00 UTC
const NOW = new Date('2026-08-16T03:00:00Z');
describe('parseKstDate — 날짜 해석', () => {
test('상대어', () => {
// 모델이 "어제"를 그대로 넘기는 일이 잦다.
assert.equal(parseKstDate('오늘', NOW), '20260816');
assert.equal(parseKstDate('어제', NOW), '20260815');
assert.equal(parseKstDate('그저께', NOW), '20260814');
assert.equal(parseKstDate('3일 전', NOW), '20260813');
});
test('절대 날짜 표기들', () => {
assert.equal(parseKstDate('2026-08-15', NOW), '20260815');
assert.equal(parseKstDate('2026/8/5', NOW), '20260805');
assert.equal(parseKstDate('20260815', NOW), '20260815');
assert.equal(parseKstDate('8월 15일', NOW), '20260815');
assert.equal(parseKstDate('08-15', NOW), '20260815');
});
test('KST 기준이라 UTC 자정 근처에서 하루 안 밀린다', () => {
// 2026-08-16 00:30 KST = 2026-08-15 15:30 UTC. 서버가 UTC라 new Date()로 뽑으면
// "오늘"이 8/15로 나와 하루 어긋난다.
const lateNight = new Date('2026-08-15T15:30:00Z');
assert.equal(parseKstDate('오늘', lateNight), '20260816');
assert.equal(parseKstDate('어제', lateNight), '20260815');
});
test('해석 불가는 null', () => {
assert.equal(parseKstDate('언젠가', NOW), null);
assert.equal(parseKstDate('', NOW), null);
});
});
describe('parseAsosDaily — 일자료 한 줄 요약', () => {
test('강수량과 기온을 낸다', () => {
const out = parseAsosDaily({ tm: '2026-08-15', stnNm: '제주', sumRn: '13.9', maxTa: '29.5', minTa: '24.7', avgTa: '26.5' });
assert.ok(out[0].includes('2026-08-15'));
assert.ok(out[1].includes('13.9 mm'));
assert.ok(out.some(l => l.includes('24.7~29.5')));
});
test('빈 sumRn은 결측이 아니라 "강수 없음"이다', () => {
// "관측값 없음"으로 내보내면 맑았던 날마다 모델이 "자료가 없다"고 답하게 된다.
const out = parseAsosDaily({ tm: '2026-08-11', stnNm: '구미', sumRn: '' });
assert.ok(out[1].includes('0 mm'));
assert.ok(out[1].includes('강수 없음'));
});
test('"0.0"은 미량 강수로 구분한다', () => {
const out = parseAsosDaily({ tm: '2026-08-13', stnNm: '구미', sumRn: '0.0' });
assert.ok(out[1].includes('미량'));
});
test('없는 항목은 줄을 만들지 않는다', () => {
const out = parseAsosDaily({ tm: '2026-08-15', stnNm: '제주', sumRn: '1.0' });
assert.equal(out.filter(l => l.includes('평균습도')).length, 0);
assert.equal(out.filter(l => l.includes('일조시간')).length, 0);
});
});