Files
Aislo/resources/tester/helper_443_save_stops.cjs
T
eomsangdonandClaude Opus 5.5 4b6800e387 feat(472-6 6-B 노선 바뀐 [저장] 한 본문): 노선 저장 · 관 · 구조물 다시 얹기 · 횡단 고침 넘김을 서버 한 창구로 — 전부 아니면 없음
- POST /design/save 에 route(노선 초안) · carry(옛 노선 세션 칸) 칸 · 서버 = 노선 저장 → Node facility.rebase(rebasePipeDraft · rebaseStructureDraft) → 6-A 차례(새 노선) → Node stations.carry(routeCarryPatches) → 횡단 저장
- 되돌림 범위 = 노선 행 · 자식 표 · 단계 · 배수 폴더 · 노선 파일 · 지표면 확정(설계 지형 바꿈)
- 손 상단측은 안 넘김(요구 274) · 노선 바뀐 [확정] = [저장](새 노선 본 뒤 다시)
- 브라우저 = 노선 초안 + 사용자 칸만 한 본문 · 옛 차례 길(saveRouteDraft 뒤 flush · carryAfterRouteSave · keepDraftsOnNewRoute) · 부름 0 된 flush 창구 여섯 · saveCachedCrossPatches 걷음
- 등록부 R9 stations.carry · F10 facility.rebase · 옛 차례 단언 시험 한 본문 꼴로 · test_472_6_route_save(옛 길 = 새 길 · 실패 되돌림)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:07:12 +09:00

107 lines
4.2 KiB
JavaScript

/* PLAN 443 단위검증 헬퍼 — 저장 누락 ① (H6 · M3 · M6).
* H6 구조물 저장 실패(409 · 그 밖) = 던짐 · 초안 남음 · 다음 [저장]이 줄에 안 막힘
* M3 상단측 차례 성공 = uphill 세션 비움 · 실패 = 그대로
* M6 확정 응답 failed_parts = 던짐(그 차례에서 멈춤)
* TS 를 그 자리에서 트랜스파일해 Node 로 돌림 · 서버 창구는 가짜. 짝 = `test_443_save_stops.py`. */
const fs = require("fs");
const path = require("path");
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
const ROOT = path.join(__dirname, "..", "..");
globalThis.window = { setTimeout, clearTimeout };
const anything = () =>
new Proxy({}, { get: (_target, key) => (key === "__esModule" ? false : () => undefined) });
function load(file, stubs) {
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const box = { exports: {} };
new Function("exports", "module", "require", js)(box.exports, box, (r) => stubs[r] ?? anything());
return box.exports;
}
const settle = (promise) =>
promise.then(
() => "ok",
(error) => String(error.message),
);
async function run() {
const out = {};
// ── H6 구조물 ──
class StructureConflictError extends Error {}
let pending = [{ structure_id: "s1" }];
let mode = "conflict";
const puts = [];
const bridgeMod = load(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Page_Structures.ts"), {
"./B05_Profile_Api_Structures": {
StructureConflictError,
fetchStructures: async () => ({ revision: 3, structures: [] }),
saveStructures: async (_p, revision, list) => {
puts.push(revision);
if (mode === "conflict") throw new StructureConflictError("409");
if (mode === "boom") throw new Error("서버 500");
return { revision: revision + 1 };
},
readPendingStructures: () => pending,
writePendingStructures: (_p, next) => (pending = next),
definitionStale: () => false,
staleDefinitionNotice: () => null,
},
"@ui/ui_template_elements": { showToast: () => undefined },
});
const quiet = new Proxy({}, { get: () => () => undefined });
const bridge = bridgeMod.createStructuresBridge({
projectId: "p1",
panel: () => ({ structures: quiet, values: () => ({}) }),
lane: quiet, // 시설 줄 한 벌(462)
isRestoring: () => false,
});
out.conflict = await settle(bridge.saveStructuresIfDirty());
out.pendingAfterConflict = pending;
mode = "boom";
out.boom = await settle(bridge.saveStructuresIfDirty()); // 앞 실패에 줄이 안 막힘
out.pendingAfterBoom = pending;
mode = "ok";
out.ok = await settle(bridge.saveStructuresIfDirty());
out.pendingAfterOk = pending;
out.puts = puts;
// ── M3 · M6 상단측 · 확정 하위 ──
const state = new Map();
let reply = {};
const bodies = [];
globalThis.fetch = async (_url, init) => {
bodies.push(JSON.parse(init.body));
return { ok: true, json: async () => reply };
};
const api = load(path.join(ROOT, "B05_Profile", "B05_Profile_Api_Fetch.ts"), {
"../A00_Common/b_page_state": {
readState: (name, p) => state.get(`${name}:${p}`) ?? null,
writeState: (name, value, p) => state.set(`${name}:${p}`, value),
clearState: (name, p) => state.delete(`${name}:${p}`),
},
"@config/config_frontend": { API_BASE_URL: "", API_TIMEOUT_MS: 1000 },
});
// 472-6 — 상단측 차례 = 한 본문 `uphill` 칸(본문은 세션 · 비우기는 서버가 다 저장한 뒤 조각 정리 ·
// 하위 차례 실패 = 서버가 그 차례에서 되돌림 · 옛 창구 `flushUphillOverrides` · `saveRouteParts` 걷음)
state.set("uphill:p1", { "40.000": "right" });
out.exported = ["flushUphillOverrides", "saveRouteParts", "routePartsBody"].filter(
(name) => typeof api[name] === "function",
);
out.uphillAfterBody = state.get("uphill:p1") ?? null;
out.bodies = bodies;
return out;
}
run().then(
(out) => process.stdout.write(JSON.stringify(out)),
(error) => {
process.stderr.write(String(error.stack ?? error));
process.exit(1);
},
);