/* PLAN 386-2 단위검증 헬퍼 — 진짜 `design_page_data.ts` 를 서버 흉내(받은 횟수 셈)로 돌림. * 결과 = 표준출력 JSON 한 벌. 짝 = `test_384_2_page_data.py`. */ const fs = require("fs"); const path = require("path"); const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); const ROOT = path.join(__dirname, "..", ".."); const calls = { structures: 0, pipes: 0, version: 0 }; let failPipes = false; const routeJs = ts.transpileModule( fs.readFileSync(path.join(ROOT, "A00_Common", "design", "design_page_route.ts"), "utf8"), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 } }, ).outputText; const routeBox = { exports: {} }; new Function("exports", "module", "require", routeJs)(routeBox.exports, routeBox, () => ({})); const STUBS = { "./design_page_route": routeBox.exports, // 노선 id 한 칸(386-2b · 진짜) "../../B04_PreProcess/B04_PreProcess_Api_Fetch": { fetchDetailPipePoints: async () => { calls.pipes += 1; if (failPipes) throw new Error("끊김"); return { pipe_points: [{ chainage_m: 10, facility: "pipe" }], basins: [] }; }, }, // 횡단 context(세션 캐시) — 노선 id 한 칸(386-2b) "../../B06_Section/B06_Section_Api_Fetch": { fetchSectionContext: async (projectId) => ({ route_id: projectId === "p-none" ? null : 304 }), }, "../../B05_Profile/B05_Profile_Api_Structures": { fetchStructures: async () => { calls.structures += 1; return { structures: [{ structure_id: "s1", chainage_m: 20 }], revision: calls.structures }; }, dropStaleStructureTypes: async () => { calls.version += 1; return false; }, }, }; const js = ts.transpileModule( fs.readFileSync(path.join(ROOT, "A00_Common", "design", "design_page_data.ts"), "utf8"), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 } }, ).outputText; const box = { exports: {} }; new Function("exports", "module", "require", js)(box.exports, box, (name) => { if (!STUBS[name]) throw new Error(`stub 없음: ${name}`); return STUBS[name]; }); const data = box.exports; const P = "p1"; const tick = () => new Promise((resolve) => setTimeout(resolve, 0)); (async () => { const out = {}; // 열 때 · 보기 전환 · 다시 보임 = 같은 저장분 — 서버는 한 번 const [a, b] = await Promise.all([data.pageStructures(P), data.pageStructures(P)]); await data.pageStructures(P); await data.pagePipes(P); await data.pagePipes(P); await data.pageTypesChecked(P); await data.pageTypesChecked(P); out.open = { ...calls }; // 사본 — 받는 쪽이 고쳐도 다음 받기는 그대로 a.structures.push({ structure_id: "x" }); out.copy = { same: a === b, after: (await data.pageStructures(P)).structures.length }; // 다른 프로젝트는 따로 await data.pageStructures("p2"); out.other = calls.structures; // 저장 정본이 바뀜 — 비우면 다음에 한 번 다시 data.forgetPageData(P); await data.pageStructures(P); await data.pageStructures(P); out.afterForget = { ...calls }; // 실패는 기억 안 함 — 다음에 다시 물음 data.forgetPageData(P); failPipes = true; out.failed = await data.pagePipes(P).then( () => "ok", () => "error", ); failPipes = false; await data.pagePipes(P); out.retry = calls.pipes; // 「계산 중」 — 떠 있는 차례가 끝날 때까지 켬(밀림 · 실패도 끔) const seen = []; const stop = data.onPageBusy(P, (busy) => seen.push(busy)); let finish; data.trackPageBusy(P, new Promise((resolve) => (finish = resolve))); data.trackPageBusy(P, Promise.reject(new Error("밀림"))); data.trackPageBusy(P, null); await tick(); out.midBusy = seen[seen.length - 1]; finish(); await tick(); stop(); data.trackPageBusy(P, Promise.resolve()); await tick(); out.busy = seen; // 노선 id 한 칸(386-2b) — context 가 채움 · 저장 정본 바뀜(비움)엔 그대로 · 페이지 떠남에 비움 const route = { before: data.pageRouteId(P) }; route.context = (await data.pageSectionContext(P)).route_id; route.after = data.pageRouteId(P); data.forgetPageData(P); route.keptOnSave = data.pageRouteId(P); data.forgetPageData(P, true); route.leaving = data.pageRouteId(P); await data.pageSectionContext("p-none"); route.none = data.pageRouteId("p-none"); out.route = route; process.stdout.write(JSON.stringify(out)); })().catch((error) => { console.error(error); process.exit(1); });