Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1

This commit is contained in:
2026-10-20 09:51:15 +09:00
9 changed files with 209 additions and 168 deletions
+115 -72
View File
@@ -1,14 +1,18 @@
# -*- coding: utf-8 -*-
"""492-1 · 요구 283 · 284 · 285 — 노선 모양이 바뀐 [저장] · 추천 [적용] = 초기화 + 초기값 다시 뜸.
"""493-1 · 요구 289 · 290 — 손 고침 [저장] = 보이는 화면 그대로 · 추천 [적용] = 초기화 + 초기값
다시 뜸.
① 교점 끌기 [저장] — 판정(저장 · 새 계획 폴리라인) = 초기화 · 초기값 다시 뜸 · 체인 = 구조물 0 ·
추가 측점 0 · 관 = 새 교차만(stream · 옵션 없음 · 옆 0 m) · 계획고 옮김 없음 · 횡단 손 칸 0
② 추천 [적용] — 같은 초기화(균형 종단 갈래도)
③ 스냅 · 측점 간격만(선 그대로) — 초기화 아님 · 시설 따라감 · 초기값 그대로
④ 검증_sub8 자료(노선 522 → 628 · 관 p35100) — 관 = 새 교차 388 하나(옛 = 388 + 394 · 34.44 m 옆)
⑤ 한 본문 노선 초기화 — 보낸 관 · 구조물 · 추가 측점 · 계획고 · 관 옵션 · 넘김 칸 안 씀
⑥ 한 본문 실패 — 초기값 사본(`initial_snapshot`)까지 되돌림
옛 코드(492 전) = ① ② ④ ⑤ ⑥ 실패.
① 교점 끌기 [저장](손 고침) — 초기화 아님 · 초기값 그대로 · 체인 = 구조물 · 추가 측점 · 사용자 관
(옵션) 그대로 따라감 · 새 세류선 교차 관만 더함 · `route_reset` 칸 없음
② 추천 [적용] — 초기화(균형 종단 갈래도) · 사용자 구조물 · 추가 측점 · 관 버림 · `route_reset`
③ 스냅 · 측점 간격만 — 초기화 아님 · 초기값 그대로
④ 검증_sub8 자료(노선 522 → 628 · 관 p35100) — 손 고침 = 관 p35100(옵션) 지킴 + 교차 388 더함 ·
추천 [적용] = 교차 388 하나
⑤ 한 본문 손 고침 — 노선 차례 뒤 보낸 시설 · 넘김 차례 그대로 씀
⑥ 추천 [적용] 실패 — 초기값 사본(`initial_snapshot`)까지 되돌림
⑦ 떠 두기 중 뒤 미리 셈이 파일을 지움(`_derived` 원자 쓰기 · 정리) — 건너뜀(옛 = 500
FileNotFoundError)
492 코드 = ① ④(손 고침) ⑤ 실패 · 옛 떠 두기 = ⑦ 실패.
체인 · API 는 346-3 가짜 세계(`chain_world` · `api_world`)를 빌려 씀 — 파일은 임시 폴더만.
@@ -139,20 +143,7 @@ def _assert_reset(world, result: dict, cross_seen: list) -> None:
assert cross_seen == [[]] # 횡단 손 입력 안 넘김
# ── ① 교점 끌기 [저장] ─────────────────────────────────────────────────────────
def test_판정_선이_바뀌면_참_같으면_거짓():
from B05_Profile.B05_Profile_Engine_Grade_Carry import route_shape_changed
line = [(0.0, 0.0), (100.0, 0.0), (200.0, 0.0)]
assert route_shape_changed(line, list(line)) is False
assert (
route_shape_changed(line, [(0.0, 0.0), (100.0, 0.0005), (200.0, 0.0)]) is False
) # 1 mm 안
assert route_shape_changed(line, [(0.0, 0.0), (100.0, 20.0), (200.0, 0.0)]) is True
assert route_shape_changed(line, [(0.0, 0.0), (200.0, 0.0)]) is True # 교점 지움
assert route_shape_changed(line, [*line, (300.0, 0.0)]) is True # 교점 더함
# ── ① 교점 끌기 [저장](손 고침) ─────────────────────────────────────────────
def _replan_world(monkeypatch, old_line, new_line) -> list:
@@ -170,24 +161,32 @@ def _replan_world(monkeypatch, old_line, new_line) -> list:
return calls
def test_교점_끌기_저장은_초기화_초기값_다시(api_world, monkeypatch):
calls = _replan_world(monkeypatch, [[0.0, 0.0], [4.0, 4.0]], [[0.0, 0.0], [5.0, 5.0]])
result = asyncio.run(replan.replan_route(uuid4(), base._body()))
assert calls == [("recompute", True, False)] # 균형 종단은 추천 [적용]만
assert result["initial_refreshed"] is True and len(api_world) == 1
def test_교점_끌기_저장은_초기화_아님_초기값_그대로(api_world, monkeypatch):
calls = _replan_world(monkeypatch, [[0.0, 0.0], [4.0, 4.0]], [[0.0, 0.0], [25.0, 5.0]])
result = asyncio.run(replan.replan_route(uuid4(), base._body(balanced_profile=True)))
assert calls == [("recompute", False, False)] # 균형 종단 · 초기화는 추천 [적용]만
assert result["initial_refreshed"] is False and api_world == []
def test_교점_끌기_체인은_사용자_시설_버림(chain_world, crossings, cross_seen):
def test_교점_끌기_체인은_시설_따라감(chain_world, crossings, cross_seen):
_seed(chain_world)
result = base._run(chain_world, initial=True)
_assert_reset(chain_world, result, cross_seen)
result = base._run(chain_world)
assert "error" not in result, result
assert "route_reset" not in result
assert len(load_structures(str(chain_world.root))[1]) == 1
assert len(load_plus_stations(chain_world.root)) == 1
points = json.loads(chain_world.pipes_file.read_text("utf-8"))["points"]
assert len(points) == 2, points # 사용자 관 그대로 + 새 교차 관
assert any(p.get("options") == {"pipe_kind": "흄관"} for p in points)
assert result["pipes"]["crossing"] == 1
assert "profile" in chain_world.order # 계획고 옮김
assert cross_seen != [[]] # 횡단 손 입력 넘김
# ── ② 추천 [적용] ─────────────────────────────────────────────────────────────
def test_추천_적용도_같은_초기화(api_world, chain_world, crossings, cross_seen, monkeypatch):
def test_추천_적용은_초기화(api_world, chain_world, crossings, cross_seen, monkeypatch):
import B05_Profile.B05_Profile_Router_RouteInitial as route_initial
same = [[0.0, 0.0], [5.0, 5.0]]
@@ -202,6 +201,13 @@ def test_추천_적용도_같은_초기화(api_world, chain_world, crossings, cr
_assert_reset(chain_world, chained, cross_seen)
def test_추천_적용_체인은_사용자_시설_버림(chain_world, crossings, cross_seen):
_seed(chain_world)
result = base._run(chain_world, initial=True)
_assert_reset(chain_world, result, cross_seen)
assert result["pipes"]["crossing"] == 1
# ── ③ 스냅 · 간격만 ───────────────────────────────────────────────────────────
@@ -214,17 +220,6 @@ def test_스냅만_바꾼_저장은_초기화_아님(api_world, monkeypatch):
assert result["initial_refreshed"] is False and api_world == []
def test_스냅만_체인은_시설_따라감(chain_world, crossings, cross_seen):
_seed(chain_world)
result = base._run(chain_world)
assert result["route_reset"]["reset"] is False
assert result["route_reset"]["dropped"] == {"structures": 0, "pipes": 0, "plus_stations": 0}
assert len(load_structures(str(chain_world.root))[1]) == 1
assert load_plus_stations(chain_world.root)
points = json.loads(chain_world.pipes_file.read_text("utf-8"))["points"]
assert any(p.get("options") == {"pipe_kind": "흄관"} for p in points) # 사용자 관 그대로
# ── ④ 검증_sub8 자료 ─────────────────────────────────────────────────────────
@@ -238,23 +233,38 @@ def sub8(monkeypatch):
return data
def test_검증_sub8_노선_522_628_관은_교차_하나(sub8, chain_world, cross_seen):
chain_world.pipes_file.write_text(
json.dumps({"route_signature": "old", "points": [sub8["old_pipe"]]}), "utf-8"
def _sub8_pipes(world, data) -> None:
world.pipes_file.write_text(
json.dumps({"route_signature": "old", "points": [data["old_pipe"]]}), "utf-8"
)
def test_검증_sub8_손_고침은_관_지킴_교차_더함(sub8, chain_world, cross_seen):
_sub8_pipes(chain_world, sub8)
result = base._run(chain_world)
assert "error" not in result, result
points = json.loads(chain_world.pipes_file.read_text("utf-8"))["points"]
assert len(points) == 2, points
kept = [p for p in points if p.get("id") == "p35100"]
assert kept and kept[0]["options"] == sub8["old_pipe"]["options"] # 화면 그대로
assert any(round(p["chainage_m"]) == 388 for p in points) # 새 교차
def test_검증_sub8_추천_적용은_교차_하나(sub8, chain_world, cross_seen):
_sub8_pipes(chain_world, sub8)
result = base._run(chain_world, initial=True)
assert "error" not in result, result
points = json.loads(chain_world.pipes_file.read_text("utf-8"))["points"]
assert [round(p["chainage_m"]) for p in points] == [388] # 옛 = [388, 394]
assert _side(sub8["new_xy"], points[0]["x"], points[0]["y"]) < 0.01 # 옛 394 = 34.44 m
assert [round(p["chainage_m"]) for p in points] == [388]
assert _side(sub8["new_xy"], points[0]["x"], points[0]["y"]) < 0.01
assert not points[0].get("options")
assert result["route_reset"]["dropped"]["pipes"] == 1
# ── ⑤ · ⑥ 한 본문 ──────────────────────────────────────────────────────────────
# ── ⑤ 한 본문 손 고침 ─────────────────────────────────────────────────────────
def test_한_본문_초기화는_보낸_칸을_안_씀(monkeypatch):
def test_한_본문_손_고침은_보낸_칸을_씀(monkeypatch):
import B04_PreProcess.B04_PreProcess_Router_Basins as basins
import B05_Profile.B05_Profile_Router as b05_router
import B05_Profile.B05_Profile_Router_Lifecycle as lifecycle
@@ -273,14 +283,7 @@ def test_한_본문_초기화는_보낸_칸을_안_씀(monkeypatch):
return fake
routed = {
"route_id": 9,
"route_reset": {
"reset": True,
"dropped": {"structures": 1, "pipes": 2, "plus_stations": 1},
},
}
monkeypatch.setattr(replan, "replan_route", record("route", routed))
monkeypatch.setattr(replan, "replan_route", record("route", {"route_id": 9}))
monkeypatch.setattr(moving, "saved_facilities", record("before"))
monkeypatch.setattr(moving, "rebase_drafts", record("rebase"))
monkeypatch.setattr(moving, "carry_cross", record("carry", {"patches": [], "outside": 0}))
@@ -299,39 +302,79 @@ def test_한_본문_초기화는_보낸_칸을_안_씀(monkeypatch):
structures=SimpleNamespace(),
plus_stations=SimpleNamespace(),
alignment=SimpleNamespace(model_copy=lambda update: SimpleNamespace(**update)),
culvert_options={"50.00": {"pipe_kind": "흄관"}},
culvert_options=None,
uphill=None,
cross=None,
route=SimpleNamespace(),
carry=SimpleNamespace(),
)
out = asyncio.run(design_save._run_steps(uuid4(), request, None))
assert called == ["before", "route", "settings"], called
assert out["route"]["route_reset"]["reset"] is True
assert called == [
"before",
"route",
"rebase",
"settings",
"pipes",
"structures",
"plus",
"alignment",
"carry",
"cross",
], called
assert "route_reset" not in out["route"]
def test_한_본문_실패면_초기값_사본까지_되돌림(tmp_path, monkeypatch):
# ── ⑥ 추천 [적용] 실패 ─────────────────────────────────────────────────────────
def test_추천_적용_실패면_초기값_사본까지_되돌림(tmp_path, monkeypatch):
import B06_Section.B06_Section_Router_DesignSave as design_save
import common_util.common_util_save_guard as guard
from B06_Section.B06_Section_Router_DesignSave_Route import StepFailed
from fastapi.responses import JSONResponse
copy = tmp_path / "initial_snapshot" / "B04_PreProcess__drainage" / "edits"
copy.mkdir(parents=True)
(copy / "pipe_points.json").write_text("old", encoding="utf-8")
async def steps(project_id, request, session):
async def fail(project_id, body):
(copy / "pipe_points.json").write_text("new", encoding="utf-8") # 초기값 다시 뜸
raise StepFailed("route", "뒤 차례 실패")
return JSONResponse(status_code=409, content={"status": "error", "message": "x"})
monkeypatch.setattr(replan, "_project_paths", _async((tmp_path, "p")))
monkeypatch.setattr(replan, "_save_route", fail)
monkeypatch.setattr(replan, "get_db_pool", lambda: base._Pool([]))
monkeypatch.setattr(design_save, "get_db_pool", lambda: base._Pool([]))
monkeypatch.setattr(design_save, "get_project_storage_relative_path", _async("storage/x"))
monkeypatch.setattr(design_save, "resolve_stored_project_path", lambda stored: str(tmp_path))
monkeypatch.setattr(guard, "snapshot_rows", _async({}))
monkeypatch.setattr(guard, "restore_rows", _async(None))
monkeypatch.setattr(design_save, "_run_steps", steps)
request = design_save.DesignSaveRequest.model_construct(
route_id=7, confirm=False, pipes=None, culvert_options=None, route=SimpleNamespace()
)
response = asyncio.run(design_save.save_design(uuid4(), request, None))
response = asyncio.run(replan.replan_route(uuid4(), base._body(as_initial=True)))
assert response.status_code == 409
assert (copy / "pipe_points.json").read_text(encoding="utf-8") == "old"
# ── ⑦ 떠 두기 중 파일 사라짐 ───────────────────────────────────────────────────
def test_떠_두기_중_지워진_파일은_건너뜀(tmp_path, monkeypatch):
import common_util.common_util_save_guard as guard
derived = tmp_path / "_derived" / "route_initial"
derived.mkdir(parents=True)
(derived / "a.json").write_text("a", encoding="utf-8")
ghost = derived / "b.json.tmp" # 목록 뒤 · 사본 전에 원자 쓰기가 옮김 · 정리가 지움
real_files = guard._files
def listed(root, scope):
return {**real_files(root, scope), "_derived/route_initial/b.json.tmp": ghost}
monkeypatch.setattr(guard, "_files", listed)
snap = guard.snapshot_storage(tmp_path, ("_derived",))
monkeypatch.setattr(guard, "_files", real_files)
try:
assert set(snap.stamps) == {"_derived/route_initial/a.json"}
(derived / "a.json").write_text("changed", encoding="utf-8")
(derived / "c.json").write_text("new", encoding="utf-8")
snap.restore()
assert sorted(p.name for p in derived.iterdir()) == ["a.json"]
assert (derived / "a.json").read_text(encoding="utf-8") == "a"
finally:
snap.discard()
+4 -8
View File
@@ -2,8 +2,6 @@
"""346-3 노선 저장 서버 길 — 차례 · 관 지킴 · 새 교차점 기본 관 · 보충 없음(369) · 이월 열쇠 ·
초기값 규칙 ·
배수유역 낡음 · 실패 때 옛 노선 되살림.
관 지킴 · 따라감 = 초기화 아닌 저장(스냅 · 간격만) 몫 — 모양 바뀐 저장 · 추천 [적용] = 초기화(492 ·
`test_283_route_change_reset`).
순수 부품(`B05_Profile_Route_Save_Parts`)은 값으로 · 체인(`run_route_save_chain`)과 API
(`replan_route`)는 DB · 엔진을 가짜로 바꿔 차례 · 되살림 · 초기값만 잰다.
@@ -482,8 +480,7 @@ def _body(**kwargs):
return save.RouteSaveRequest(vertices=[{"x": 0, "y": 0}, {"x": 5, "y": 5}], **kwargs)
def test_모양_그대로_노선_저장은_초기값_그대로(api_world):
# 492 — 저장 선 = 새 선(스냅 · 간격만) · 모양 바뀐 저장 = test_283_route_change_reset
def test_손으로_고친_노선_저장은_초기값_그대로(api_world):
result = asyncio.run(replan.replan_route(uuid4(), _body()))
assert result["initial_refreshed"] is False and api_world == []
@@ -493,13 +490,13 @@ def test_추천_적용은_초기값_다시(api_world):
assert result["initial_refreshed"] is True and len(api_world) == 1
def test_균형_종단은_추천_적용만_손_저장은_초기화(api_world, monkeypatch):
def test_균형_종단은_추천_적용만_손_저장은_계획고_옮김(api_world, monkeypatch):
calls: list = []
async def recompute(
project_id, root, stored, balanced=None, terrain=None, lines=None, initial=False
): # 476 — [적용] = initial 칸 더함
calls.append((balanced, lines, initial))
calls.append((balanced, lines))
return {"route_id": 9, "total_length_m": 10.0}
monkeypatch.setattr(replan, "_recompute", recompute)
@@ -507,8 +504,7 @@ def test_균형_종단은_추천_적용만_손_저장은_초기화(api_world, mo
monkeypatch.setattr(replan, "_vertices_of", lambda path: [[0.0, 0.0], [5.0, 5.0]])
asyncio.run(replan.replan_route(uuid4(), _body(balanced_profile=True))) # 손으로 고친 노선
asyncio.run(replan.replan_route(uuid4(), _body(balanced_profile=True, as_initial=True)))
# 손으로 모양 바꾼 노선 = 초기화(492) · 균형 종단 없음(1차 계획선)
assert calls[0] == (None, ([[0.0, 0.0], [4.0, 4.0]], [[0.0, 0.0], [5.0, 5.0]]), True)
assert calls[0] == (None, ([[0.0, 0.0], [4.0, 4.0]], [[0.0, 0.0], [5.0, 5.0]]))
assert calls[1][0] is not None # 추천 [적용] = 균형 종단 그대로
+31 -18
View File
@@ -6,10 +6,9 @@
② 다시 얹기 — 관 · 구조물 초안(`rebasePipeDraft` · `rebaseStructureDraft`) ==
Node `facility.rebase`.
③ 옛 코드 실패 — 한 본문이 `route` · `carry` 칸을 받음 · 브라우저가 노선 저장 창구를 안 부름.
④ 실제 창구(검증_sub8 DB · storage 는 임시 사본 · 492) — 노선 차례 뒤 실패(시험 갈래 · 넘김 실패)
= 노선 행 · 자식 표 · storage(초기값 사본 포함) 그대로 · 정상 한 본문(스냅만 바꾼 노선 =
따라감) = 옛 차례(노선 저장 → 다시 얹기 → 횡단 저장)와 행 · 파일 같음 · 모양 바뀐 노선 =
초기화(노선 차례만 · `route_reset`).
④ 실제 창구(검증_sub8 DB · storage 는 임시 사본) — 노선 차례 뒤 실패(시험 갈래 · 넘김 실패)
= 노선 행 · 자식 표 · storage(초기값 사본 포함) 그대로 · 정상 한 본문(교점 끌기) = 옛 차례
(노선 저장 → 다시 얹기 → 횡단 저장)와 행 · 파일 같음 · 시설 수 그대로 · 초기값 그대로(493).
검증 프로젝트가 없는 자리(다른 PC · DB 없음)는 ④ 건너뜀.
./venv/Scripts/python.exe -m pytest resources/tester/test_472_6_route_save.py -q
@@ -233,8 +232,8 @@ def _state(root: Path) -> tuple[dict, dict]:
def _plan_draft(shape: bool = True) -> dict:
"""지금 계획노선 노드 · 넣은 편집값 그대로 + `shape` 면 가운데 노드 하나를 3 m 옆으로(노선 끌기
= 초기화) · 아니면 측점 스냅만 뒤집음(모양 그대로 = 따라감 · 492)."""
"""지금 계획노선 노드 · 넣은 편집값 그대로 + `shape` 면 가운데 노드 하나를 3 m 옆으로(노선
끌기) · 아니면 측점 스냅만 뒤집음(모양 그대로)."""
from B05_Profile.B05_Profile_Router_Replan import read_route_plan
from config.config_db import close_db_pool, init_db_pool
@@ -320,7 +319,7 @@ def test_넘김_실패면_노선까지_그대로(monkeypatch) -> None:
return run_calc(ids, inputs)
monkeypatch.setattr(moving, "run_calc", broken)
status, body = _save(_body(_plan_draft(shape=False))) # 넘김 = 모양 그대로 노선만
status, body = _save(_body(_plan_draft()))
assert status == 409 and body["step"] == "cross" and body["restored"] is True, body
assert "넘김 일부러 실패" in body["message"]
assert _state(ROOT_DIR) == before
@@ -353,7 +352,7 @@ def test_정상_한_본문은_옛_차례와_같음() -> None:
from config.config_db import close_db_pool, get_db_pool, init_db_pool
start = _state(ROOT_DIR)
draft = _plan_draft(shape=False)
draft = _plan_draft()
old_ids = {int(row["id"]) for row in _rows()["routes"]}
async def old_way():
@@ -417,11 +416,23 @@ def test_정상_한_본문은_옛_차례와_같음() -> None:
@live
def test_모양_바뀐_노선은_초기화_노선_차례만() -> None:
"""492 — 교점 3 m 끌기 = 서버 초기화 · 보낸 넘김 칸 안 씀 · 초기값 = 작업본 관 · 끝에 되돌림."""
def test_모양_바뀐_노선은_시설_따라감_초기값_그대로() -> None:
"""493 · 요구 289 · 290 — 교점 3 m 끌기 [저장] = 보이는 화면 그대로(구조물 · 관 · 추가 측점 수
같음 · 관 id 그대로) · 초기화 · 초기값 다시 뜸 없음 · 끝에 되돌림."""
from B05_Profile.B05_Profile_Plus_Stations import load_plus_stations
from B05_Profile.B05_Profile_Structures_Repository import load_structures
from config.config_db import close_db_pool, get_db_pool, init_db_pool
start = _state(ROOT_DIR)
pipes = ROOT_DIR / "B04_PreProcess" / "drainage" / "edits" / "pipe_points.json"
def facilities() -> tuple[int, set, int]:
points = json.loads(pipes.read_text("utf-8"))["points"] if pipes.is_file() else []
return (
len(load_structures(str(ROOT_DIR))[1]),
{p.get("id") for p in points},
len(load_plus_stations(ROOT_DIR)),
)
async def hold():
await init_db_pool()
@@ -435,16 +446,18 @@ def test_모양_바뀐_노선은_초기화_노선_차례만() -> None:
files = guard.snapshot_storage(
ROOT_DIR, guard.STORAGE_SCOPE + guard.PIPE_SCOPE + guard.INITIAL_SCOPE
)
structures, pipe_ids, plus = facilities()
initial = {k: v for k, v in start[1].items() if k.startswith("initial_snapshot/")}
try:
status, body = _save(_body(_plan_draft()))
assert status == 200 and set(body["steps"]) == {"route"}, body
reset = body["steps"]["route"]["route_reset"]
assert reset["reset"] is True and body["steps"]["route"]["initial_refreshed"] is True
pipes = ROOT_DIR / "B04_PreProcess" / "drainage" / "edits" / "pipe_points.json"
kept = json.loads(pipes.read_text("utf-8"))["points"]
assert all(not p.get("options") and p.get("source") == "stream" for p in kept)
copy = ROOT_DIR / "initial_snapshot" / "B04_PreProcess__drainage" / "edits"
assert (copy / "pipe_points.json").read_bytes() == pipes.read_bytes() # 초기값 = 작업본
assert status == 200 and {"route", "carry", "cross"} <= set(body["steps"]), body
route = body["steps"]["route"]
assert "route_reset" not in route and route["initial_refreshed"] is False
after_structures, after_ids, after_plus = facilities()
assert (after_structures, after_plus) == (structures, plus)
assert pipe_ids <= after_ids # 사용자 · 옛 관 그대로 · 새 교차 관만 더함
now = {k: v for k, v in _state(ROOT_DIR)[1].items() if k.startswith("initial_snapshot/")}
assert now == initial # 초기값 그대로
finally:
async def undo():
+2 -3
View File
@@ -76,8 +76,7 @@ def test_적용_같은_노선_끔_새_1차_계획선(chain_world, monkeypatch):
assert not (alignment.get("edits") or {}).get("station_offsets")
def test_초기화_아닌_저장은_계획고_옮김_그대로(chain_world, monkeypatch):
# 492 — 체인 initial=False = 노선 모양 그대로(스냅 · 간격만) 저장 몫
def test_손_노선_저장은_계획고_옮김_그대로(chain_world, monkeypatch):
path, fresh = _real_profile_world(chain_world, monkeypatch)
_run(chain_world)
alignment = json.loads(path.read_text("utf-8"))["profile_alignment"]
@@ -105,7 +104,7 @@ def test_적용은_체인에_initial(api_world, monkeypatch):
def test_적용은_횡단_손_입력_버림(chain_world, monkeypatch):
# 요구 277 — 추천 [적용] · 모양 바뀐 [저장](492) = 초기화(횡단 손 입력 전부 버림) · 스냅 · 간격만은 넘김
# 요구 277 — 추천 [적용] = 새 노선 기준 초기화(횡단 손 입력 전부 버림) · 손 노선 [저장]은 넘김
import B06_Section.B06_Section_Repository as b06_repo
seen: list = []