From 2e03b5633fefd7a0883833efdf62b0dbdabcc872 Mon Sep 17 00:00:00 2001 From: helenanova Date: Thu, 10 Sep 2026 17:52:42 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=A9=94=EA=B0=80=EB=B0=95=EC=8A=A4=20?= =?UTF-8?q?CLI=20=EB=AA=85=EB=A0=B9=203=EC=A2=85=20=EC=B6=94=EA=B0=80=20(#?= =?UTF-8?q?179)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 서버에 이미 구현된 메가박스 API(megabox/theaters, megabox/movies, megabox/seats)를 CLI에서도 사용할 수 있도록 megabox-theaters, megabox-movies, megabox-seats 명령을 추가합니다. cgv/lottecinema 명령과 동일한 옵션 체계(keyword, lat/lng, playDate, theaterId, movieId, areaCode, limit)를 따릅니다. --- .../daiso-cli/references/cli-command-map.md | 3 + src/cli.ts | 7 ++ src/cli/commandOptions.ts | 24 ++++++ src/cli/commands/cinema.ts | 30 +++++++ src/cliHelpDefinitions.ts | 33 ++++++++ tests/app/cli.test.ts | 84 +++++++++++++++++++ 6 files changed, 181 insertions(+) diff --git a/skills/daiso-cli/references/cli-command-map.md b/skills/daiso-cli/references/cli-command-map.md index 34cebb13..c9651f48 100644 --- a/skills/daiso-cli/references/cli-command-map.md +++ b/skills/daiso-cli/references/cli-command-map.md @@ -73,6 +73,9 @@ Use `--json` for structured output. In shell commands, quoted Korean strings are - Lotte Cinema theaters: `npx daiso lottecinema-theaters 잠실 --limit 10 --json` - Lotte Cinema movies: `npx daiso get /api/lottecinema/movies --theaterId --json` - Lotte Cinema seats: `npx daiso get /api/lottecinema/seats --theaterId --movieId --playDate --json` +- Megabox theaters: `npx daiso megabox-theaters 강남 --limit 10 --json` +- Megabox movies: `npx daiso megabox-movies --theaterId --playDate --json` +- Megabox seats: `npx daiso megabox-seats --theaterId --movieId --playDate --json` - CGV theaters: `npx daiso cgv-theaters 강남 --limit 10 --json` - CGV movies: `npx daiso cgv-movies --playDate --theaterCode --json` - CGV timetable: `npx daiso cgv-timetable --playDate --theaterCode --json` diff --git a/src/cli.ts b/src/cli.ts index 18f0da54..8d003870 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -48,6 +48,9 @@ import { handleLottecinemaTheaters, handleLottecinemaMovies, handleLottecinemaSeats, + handleMegaboxTheaters, + handleMegaboxMovies, + handleMegaboxSeats, } from './cli/commands/cinema.js'; export type { CliDeps } from './cli/types.js'; @@ -179,6 +182,10 @@ export async function runCli(argv: string[], deps?: Partial): Promise { + return await runApiCommand({ + command: 'megabox-theaters', + options, + deps, + path: '/api/megabox/theaters', + configure: applyKeywordPositional, + }); +} + +export async function handleMegaboxMovies(options: string[], deps: CliDeps): Promise { + return await runApiCommand({ + command: 'megabox-movies', + options, + deps, + path: '/api/megabox/movies', + configure: applyKeywordPositional, + }); +} + +export async function handleMegaboxSeats(options: string[], deps: CliDeps): Promise { + return await runApiCommand({ + command: 'megabox-seats', + options, + deps, + path: '/api/megabox/seats', + configure: applyKeywordPositional, + }); +} + export async function handleCgvTheaters(options: string[], deps: CliDeps): Promise { return await runApiCommand({ command: 'cgv-theaters', diff --git a/src/cliHelpDefinitions.ts b/src/cliHelpDefinitions.ts index 61d9bee2..b356ffb0 100644 --- a/src/cliHelpDefinitions.ts +++ b/src/cliHelpDefinitions.ts @@ -24,6 +24,9 @@ export type CommandName = | 'lottecinema-theaters' | 'lottecinema-movies' | 'lottecinema-seats' + | 'megabox-theaters' + | 'megabox-movies' + | 'megabox-seats' | 'lottemart-stores' | 'lottemart-products' | 'emart24-stores' @@ -60,6 +63,9 @@ export const COMMAND_LIST: CommandName[] = [ 'lottecinema-theaters', 'lottecinema-movies', 'lottecinema-seats', + 'megabox-theaters', + 'megabox-movies', + 'megabox-seats', 'lottemart-stores', 'lottemart-products', 'emart24-stores', @@ -97,6 +103,9 @@ export const COMMAND_SUMMARY: Record = { 'lottecinema-theaters': '롯데시네마 주변 지점 조회', 'lottecinema-movies': '롯데시네마 영화/회차 조회', 'lottecinema-seats': '롯데시네마 잔여 좌석 조회', + 'megabox-theaters': '메가박스 주변 지점 조회', + 'megabox-movies': '메가박스 영화/회차 조회', + 'megabox-seats': '메가박스 잔여 좌석 조회', 'lottemart-stores': '롯데마트 매장 검색', 'lottemart-products': '롯데마트 상품 검색', 'emart24-stores': '이마트24 매장 검색', @@ -271,6 +280,30 @@ export const COMMAND_DETAIL: Record = { '예시: daiso lottecinema-seats 잠실 --playDate 20260310 --movieId 23816', '예시: daiso lottecinema-seats --playDate 20260310 --keyword "안산 중앙역" --movieId 23816', ], + 'megabox-theaters': [ + '명령: megabox-theaters', + '설명: 메가박스 주변 지점 API를 호출합니다.', + '사용법: daiso megabox-theaters [keyword] [--lat 값] [--lng 값] [--playDate YYYYMMDD] [--areaCode 값] [--limit N] [--json]', + '옵션: --keyword, --lat, --lng, --playDate, --areaCode, --limit, --json', + '예시: daiso megabox-theaters 강남 --limit 5', + '예시: daiso megabox-theaters --keyword "코엑스" --limit 5', + ], + 'megabox-movies': [ + '명령: megabox-movies', + '설명: 메가박스 영화/회차 API를 호출합니다.', + '사용법: daiso megabox-movies [keyword] [--playDate YYYYMMDD] [--theaterId 값] [--movieId 값] [--areaCode 값] [--lat 값] [--lng 값] [--json]', + '옵션: --playDate, --theaterId, --movieId, --keyword, --areaCode, --lat, --lng, --json', + '예시: daiso megabox-movies 강남 --playDate 20260310', + '예시: daiso megabox-movies --playDate 20260310 --keyword "코엑스"', + ], + 'megabox-seats': [ + '명령: megabox-seats', + '설명: 메가박스 잔여 좌석 API를 호출합니다.', + '사용법: daiso megabox-seats [keyword] [--playDate YYYYMMDD] [--theaterId 값] [--movieId 값] [--areaCode 값] [--lat 값] [--lng 값] [--limit N] [--json]', + '옵션: --playDate, --theaterId, --movieId, --keyword, --areaCode, --lat, --lng, --limit, --json', + '예시: daiso megabox-seats 강남 --playDate 20260310 --movieId 23816', + '예시: daiso megabox-seats --playDate 20260310 --keyword "코엑스" --movieId 23816', + ], 'lottemart-stores': [ '명령: lottemart-stores', '설명: 롯데마트 계열 매장 검색 API를 호출합니다.', diff --git a/tests/app/cli.test.ts b/tests/app/cli.test.ts index a7ebd952..c2dbcd54 100644 --- a/tests/app/cli.test.ts +++ b/tests/app/cli.test.ts @@ -630,6 +630,87 @@ describe('CLI', () => { ); }); + it('megabox-theaters 명령은 메가박스 주변 지점 API를 호출한다', async () => { + const { deps } = createDeps(); + const fetchImpl = vi.fn().mockResolvedValue({ + ok: true, + json: vi + .fn() + .mockResolvedValue({ success: true, data: { theaters: [] }, meta: { total: 0 } }), + } as unknown as Response); + deps.fetchImpl = fetchImpl; + + const exitCode = await runCli( + ['megabox-theaters', '--lat', '37.5125', '--lng', '127.0589', '--limit', '5'], + deps, + ); + + expect(exitCode).toBe(0); + expect(fetchImpl).toHaveBeenCalledWith( + 'https://mcp.aka.page/api/megabox/theaters?lat=37.5125&lng=127.0589&limit=5', + ); + }); + + it('megabox-theaters 명령은 위치 검색어를 positional 인자로 받는다', async () => { + const { deps } = createDeps(); + const fetchImpl = vi.fn().mockResolvedValue({ + ok: true, + json: vi + .fn() + .mockResolvedValue({ success: true, data: { theaters: [] }, meta: { total: 0 } }), + } as unknown as Response); + deps.fetchImpl = fetchImpl; + + const exitCode = await runCli(['megabox-theaters', '강남', '--limit', '5'], deps); + + expect(exitCode).toBe(0); + expect(fetchImpl).toHaveBeenCalledWith( + 'https://mcp.aka.page/api/megabox/theaters?limit=5&keyword=%EA%B0%95%EB%82%A8', + ); + }); + + it('megabox-movies 명령은 메가박스 영화/회차 API를 호출한다', async () => { + const { deps } = createDeps(); + const fetchImpl = vi.fn().mockResolvedValue({ + ok: true, + json: vi + .fn() + .mockResolvedValue({ success: true, data: { movies: [] }, meta: { total: 0 } }), + } as unknown as Response); + deps.fetchImpl = fetchImpl; + + const exitCode = await runCli( + ['megabox-movies', '--playDate', '20260310', '--theaterId', '1372'], + deps, + ); + + expect(exitCode).toBe(0); + expect(fetchImpl).toHaveBeenCalledWith( + 'https://mcp.aka.page/api/megabox/movies?playDate=20260310&theaterId=1372', + ); + }); + + it('megabox-seats 명령은 메가박스 잔여 좌석 API를 호출한다', async () => { + const { deps } = createDeps(); + const fetchImpl = vi.fn().mockResolvedValue({ + ok: true, + json: vi + .fn() + .mockResolvedValue({ success: true, data: { seats: [] }, meta: { total: 0 } }), + } as unknown as Response); + deps.fetchImpl = fetchImpl; + + const exitCode = await runCli( + ['megabox-seats', '--playDate', '20260310', '--theaterId', '1372', '--movieId', '23816'], + deps, + ); + + expect(exitCode).toBe(0); + expect(fetchImpl).toHaveBeenCalledWith( + 'https://mcp.aka.page/api/megabox/seats?playDate=20260310&theaterId=1372&movieId=23816', + ); + }); + it('lottecinema-movies 명령은 롯데시네마 영화/회차 API를 호출한다', async () => { const { deps } = createDeps(); const fetchImpl = vi.fn().mockResolvedValue({ @@ -1147,6 +1228,9 @@ describe('CLI', () => { ['lottecinema-theaters', ['lottecinema-theaters', '잠실']], ['lottecinema-movies', ['lottecinema-movies', '잠실']], ['lottecinema-seats', ['lottecinema-seats', '잠실']], + ['megabox-theaters', ['megabox-theaters', '강남']], + ['megabox-movies', ['megabox-movies', '강남']], + ['megabox-seats', ['megabox-seats', '강남']], ['emart24-stores', ['emart24-stores', '강남']], ['emart24-products', ['emart24-products', '두바이']], ['emart24-inventory', ['emart24-inventory', '8800244010504', '--bizNoArr', '28339']],