feat(B05): 우클릭 세월교 · 물넘이포장도 바로 추가 · 빈 월류 높이는 재계산 유량이 오면 그 항목에 채움(126-1)
- 우클릭 바로 추가에서 세월교 · 물넘이포장을 폼만 갈래에서 뺌 · 추가 뒤 그 측점 관 고름 + 알림 한 줄 - 배수유역 패널 — 바로 놓은 빈 높이 측점을 기다렸다가 응답 유량으로 필요 수심 채움(`fordHeightM` · 서버 `ford_height_m` 짝) · 손 값은 안 덮음 · 유량 전엔 빈칸 · 채우면 세션 초안에 다시 담음 - 세부유역 요청은 최신 응답만 채택(requestSeq) - 시험 test_126_ford_right_click(헬퍼 helper_126_ford_fill) · test_116 기대값 고침 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UoSr2sqvYtBf1dtQdus88s
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
/* =============================================================================
|
||||
* B05_Profile_UI_Drainage_Ford_Fill.ts
|
||||
* 우클릭으로 바로 추가한 세월교 · 물넘이포장의 빈 월류 높이 = 배수유역 재계산이 준 설계유량의
|
||||
* 필요 수심(PLAN 126-1 · 폼 쪽 65-4 와 같은 풀이 `fordHeightM`).
|
||||
*
|
||||
* 기다리는 측점만 채움 · 사용자가 넣은 높이(응답 옵션에 있음)는 안 덮음 · 유량이 아직 없으면 다음
|
||||
* 응답까지 기다림. 응답 옵션을 고쳐 두면 보관소(`replaceFromResponse`)가 그 값을 정본으로 받음.
|
||||
* ========================================================================== */
|
||||
|
||||
import {
|
||||
FORD_BRIDGE_DEFAULT_WIDTH_M,
|
||||
FORD_PAVEMENT_DEFAULT_WIDTH_M,
|
||||
} from "@config/config_frontend";
|
||||
import { fordHeightM } from "../A00_Common/design/design_structure_ford_calc";
|
||||
|
||||
const FORD_KINDS: Record<string, number> = {
|
||||
ford_bridge: FORD_BRIDGE_DEFAULT_WIDTH_M,
|
||||
ford_pavement: FORD_PAVEMENT_DEFAULT_WIDTH_M,
|
||||
};
|
||||
|
||||
/** 새로 놓으면 높이를 기다리는 종류 — 높이 칸이 빈 세월교 · 물넘이포장. */
|
||||
export function waitsFordHeight(
|
||||
facility: string | undefined,
|
||||
options?: Record<string, string | number>,
|
||||
): boolean {
|
||||
return !!facility && facility in FORD_KINDS && options?.ford_height_m === undefined;
|
||||
}
|
||||
|
||||
/** 기다리는 측점의 빈 높이를 채움 — 채운(또는 사용자 값이라 그만 기다릴) 측점은 `pending` 에서 뺌.
|
||||
* 채운 것이 있으면 true(세션 초안에 다시 담을 것). */
|
||||
export function fillFordHeights(
|
||||
points: Array<{
|
||||
chainage_m: number;
|
||||
facility?: string;
|
||||
options?: Record<string, string | number>;
|
||||
}>,
|
||||
basins: ReadonlyArray<{ chainage_m: number; design_flow_m3s?: number | null }>,
|
||||
pending: Set<number>,
|
||||
): boolean {
|
||||
let filled = false;
|
||||
for (const wait of [...pending]) {
|
||||
const point = points.find((entry) => Math.abs(entry.chainage_m - wait) < 0.51);
|
||||
if (!point?.facility || !(point.facility in FORD_KINDS)) {
|
||||
pending.delete(wait); // 관이 사라졌거나 종류가 바뀜
|
||||
continue;
|
||||
}
|
||||
if (point.options?.ford_height_m !== undefined && point.options.ford_height_m !== "") {
|
||||
pending.delete(wait); // 사용자가 넣은 값 — 안 덮음
|
||||
continue;
|
||||
}
|
||||
const flow = basins.find((entry) => Math.abs(entry.chainage_m - point.chainage_m) < 0.51);
|
||||
const width = Number(point.options?.ford_width_m) || FORD_KINDS[point.facility];
|
||||
const need = fordHeightM(flow?.design_flow_m3s ?? null, width);
|
||||
if (need === null) continue; // 유량 아직 없음 — 다음 응답까지 빈칸
|
||||
point.options = { ...point.options, ford_height_m: need };
|
||||
pending.delete(wait);
|
||||
filled = true;
|
||||
}
|
||||
return filled;
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import { createFacilityStore } from "./B05_Profile_UI_Drainage_Facility";
|
||||
import { mergeFacilityOptions } from "./B05_Profile_UI_Drainage_Facility_Merge";
|
||||
import { readPendingPipes, writePendingPipes } from "./B05_Profile_Api_Pipes_Draft";
|
||||
import { buildPipeReports } from "./B05_Profile_UI_Drainage_Report";
|
||||
import { fillFordHeights, waitsFordHeight } from "./B05_Profile_UI_Drainage_Ford_Fill";
|
||||
import { createDrainageChrome } from "./B05_Profile_UI_Drainage_Chrome";
|
||||
import { bindDrainageInteractions } from "./B05_Profile_UI_Drainage_Interact";
|
||||
import type { RouteSpanBand } from "./B05_Profile_UI_Drainage_Spans";
|
||||
@@ -172,6 +173,9 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
let offsetY = 0;
|
||||
let frameHandle = 0;
|
||||
let loadSequence = 0;
|
||||
let requestSeq = 0; // 세부유역 요청 — 최신 응답만 채택
|
||||
/** 우클릭으로 바로 놓은 세월교 · 물넘이포장 — 재계산 유량이 오면 빈 월류 높이를 채울 측점(126-1). */
|
||||
const fordWaits = new Set<number>();
|
||||
let canvasWidth = 0;
|
||||
let canvasHeight = 0;
|
||||
let canvasDpr = 0;
|
||||
@@ -346,8 +350,10 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
reason: pipe.source,
|
||||
})),
|
||||
);
|
||||
// 시설 확장 정보(종류·구간·옵션)는 응답이 정본 — 통째로 다시 채운다.
|
||||
// 시설 확장 정보(종류·구간·옵션)는 응답이 정본 — 통째로 다시 채운다(기다리던 월류 높이를 먼저 얹음).
|
||||
const fordFilled = fillFordHeights(response.pipe_points, response.basins, fordWaits);
|
||||
facilityStore.replaceFromResponse(response.pipe_points);
|
||||
if (fordFilled) rememberPipes();
|
||||
// 재계산 뒤에는 선택을 다시 알려야 한다 — 관을 새로 넣은 직후에는 목록이 아직
|
||||
// 비어 폼이 열리지 못했고, 중복 억제 가드에 걸려 두 번째 기회를 잃는다.
|
||||
lastNotifiedPipeChainage = null;
|
||||
@@ -367,18 +373,24 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
status.hidden = false;
|
||||
status.textContent = L("B05_Drainage_Status_Analyzing");
|
||||
showProgress(null, L("B05_Drainage_Status_Analyzing"));
|
||||
const seq = ++requestSeq;
|
||||
try {
|
||||
apply(await request());
|
||||
const response = await request();
|
||||
if (seq !== requestSeq) return; // 뒤에 보낸 요청이 있음 — 그 응답을 기다림
|
||||
apply(response);
|
||||
status.hidden = basins.length > 0;
|
||||
if (basins.length === 0) status.textContent = L("B05_Drainage_Status_NoBasin");
|
||||
scheduleDraw();
|
||||
} catch (error) {
|
||||
if (seq !== requestSeq) return;
|
||||
status.hidden = false;
|
||||
status.textContent =
|
||||
error instanceof Error ? error.message : L("B05_Drainage_Status_AnalyzeFailed");
|
||||
} finally {
|
||||
analyzeButton.disabled = false;
|
||||
showProgress(null, null);
|
||||
if (seq === requestSeq) {
|
||||
analyzeButton.disabled = false;
|
||||
showProgress(null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,6 +603,7 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
addPipe(chainageM, attributes) {
|
||||
// 시설 정보를 먼저 보관해야 addAtChainage가 촉발하는 재계산 요청에 실려 간다.
|
||||
facilityStore.set(chainageM, attributes ?? null);
|
||||
if (waitsFordHeight(attributes?.facility, attributes?.options)) fordWaits.add(chainageM);
|
||||
pipeEditor.addAtChainage(chainageM);
|
||||
},
|
||||
updatePipeFacility(fromChainageM, toChainageM, attributes) {
|
||||
@@ -598,6 +611,7 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
const previous = facilityStore.get(fromChainageM);
|
||||
facilityStore.set(fromChainageM, null);
|
||||
facilityStore.set(toChainageM, mergeFacilityOptions(previous, attributes));
|
||||
if (fordWaits.delete(fromChainageM)) fordWaits.add(toChainageM);
|
||||
if (Math.abs(fromChainageM - toChainageM) > 0.005) {
|
||||
// 기준점이 옮겨졌다 — 관을 이동시키면 onCommit이 재계산을 돌리고,
|
||||
// attach가 새 위치로 시설 정보를 승계한다.
|
||||
|
||||
@@ -2,14 +2,19 @@
|
||||
* B05_Profile_UI_Structures_Panel_AddAt.ts
|
||||
* 종단 우클릭 → 종류 고름 = 「[추가]까지 끝난 상태」(PLAN 116-1) — 세 길(B05 종단 · B06 종단 ·
|
||||
* 3D)이 함께 쓰는 패널 `addAt` 의 끝맺음. 필수 빈칸이 없는 종류는 그 측점에 바로 추가하고
|
||||
* 새 항목을 고른 채로 둠 · 남는 종류는 폼만.
|
||||
* 새 항목을 고른 채로 둠 · 남는 종류는 폼만. 세월교 · 물넘이포장도 바로 추가 — 빈 월류 높이는
|
||||
* 배수유역 재계산 유량이 오면 그 항목에 채움(126-1 · `_Drainage_Ford_Fill`).
|
||||
* ========================================================================== */
|
||||
|
||||
import type { StructureInstance, StructureType } from "./B05_Profile_Api_Structures";
|
||||
import type { StructuresSection } from "./B05_Profile_UI_Structures_Panel_Types";
|
||||
import { showToast } from "@ui/ui_template_elements";
|
||||
|
||||
/** 바로 추가하지 않는 종류 — 노면공(겹침 확인창) · 세월교 · 물넘이포장(월류 높이가 유량 뒤 채워짐). */
|
||||
const FORM_ONLY = new Set(["pavement_concrete", "ford_bridge", "ford_pavement"]);
|
||||
/** 바로 추가하지 않는 종류 — 노면공(겹침 확인창). */
|
||||
const FORM_ONLY = new Set(["pavement_concrete"]);
|
||||
/** 월류 높이를 유량 뒤에 받는 종류 — 추가 때 알림 한 줄. */
|
||||
const FORD_KINDS = new Set(["ford_bridge", "ford_pavement"]);
|
||||
export const FORD_WAIT_NOTICE = "월류 높이는 배수유역 재계산(설계유량)이 오면 채워집니다.";
|
||||
|
||||
/** 우클릭 한 번으로 [추가]까지 끝낼 수 있는지 — [추가]가 막는 빈 필수 칸이 없어야 함. */
|
||||
export function addsAtOnce(
|
||||
@@ -35,6 +40,7 @@ export async function finishAdd(ctx: {
|
||||
// B05 는 재계산 뒤 배수유역 패널이 새 관 고름을 다시 알려 폼이 열림.
|
||||
await ctx.commit();
|
||||
ctx.pick.selectPipeByChainage(ctx.anchorM);
|
||||
if (FORD_KINDS.has(ctx.type.type_id)) showToast(FORD_WAIT_NOTICE);
|
||||
return true;
|
||||
}
|
||||
const before = new Set(ctx.structures.map((entry) => entry.structure_id));
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/* 126-1 우클릭 세월교 · 물넘이포장 바로 추가 헬퍼 — `_Structures_Panel_AddAt`(판정 · 끝맺음 · 알림)과
|
||||
* `_Drainage_Ford_Fill`(유량 응답 뒤 빈 월류 높이 채움)을 Node 로 돌려 표준출력 JSON 한 벌로.
|
||||
* TS 로더는 `helper_116_right_click_add.cjs` 와 같다 · 설정값은 진짜 `config_frontend` 를 읽음.
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
||||
|
||||
const ROOT = path.join(__dirname, "..", "..");
|
||||
const cache = new Map();
|
||||
const toasts = [];
|
||||
const STUBS = { "@ui/ui_template_elements": { showToast: (message) => toasts.push(message) } };
|
||||
|
||||
function loadTs(file) {
|
||||
const full = path.resolve(file);
|
||||
if (cache.has(full)) return cache.get(full);
|
||||
const js = ts.transpileModule(fs.readFileSync(full, "utf8"), {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
||||
}).outputText;
|
||||
const box = { exports: {} };
|
||||
cache.set(full, box.exports);
|
||||
const localRequire = (request) => {
|
||||
if (STUBS[request]) return STUBS[request];
|
||||
if (request === "@config/config_frontend")
|
||||
return loadTs(path.join(ROOT, "config", "config_frontend.ts"));
|
||||
if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`));
|
||||
if (request.startsWith("@")) return new Proxy({}, { get: () => () => {} });
|
||||
return require(request);
|
||||
};
|
||||
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
||||
cache.set(full, box.exports);
|
||||
return box.exports;
|
||||
}
|
||||
|
||||
const B05 = path.join(ROOT, "B05_Profile");
|
||||
const { addsAtOnce, finishAdd, FORD_WAIT_NOTICE } = loadTs(
|
||||
path.join(B05, "B05_Profile_UI_Structures_Panel_AddAt.ts"),
|
||||
);
|
||||
const { fillFordHeights, waitsFordHeight } = loadTs(
|
||||
path.join(B05, "B05_Profile_UI_Drainage_Ford_Fill.ts"),
|
||||
);
|
||||
const { fordHeightM } = loadTs(
|
||||
path.join(ROOT, "A00_Common", "design", "design_structure_ford_calc.ts"),
|
||||
);
|
||||
|
||||
const type = (type_id) => ({ type_id, group: "A", managed_by: "pipe_points" });
|
||||
|
||||
/** 우클릭 → 바로 추가 — 관 지점 정본에 한 번 넘기고 그 측점 관을 고름 · 알림 한 줄. */
|
||||
async function rightClick(typeId) {
|
||||
toasts.length = 0;
|
||||
const picked = [];
|
||||
let commits = 0;
|
||||
const done = await finishAdd({
|
||||
type: type(typeId),
|
||||
anchorM: 120,
|
||||
structures: [],
|
||||
commit: async () => void commits++,
|
||||
pick: {
|
||||
selectById: (id) => picked.push(["id", id]),
|
||||
selectPipeByChainage: (at) => picked.push(["pipe", at]),
|
||||
},
|
||||
});
|
||||
return { done, commits, picked, toasts: [...toasts] };
|
||||
}
|
||||
|
||||
/** 재계산 응답 한 번 — 응답 관 목록에 기다리던 높이를 얹은 결과. */
|
||||
function respond(points, basins, pending) {
|
||||
const filled = fillFordHeights(points, basins, pending);
|
||||
return {
|
||||
filled,
|
||||
heights: points.map((p) => p.options?.ford_height_m ?? null),
|
||||
pending: [...pending],
|
||||
};
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const flow = 8.0;
|
||||
// 첫 응답은 유량 없음 → 빈칸 · 둘째 응답에 유량 → 그 항목에 채움(폭 = 항목 값)
|
||||
const pendingA = new Set([120]);
|
||||
const pointsA = () => [
|
||||
{ chainage_m: 120.2, facility: "ford_bridge", options: { ford_width_m: 10 } },
|
||||
];
|
||||
const noFlow = respond(pointsA(), [{ chainage_m: 120.2, design_flow_m3s: null }], pendingA);
|
||||
const withFlow = respond(pointsA(), [{ chainage_m: 120.2, design_flow_m3s: flow }], pendingA);
|
||||
// 손 값은 안 덮음
|
||||
const pendingB = new Set([300]);
|
||||
const hand = respond(
|
||||
[
|
||||
{
|
||||
chainage_m: 300,
|
||||
facility: "ford_pavement",
|
||||
options: { ford_width_m: 5, ford_height_m: 0.5 },
|
||||
},
|
||||
],
|
||||
[{ chainage_m: 300, design_flow_m3s: flow }],
|
||||
pendingB,
|
||||
);
|
||||
// 기다리지 않는 측점(옛 세월교)의 빈 높이는 그대로 · 폭 없으면 기본 폭
|
||||
const pendingC = new Set([50]);
|
||||
const other = respond(
|
||||
[
|
||||
{ chainage_m: 50, facility: "ford_pavement", options: {} },
|
||||
{ chainage_m: 80, facility: "ford_bridge", options: { ford_width_m: 10 } },
|
||||
],
|
||||
[
|
||||
{ chainage_m: 50, design_flow_m3s: flow },
|
||||
{ chainage_m: 80, design_flow_m3s: flow },
|
||||
],
|
||||
pendingC,
|
||||
);
|
||||
// 종류가 바뀌었거나 관이 사라짐 → 그만 기다림
|
||||
const pendingD = new Set([10, 20]);
|
||||
const gone = respond([{ chainage_m: 10, facility: "pipe", options: {} }], [], pendingD);
|
||||
|
||||
const out = {
|
||||
once: {
|
||||
fordBridge: addsAtOnce(type("ford_bridge"), []),
|
||||
fordPavement: addsAtOnce(type("ford_pavement"), []),
|
||||
pavement: addsAtOnce({ type_id: "pavement_concrete", managed_by: null }, []),
|
||||
},
|
||||
bridge: await rightClick("ford_bridge"),
|
||||
paveRc: await rightClick("ford_pavement"),
|
||||
pipe: await rightClick("pipe"),
|
||||
notice: FORD_WAIT_NOTICE,
|
||||
waits: {
|
||||
bridgeEmpty: waitsFordHeight("ford_bridge", { ford_width_m: 10 }),
|
||||
paveEmpty: waitsFordHeight("ford_pavement", undefined),
|
||||
bridgeHand: waitsFordHeight("ford_bridge", { ford_height_m: 0.4 }),
|
||||
pipe: waitsFordHeight("pipe", {}),
|
||||
},
|
||||
noFlow,
|
||||
withFlow,
|
||||
hand,
|
||||
other,
|
||||
gone,
|
||||
need: { w10: fordHeightM(flow, 10), w5: fordHeightM(flow, 5) },
|
||||
};
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
})();
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
못박는 것
|
||||
- 필수 빈칸 없는 종류 = 그 측점에 바로 추가(캐시) + 새 항목을 고른 채(구조물 = 임시 id · A군 = 측점).
|
||||
- 필수 빈칸 남는 종류 · 노면공(확인창) · 세월교 · 물넘이포장(월류 높이 = 유량 뒤) = 폼만.
|
||||
- 필수 빈칸 남는 종류 · 노면공(확인창) = 폼만 · 세월교 · 물넘이포장은 바로 추가(126-1 · test_126).
|
||||
- [추가]가 거절하면 아무것도 안 듦 · 고르지 않음.
|
||||
- 새로 놓기에서 시작 — 옛 집계 항목 · 그룹 잇기 · 메모가 새 항목에 안 실림.
|
||||
- 세 길(B05 종단 · B06 종단 · 3D)이 모두 패널 `addAt` 한 곳 · 패널을 열고 「구조물 배치」를 폄.
|
||||
@@ -47,10 +47,10 @@ def test_필수_빈칸_없는_종류만_바로_추가(out):
|
||||
assert once["requiredEmpty"] is False
|
||||
|
||||
|
||||
def test_노면공_세월교_물넘이포장은_폼만(out):
|
||||
def test_노면공만_폼만_세월교_물넘이포장은_바로(out):
|
||||
once = out["once"]
|
||||
assert once["pavement"] is False
|
||||
assert once["fordBridge"] is False and once["fordPavement"] is False
|
||||
assert once["fordBridge"] is True and once["fordPavement"] is True # 126-1
|
||||
|
||||
|
||||
def test_구조물은_목록에_들고_새_임시_id로_고른_채(out):
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""126-1 우클릭 세월교 · 물넘이포장도 바로 추가 · 월류 높이 실시간.
|
||||
|
||||
못박는 것
|
||||
- 세월교 · 물넘이포장 = 바로 추가 + 그 측점 관을 고른 채 · 알림 한 줄(높이는 유량 뒤).
|
||||
- 배수유역 재계산 응답에 유량이 오면 기다리던 항목의 빈 월류 높이 = 필요 수심(서버 `ford_height_m` 거울).
|
||||
- 유량이 아직 없으면 빈칸으로 다음 응답까지 · 손 값은 안 덮음 · 기다리지 않는 옛 항목은 그대로.
|
||||
- 세부유역 요청은 최신 응답만 채택 · 채운 뒤 세션 초안에 다시 담음.
|
||||
|
||||
Node 헬퍼 `helper_126_ford_fill.cjs` 가 판정 · 끝맺음 · 채움을 돌림. 화면은 사용자 확인.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
from B04_PreProcess import B04_PreProcess_Router_Basins as basins
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = os.path.abspath(os.path.join(HERE, "..", ".."))
|
||||
|
||||
|
||||
def _read(*parts: str) -> str:
|
||||
with open(os.path.join(ROOT, *parts), encoding="utf-8") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def out():
|
||||
proc = subprocess.run(
|
||||
["node", os.path.join(HERE, "helper_126_ford_fill.cjs")],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
)
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
return json.loads(proc.stdout)
|
||||
|
||||
|
||||
def test_우클릭_세월교_물넘이포장은_바로_추가(out):
|
||||
assert out["once"] == {"fordBridge": True, "fordPavement": True, "pavement": False}
|
||||
|
||||
|
||||
def test_추가_뒤_목록에_들고_그_관을_고른_채_알림_한_줄(out):
|
||||
for key in ("bridge", "paveRc"):
|
||||
r = out[key]
|
||||
assert r["done"] is True and r["commits"] == 1
|
||||
assert r["picked"] == [["pipe", 120]]
|
||||
assert r["toasts"] == [out["notice"]]
|
||||
assert out["pipe"]["toasts"] == [] # 배관은 알림 없음
|
||||
|
||||
|
||||
def test_빈_높이만_기다림(out):
|
||||
assert out["waits"] == {
|
||||
"bridgeEmpty": True,
|
||||
"paveEmpty": True,
|
||||
"bridgeHand": False,
|
||||
"pipe": False,
|
||||
}
|
||||
|
||||
|
||||
def test_유량_오기_전엔_빈칸_오면_그_항목에_채움(out):
|
||||
assert out["noFlow"] == {"filled": False, "heights": [None], "pending": [120]}
|
||||
assert out["withFlow"] == {"filled": True, "heights": [0.16], "pending": []}
|
||||
|
||||
|
||||
def test_채운_높이는_서버_풀이와_같음(out):
|
||||
assert out["need"]["w10"] == basins.ford_height_m(8.0, 10) == 0.16
|
||||
assert out["need"]["w5"] == basins.ford_height_m(8.0, 5)
|
||||
|
||||
|
||||
def test_손_값은_안_덮음(out):
|
||||
assert out["hand"] == {"filled": False, "heights": [0.5], "pending": []}
|
||||
|
||||
|
||||
def test_기다리지_않는_옛_항목은_그대로_폭_없으면_기본_폭(out):
|
||||
assert out["other"]["heights"] == [0.16, None] # 물넘이 기본 폭 10 · 옛 세월교 빈칸 그대로
|
||||
assert out["other"]["pending"] == []
|
||||
|
||||
|
||||
def test_관이_사라지거나_종류가_바뀌면_그만_기다림(out):
|
||||
assert out["gone"] == {"filled": False, "heights": [None], "pending": []}
|
||||
|
||||
|
||||
def test_배수유역_패널_배선():
|
||||
src = _read("B05_Profile", "B05_Profile_UI_Drainage_Panel.ts")
|
||||
# 최신 응답만 채택
|
||||
run = src[src.index("async function run(") : src.index("/** 지금 관 목록")]
|
||||
assert "const seq = ++requestSeq;" in run
|
||||
assert run.index("await request()") < run.index("if (seq !== requestSeq) return;")
|
||||
assert run.index("if (seq !== requestSeq) return;") < run.index("apply(response)")
|
||||
# 채움은 보관소가 응답을 받기 전에 · 채웠으면 세션 초안에 다시 담음
|
||||
apply = src[src.index("function apply(") : src.index("async function run(")]
|
||||
order = [
|
||||
apply.index("fillFordHeights(response.pipe_points, response.basins, fordWaits)"),
|
||||
apply.index("facilityStore.replaceFromResponse(response.pipe_points)"),
|
||||
apply.index("if (fordFilled) rememberPipes()"),
|
||||
apply.index("callbacks.onPipesChanged?.("),
|
||||
]
|
||||
assert order == sorted(order)
|
||||
add = src[src.index(" addPipe(chainageM, attributes) {") : src.index(" removePipe(")]
|
||||
assert "waitsFordHeight(attributes?.facility, attributes?.options)" in add
|
||||
assert add.index("fordWaits.add(chainageM)") < add.index("pipeEditor.addAtChainage(chainageM)")
|
||||
assert "if (fordWaits.delete(fromChainageM)) fordWaits.add(toChainageM);" in add
|
||||
Reference in New Issue
Block a user