- 모델이 화면 안내 시 [[NAV:route]] 마커를 붙이면 서버가 검증·제거 후 actions[]로 응답·이력에 실어, 클라가 해당 화면으로 이동하는 버튼을 렌더할 수 있게 함 - chatNavService(마커 파서 + APP_ROUTES 12개 라우트·라벨), NavAction/AppRoute 타입, finalizeExchange 저장·멱등 재반환·이력 매핑까지 전 경로 연결 - 위기/필터로 교체된 응답에는 액션을 붙이지 않고, 목록 밖 route는 드롭(모델 환각 방어) - actions는 route+label만 전달(실제 경로 매핑·이동은 클라가 보유) - 분석 이벤트에 navRoutes/navCount 추가, chatNavService 단위 테스트 추가
55 lines
2.5 KiB
TypeScript
55 lines
2.5 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { extractNavActions, APP_ROUTES } from "../../src/services/chatNavService";
|
|
|
|
describe("chatNavService.extractNavActions", () => {
|
|
it("마커가 없으면 actions 빈 배열·원문 유지(trim만)", () => {
|
|
const r = extractNavActions("오늘 우리 경기 대전에서 6시 30분에 해");
|
|
expect(r.actions).toEqual([]);
|
|
expect(r.clean).toBe("오늘 우리 경기 대전에서 6시 30분에 해");
|
|
});
|
|
|
|
it("유효 마커를 actions로 분리하고 본문에서 제거한다", () => {
|
|
const r = extractNavActions("예측은 예측 탭에서 하면 돼 [[NAV:prediction]]");
|
|
expect(r.actions).toEqual([
|
|
{ type: "navigate", route: "prediction", label: APP_ROUTES.prediction },
|
|
]);
|
|
expect(r.clean).toBe("예측은 예측 탭에서 하면 돼");
|
|
expect(r.clean).not.toContain("[[NAV:");
|
|
});
|
|
|
|
it("여러 마커를 등장 순서대로, 라벨까지 채워 반환한다", () => {
|
|
const r = extractNavActions("출석하고 [[NAV:attendance]] 예측도 해 [[NAV:prediction]]");
|
|
expect(r.actions.map((a) => a.route)).toEqual(["attendance", "prediction"]);
|
|
expect(r.actions[0].label).toBe(APP_ROUTES.attendance);
|
|
expect(r.clean).toBe("출석하고 예측도 해"); // 마커 제거(중간 공백은 남되 양끝 trim)
|
|
});
|
|
|
|
it("알 수 없는 route는 무시한다(모델 환각 방어)", () => {
|
|
const r = extractNavActions("어딘가 가봐 [[NAV:settings_unknown]] [[NAV:schedule]]");
|
|
expect(r.actions.map((a) => a.route)).toEqual(["schedule"]);
|
|
});
|
|
|
|
it("프로토타입 키(toString 등)를 route로 받지 않는다", () => {
|
|
const r = extractNavActions("ㅋㅋ [[NAV:toString]] [[NAV:hasOwnProperty]]");
|
|
expect(r.actions).toEqual([]);
|
|
});
|
|
|
|
it("중복 route는 하나로 접는다", () => {
|
|
const r = extractNavActions("[[NAV:shop]] 포인트는 상점에서 [[NAV:shop]]");
|
|
expect(r.actions.map((a) => a.route)).toEqual(["shop"]);
|
|
});
|
|
|
|
it("마커만 있고 본문이 비면 clean이 빈 문자열(상위에서 필터 처리)", () => {
|
|
const r = extractNavActions("[[NAV:home]]");
|
|
expect(r.clean).toBe("");
|
|
expect(r.actions.map((a) => a.route)).toEqual(["home"]);
|
|
});
|
|
|
|
it("APP_ROUTES는 12개 무파라미터 라우트를 가진다(클라 확정본)", () => {
|
|
expect(Object.keys(APP_ROUTES).sort()).toEqual([
|
|
"attendance", "home", "inquiry", "mypage", "notices", "notification_settings",
|
|
"notifications", "photo_decorator", "prediction", "prediction_history", "schedule", "shop",
|
|
]);
|
|
});
|
|
});
|