8000 fix: 숫자 역 이름 검색 보완 및 의존성 PR 통합 by hmmhmmhm · Pull Request #283 · hmmhmmhm/daiso-mcp · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
512 changes: 256 additions & 256 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 12 additions & 3 deletions src/utils/geocode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,14 @@ const normalize = (value: string) =>
.replace(/제주도/gu, '제주')
.replace(/[^0-9a-z가-힣]/giu, '')
.toLowerCase();
function matches(query: string, value: string): boolean {
function matches(query: string, value: string, placeName: string): boolean {
// 역 이름의 숫자가 주소나 다른 역의 출구 번호와 대신 일치하는 것을 막습니다.
const station = query.match(/([0-9a-z가-힣]+역)$/iu)?.[1];
if (
station &&
!new RegExp(`${station}(?:[0-9]+호선|$|[^가-힣])`, 'iu').test(placeName)
)
return false;
const tokens =
query
.replace(/^대한민국\s*/u, '')
Expand Down Expand Up @@ -123,6 +130,7 @@ function kakaoLocation(
: matches(
query,
`${document.place_name} ${document.address_name || ''} ${document.road_address_name || ''}`,
document.place_name!,
))
)
continue;
Expand Down Expand Up @@ -173,7 +181,8 @@ export async function geocodeLocation(
// 대체 공급자도 최초 요청의 남은 시간만 사용합니다.
const deadline = Date.now() + (options.timeout ?? options.timeoutMs ?? 10000);
const remaining = () => deadline - Date.now();
const isAddress = /(?:로|길|동|읍|면|리)\s*\d/u.test(keyword);
// '을지로3가역'처럼 숫자가 든 역 이름은 주소가 아니라 장소로 검색합니다.
const isAddress = !keyword.endsWith('역') && /(?:로|길|동|읍|면|리)\s*\d/u.test(keyword);
let documents: KakaoDocument[] = [];
const kakaoTimeout = remaining();
if (options.kakaoRestApiKey?.trim() && kakaoTimeout > 0) {
Expand Down Expand Up @@ -227,7 +236,7 @@ export async function geocodeLocation(
validKoreanCoordinates(item.latitude, item.longitude) &&
(isAddress
? matchesAddress(keyword, item.address) || matchesAddress(keyword, item.roadAddress)
: matches(keyword, `${item.name} ${item.address} ${item.roadAddress}`)),
: matches(keyword, `${item.name} ${item.address} ${item.roadAddress}`, item.name)),
);
if (place)
return {
Expand Down
2 changes: 1 addition & 1 deletion tests/app/app-api-opinet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe('GET /api/opinet', () => {
.mockResolvedValueOnce(new Response(JSON.stringify({ RESULT: { OIL: [] } })))
.mockResolvedValueOnce(
new Response(
JSON.stringify({ documents: [{ place_name: '안산 중앙역', address_name: '서울 강남역', y: 37.4979, x: 127.0276 }] }),
JSON.stringify({ documents: [{ place_name: '강남역', address_name: '서울 강남구 역삼동', y: 37.4979, x: 127.0276 }] }),
),
)
.mockResolvedValueOnce(new Response(JSON.stringify({ RESULT: { OIL: [] } })));
Expand Down
9 changes: 9 additions & 0 deletions tests/durableObjects/upstreamRouteHealth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@ describe('durable state edge cases', () => {
await s.call('/failure', { key: 'cu-stock', at });
await s.call('/failure', { key: 'cu-stock', at });
});
it('accepts an equal-time probe when no request failure exists', async () => {
const s = store();
const checkedAt = Date.now();
await s.call('/checks', { checks: [{ key: 'cu-stock', direct: false, checkedAt }], relays: [] });
await s.call('/checks', { checks: [{ key: 'cu-stock', direct: true, checkedAt }], relays: [] });
expect(await (await s.call('/snapshot')).json()).toMatchObject({
routes: { 'cu-stock': { direct: true, checkedAt } },
});
});
it('allows healthy probe only after the failure cooldown expires', async () => {
const s = store();
const at = Date.now() - 300001;
Expand Down
52 changes: 52 additions & 0 deletions tests/utils/geocode-fallback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,58 @@ describe('정확한 주소 fallback', () => {
'/v1/search/local.json',
]);
});
it.each(['을지로3가역', '종로3가역'])('숫자가 든 역 이름은 장소로 검색한다: %s', async (query) => {
const fetchImpl = responses(
json({
documents: [
{
place_name: `${query} 1호선`,
address_name: '서울 종로구 예시동 10',
road_address_name: '서울 종로구 예시로 지하 129',
x: '126.99',
y: '37.57',
},
],
}),
);
expect(await geocodeLocation(query, { kakaoRestApiKey: 'test-kakao', fetchImpl })).toEqual({
latitude: 37.57,
longitude: 126.99,
formattedAddress: '서울 종로구 예시로 지하 129',
});
expect(new URL(String(fetchImpl.mock.calls[0][0])).pathname).toBe(
'/v2/local/search/keyword.json',
);
});
it.each(['Kakao', 'Naver'])('지역명을 띄어 쓴 역 이름도 같은 장소를 찾는다: %s', async (provider) => {
const fetchImpl = responses(json(provider === 'Kakao' ? {
documents: [{ place_name: '광주송정역', address_name: '광주 광산구 송정동', x: '126.79', y: '35.13' }],
} : {
items: [{ title: '광주송정역', address: '광주 광산구 송정동',
roadAddress: '광주 광산구 상무대로 201', mapx: '1267900000', mapy: '351300000' }],
}));
expect(await geocodeLocation('광주 송정역', {
...(provider === 'Kakao' ? { kakaoRestApiKey: 'test-kakao' } : {
naverClientId: 'test-id', naverClientSecret: 'test-secret',
}), fetchImpl,
})).toMatchObject({ latitude: 35.13, longitude: 126.79 });
});
it.each(['Kakao', 'Naver'])('다른 역의 출구 번호를 역 이름의 숫자로 인정하지 않는다: %s', async (provider) => {
const fetchImpl = responses(json(provider === 'Kakao' ? {
documents: [{
place_name: '종로5가역 3번출구', address_name: '서울 종로구 종로5가',
road_address_name: '서울 종로구 종로 지하 216', x: '127.001', y: '37.57',
}],
} : {
items: [{ title: '종로5가역 3번출구', address: '서울 종로구 종로5가',
roadAddress: '서울 종로구 종로 지하 216', mapx: '1270010000', mapy: '375700000' }],
}));
expect(await geocodeLocation('종로3가역', {
...(provider === 'Kakao' ? { kakaoRestApiKey: 'test-kakao' } : {
naverClientId: 'test-id', naverClientSecret: 'test-secret',
}), fetchImpl,
})).toBeNull();
});
it('빈 입력과 환경키 없는 입력은 요청하지 않는다', async () => {
expect(await geocodeLocation(' ')).toBeNull();
expect(await geocodeLocation('예시역')).toBeNull();
Expand Down
4 changes: 2 additions & 2 deletions tests/workflows/health-check-retry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ it.each(['CGV_RELAY_FAILED', 'DTRYX_RELAY_FAILED', 'CONVENIENCE_RELAY_FAILED', '
const result = await retryMinuteChecks(payload([check('cli.contract', { errorCode })]), { endpoint, secret: 'secret', fetchImpl, sleep: async () => undefined });
expect(result.payload.status).toBe('ok'); expect(fetchImpl).toHaveBeenCalledTimes(1);
});
it('CLI 계약의 외부 비분류 오류는 분당 제한 문구가 있어도 반복하지 않는다', async () => {
it.each(['UPSTREAM_FAILED', undefined])('CLI 계약의 비분류 오류 %s는 분당 제한 문구가 있어도 반복하지 않는다', async errorCode => {
const fetchImpl = vi.fn();
await retryMinuteChecks(payload([check('cli.contract', { errorCode: 'UPSTREAM_FAILED' })]), { endpoint, secret: 'secret', fetchImpl, sleep: async () => undefined });
await retryMinuteChecks(payload([check('cli.contract', { errorCode })]), { endpoint, secret: 'secret', fetchImpl, sleep: async () => undefined });
expect(fetchImpl).not.toHaveBeenCalled();
});
it('여러 ID 또는 캐시 결과는 최초 실패를 대체하지 않는다', async () => {
Expand Down
Loading
0