diff --git a/docs/superpowers/plans/2026-09-28-dtryx-comparison.md b/docs/superpowers/plans/2026-09-28-dtryx-comparison.md index 80eeda46..2d1f7108 100644 --- a/docs/superpowers/plans/2026-09-28-dtryx-comparison.md +++ b/docs/superpowers/plans/2026-09-28-dtryx-comparison.md @@ -12,3 +12,17 @@ 443 1/4, 30443 0/4다. 한 번의 성공은 지속적 복구나 포트 원인을 입증하지 않는다. 실패는 모두 응답 헤더 이전이었다. 9/19·9/21 양 포트 실패 기록과 일치하며 #188을 영구 복구로 반영하지 않는다. 모든 시험 뒤 임시 Worker를 삭제했다. 새 배포의 라우트 전파 전404가 발생한 최초 시도도 finally에서 삭제한 뒤, 인증 거절401로 준비를 확인하도록 보강해 위 실험을 수행했다. 별도 현 운영 좌석 조회3회는 503/15.65s, 503/15.65s, 200/0.82s였다. 오류를200으로 바꾸거나 15초 제한을 늘리는 변경은 하지 않았다. 다음 단계는 업스트림의 Cloudflare 출발 연결 로그와 동일 시각 TCP/TLS/헤더 지연을 대조하는 것이다. + + +## 후속 결정: 호환성 변경으로 #188 반영 + +사용자가 남은 PR도 최대한 반영하도록 요청해, 장애 해결 입증과 엔드포인트 호환성을 구분해 재검토했다. 동일한 Mac에서 극장000067(indieart)·000072(spacedog)의 시간표·상영 날짜·현재 상영작을 각각 두 포트로 호출했다. 총12회 모두200/RetCode success였으며 여섯 쌍의 Recordset이 완전히 일치했다(35~74ms). + +| 극장 | 시간표 | 상영 날짜 | 상영작 | +|---|---:|---:|---:| +| 000067 | 양쪽7건 | 양쪽12건 | 양쪽14건 | +| 000072 | 양쪽5건 | 양쪽67건 | 양쪽14건 | + +이에 표준 HTTPS 주소를 사용하는 호환성 변경으로 #188을 채택한다. 포트가 장애 원인이라는 주석은 제거하고, 테스트의 전역 fetch 복원도 보강한다. 세 엔드포인트 URL 검증과 기존 실패·시간초과 테스트, 전체 check/coverage/build를 실행한다. 배포 후 seats/movies를 반복 조회하여 결과를 별도로 기록한다. 요청 제한15초와 기존 실패/부분 성공 의미를 유지하며, 재시도·다른 포트 폴백·유료 호출은 추가하지 않는다. #187은 계속 열어둔다. + +이 결정은 위 원격 시험 결과를 번복하거나 장애 해결을 주장하는 것이 아니다. 앞선 보류 판단에서 검증 가능한 호환성 변경만 분리해 반영하는 것이다. diff --git a/package-lock.json b/package-lock.json index 2f422369..930cd0b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "daiso", - "version": "1.2.3", + "version": "1.2.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "daiso", - "version": "1.2.3", + "version": "1.2.4", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", diff --git a/package.json b/package.json index 7e6175a7..80b280f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "daiso", - "version": "1.2.3", + "version": "1.2.4", "description": "Korean retail and cinema MCP server, CLI, and Codex Skill for Daiso, convenience stores, marts, Olive Young, and theaters", "main": "dist/index.js", "bin": { diff --git a/src/services/dtryx/api.ts b/src/services/dtryx/api.ts index 4ce91196..18272bad 100644 --- a/src/services/dtryx/api.ts +++ b/src/services/dtryx/api.ts @@ -6,7 +6,11 @@ */ export const DTRYX_API = { - BASE_URL: 'https://api.dtryx.com:30443', + /** + * 두 포트의 공개 조회 응답이 동일함을 확인해 표준 HTTPS 포트를 사용합니다. + * Worker의 간헐 연결 실패는 양쪽 포트에서 관측되어 별도로 추적합니다(#187). + */ + BASE_URL: 'https://api.dtryx.com', THIRDPARTY_PATH: '/dtryx/cms/thirdparty/movie', CHANNEL_CODE: 'homepage', ENDPOINTS: { diff --git a/tests/services/dtryx/api.test.ts b/tests/services/dtryx/api.test.ts new file mode 100644 index 00000000..db6741e7 --- /dev/null +++ b/tests/services/dtryx/api.test.ts @@ -0,0 +1,65 @@ +/** + * 디트릭스 API 상수 테스트 + */ + +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { DTRYX_API } from '../../../src/services/dtryx/api.js'; +import { + fetchDtryxNowShowing, + fetchDtryxPlayDates, + fetchDtryxTimetable, +} from '../../../src/services/dtryx/client.js'; + +describe('디트릭스 API 상수', () => { + it('표준 포트를 사용한다', () => { + const url = new URL(DTRYX_API.BASE_URL); + + expect(url.protocol).toBe('https:'); + expect(url.port).toBe(''); + }); + + it('비표준 포트를 포함하지 않는다', () => { + // 검증된 표준 HTTPS 주소가 비표준 포트로 되돌아가지 않도록 확인합니다. + expect(DTRYX_API.BASE_URL).not.toMatch(/:\d+$/); + }); +}); + +describe('디트릭스 요청 URL', () => { + const mockFetch = vi.fn(); + + beforeEach(() => { + mockFetch.mockReset(); + mockFetch.mockResolvedValue( + new Response(JSON.stringify({ RetCode: 'success', Recordset: [] }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }), + ); + vi.stubGlobal('fetch', mockFetch); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + vi.restoreAllMocks(); + }); + + const request = { brandCode: 'indieart', cinemaCode: '000067' }; + + it('상영시간표 요청이 표준 포트로 나간다', async () => { + await fetchDtryxTimetable({ ...request, playDate: '20260918' }); + + expect(new URL(String(mockFetch.mock.calls[0]?.[0])).port).toBe(''); + }); + + it('상영 날짜 요청이 표준 포트로 나간다', async () => { + await fetchDtryxPlayDates(request); + + expect(new URL(String(mockFetch.mock.calls[0]?.[0])).port).toBe(''); + }); + + it('상영작 요청이 표준 포트로 나간다', async () => { + await fetchDtryxNowShowing(request); + + expect(new URL(String(mockFetch.mock.calls[0]?.[0])).port).toBe(''); + }); +});