o2o-site-AEO/ontology/scripts/smoke.ts
Mina Choi 11d30bb3d1 [chore] solution,admin,ontology: 코드 주석을 한 줄로 — 히스토리 주석 삭제
여러 줄 주석이 설명보다 경위(예전·실측·지적)를 적고 있어 읽는 사람이 결론을 찾기 어려웠다.

- ts·tsx·js·mjs·css·py 478개: 여러 줄 주석은 첫 문장 한 줄로, 과거형·날짜 문장은 삭제
- 주석 위치는 TypeScript 파서·파이썬 tokenize/ast 로 찾는다 — 문자열 안의 # · /* 는 건드리지 않는다
- eslint·ts·noqa·type: ignore 같은 지시 주석은 그대로 둔다

파이썬 275개 정리 전후 AST 동일, TS 298개 주석 뺀 토큰 동일(빈 JSX 주석 10곳만 차이).
site·frontend·admin tsc, site vitest 105 passed

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:05:19 +09:00

100 lines
3.5 KiB
TypeScript

/** 로컬 엔드투엔드 점검 스크립트. */
const BASE = process.env.BASE_URL ?? 'http://localhost:3100';
const j = async (method: string, path: string, body?: unknown) => {
const res = await fetch(`${BASE}${path}`, {
method,
headers: body ? { 'content-type': 'application/json' } : undefined,
body: body ? JSON.stringify(body) : undefined,
});
const text = await res.text();
if (!res.ok) throw new Error(`${method} ${path} → ${res.status} ${text}`);
return text ? JSON.parse(text) : null;
};
const h = (t: string) => console.log(`\n\x1b[1m${t}\x1b[0m`);
async function main() {
h('0. health');
console.log(' ', await j('GET', '/health'));
h('1. site-1001 생성 (첫 업체 — 전부 신규)');
const a = await j('POST', '/v1/merchants/site-1001/generate?sync=true');
printStats(a);
h('2. site-1002 생성 (같은 강남/미용실 — 중복제거 발동)');
const b = await j('POST', '/v1/merchants/site-1002/generate?sync=true');
printStats(b);
printDetails(b);
h('3. publish 웹훅 + 표기 변형 (trigram 단계)');
const c = await j('POST', '/v1/merchants/publish', {
externalId: 'site-1003',
name: '강남 뷰티랩',
industryId: 'beauty.hair',
regionId: 'kr.seoul.gangnam',
description: '강남 미용실. 염색 전문.',
profile: { services: ['뿌리염색약', '여성펌'], features: ['주차가능'] },
sync: true,
});
printStats(c.generation);
printDetails(c.generation);
h('4. SEO payload');
const seo = await j('GET', '/v1/sites/site-1001/seo?limit=8');
console.log(' title :', seo.title);
console.log(' description:', seo.description);
console.log(' keywords :', seo.keywords.join(', '));
h('5. AEO payload');
const aeo = await j('GET', '/v1/sites/site-1001/aeo?limit=3');
for (const f of aeo.faqs) console.log(` Q. ${f.question}\n A. ${f.answer}`);
h('6. 의미 기반 키워드 검색');
const found = await j('POST', '/v1/keywords/search', { query: '강남 미용실 예약하고 싶어요', limit: 5 });
for (const r of found) console.log(` ${r.score.toFixed(3)} ${r.canonical} (${r.intent}, ${r.usage_count}개 업체)`);
h('7. 성과 피드백 → 저성과 강등');
console.log(
' ',
await j('POST', '/v1/sites/site-1001/performance', {
items: [
{ keyword: '강남 미용실 후기', impressions: 500, clicks: 0 },
{ keyword: '강남 미용실', impressions: 300, clicks: 40 },
],
}),
);
h('8. 비동기 큐 (BullMQ)');
console.log(' ', await j('POST', '/v1/merchants/site-2001/generate'));
for (let i = 0; i < 30; i++) {
const s = await j('GET', '/v1/sites/site-2001/seo?limit=5');
if (s.keywords.length) {
console.log(' 워커 처리 완료 →', s.keywords.join(', '));
break;
}
await new Promise((r) => setTimeout(r, 500));
}
console.log('\n✅ smoke 완료');
}
function printStats(s: any) {
console.log(
` 후보 ${s.candidates} → 신규 ${s.created} / 중복(정확 ${s.matchedExact}, 표기 ${s.matchedTrigram}, 의미 ${s.matchedVector})` +
` / 차단 ${s.rejected} / 연결 ${s.linked} / QA ${s.qaCreated} (${s.durationMs}ms)`,
);
}
function printDetails(s: any) {
for (const d of s.details ?? []) {
const sim = d.similarity != null ? ` (sim=${d.similarity.toFixed(3)} → '${d.matchedTo}')` : '';
console.log(` ${d.action.padEnd(16)} ${d.candidate}${sim}`);
}
}
main().catch((e) => {
console.error('\n❌', e.message);
process.exit(1);
});