v2.2.320: /naver-shopping "CATEGORY_TREE is not iterable" 크래시 수정 + 카테고리 목록 조회

- import * as (namespace import) 로 배열 JSON 을 불러오면 esbuild 번들에서
  { default: [...] } 로 감싸져 배열이 아니게 되는 문제 — import x = require(...)
  로 전환해 esbuild/tsc/ts-jest 세 경로 모두 동일하게 동작하도록 수정
- 실제 esbuild 번들 산출물을 실행하는 회귀 테스트 추가 (ts-jest만으론 이 버그를 못 잡음)
- /naver-shopping categories [검색어] — 대분류 목록/세부 카테고리 검색 명령 추가
This commit is contained in:
2026-08-06 10:06:20 +09:00
parent c79eda1862
commit 29ad4cc023
13 changed files with 106 additions and 26 deletions
+34
View File
@@ -6,6 +6,10 @@
import { findCategoryCode, ROOT_CATEGORY_CODES } from '../src/features/naverTrend/categoryCodes';
import { parseShoppingIntent, parseSearchTrendIntent } from '../src/features/naverTrend/intentParser';
import type { IAIService, AIChatResult } from '../src/core/services';
import * as esbuild from 'esbuild';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
function mockAi(content: string, shouldThrow = false): IAIService {
return {
@@ -107,3 +111,33 @@ describe('parseSearchTrendIntent — tolerant JSON 파싱', () => {
expect(r.keywords).toEqual(['원피스 키워드']);
});
});
// v2.2.319 회귀: `import * as x from './data.json'`(namespace import) 를 esbuild 로
// 번들하면 배열 최상위 JSON 이 `{ default: [...] }` 로 감싸져 x 자체가 배열이 아니게
// 된다. ts-jest 는 require() 를 그대로 써서 이 버그가 안 보여 통과했지만 실제 배포
// 번들(esbuild)에서 "CATEGORY_TREE is not iterable" 로 터졌다. default import 로
// 고친 뒤에도 재발 안 하도록 실제 esbuild 번들 결과를 실행해 검증한다.
describe('categoryCodes.ts — esbuild 번들 회귀 방지', () => {
test('esbuild 로 번들링한 결과에서도 카테고리 트리가 정상 로드됨', () => {
const entry = path.join(__dirname, '../src/features/naverTrend/categoryCodes.ts');
const result = esbuild.buildSync({
entryPoints: [entry],
bundle: true,
platform: 'node',
format: 'cjs',
write: false,
});
const tmpFile = path.join(os.tmpdir(), `categoryCodes.bundle.${process.pid}.${Date.now()}.js`);
fs.writeFileSync(tmpFile, result.outputFiles[0].text);
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const mod = require(tmpFile);
const names = mod.listTopLevelCategoryNames();
expect(Array.isArray(names)).toBe(true);
expect(names.length).toBeGreaterThan(5);
expect(mod.findCategoryCode('패션의류')[0]).toMatchObject({ code: '50000000', source: 'root' });
} finally {
fs.unlinkSync(tmpFile);
}
});
});