- ExpectedDraw — plan-context 겹 읽음 · 그림(켬끔 상관없이 보기만) · 키트 snaps = 남 세트 끝 · 그리기 칸 벗어남 안내 한 줄 · 검증 창구 others - 시험 test_635_4 배선 줄 대조 더함 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
140 lines
5.9 KiB
Python
140 lines
5.9 KiB
Python
"""B05 흐린 다른 세트 겹 · 이음점 붙기(PLAN 635-4 · 요구 546 · 547).
|
|
|
|
TS `logic/B05_Profile_PlanContext.ts`
|
|
|
|
tsc 로 옮겨 Node 로 돌림.
|
|
- 흐릴 세트 = 내 세트 아님 · 연결 없음 = 빈 · 꺾은선 1 점 = 안 그림.
|
|
- 이음점 = 남 세트 끝 · 받을 때 이어진 이음점 = 내 받은 선 끝 1 m 안.
|
|
- 벗어남 = 이어졌던 이음점에서 지금 선 끝까지 1 m 밖만 안내 · 붙이면 안내 없음.
|
|
- 이름표 = 가장 긴 꺾은선 가운데.
|
|
- 지킴: 겹은 보기만(GET 만 · 파일 · 누름 칸 없음) · 기능 표 `others` = B05 보기만 ·
|
|
키트가 이음점을 긋기 · 끌기에 넘김 ·
|
|
B05 예정노선 수정이 겹을 그림 · 이음점 넘김 · 안내 줄.
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_635_4_plan_context_layer.py -q
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
LOGIC = ROOT / "B05_Profile" / "logic" / "B05_Profile_PlanContext.ts"
|
|
LAYER = ROOT / "B05_Profile" / "B05_Profile_UI_PlanContext.ts"
|
|
D03 = ROOT / "D03_PlanAnalysis"
|
|
TSC = ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
|
|
|
_RUNNER = """
|
|
import { writeFileSync } from "node:fs";
|
|
import * as c from "./B05_Profile/logic/B05_Profile_PlanContext.js";
|
|
const set = (key, mine, lines) => ({ key, year: +key.slice(0, 4), no: +key.slice(5), mine, parts: 1,
|
|
branch: false, length_m: 0, lines });
|
|
const ctx = { linked: true, mine: "2027-1", sets: [
|
|
set("2027-1", true, [[[0, 0], [100, 0]]]),
|
|
set("2028-1", false, [[[-50, 0], [0, 0.4]]]),
|
|
set("2027-2", false, [[[100, 0], [130, 0], [160, 30]]]),
|
|
set("2029-1", false, [[[500, 500], [600, 500]], [[700, 700]]]),
|
|
set("2029-2", false, [[[1, 1]]]),
|
|
] };
|
|
const out = {
|
|
others: c.otherSets(ctx).map((s) => s.key),
|
|
unlinked: c.otherSets({ linked: false }),
|
|
none: c.otherSets(null),
|
|
joints: c.jointPoints(ctx),
|
|
linked: c.linkedJoints(ctx),
|
|
gapsSame: c.jointGaps([[0, 0], [50, 0], [100, 0]], c.linkedJoints(ctx)),
|
|
gapsMoved: c.jointGaps([[0, 0], [50, 0], [90, 0]], c.linkedJoints(ctx)),
|
|
gapsEmpty: c.jointGaps([[0, 0]], c.linkedJoints(ctx)),
|
|
note: c.jointNote(c.jointGaps([[0, 0], [50, 0], [90, 0]], c.linkedJoints(ctx))),
|
|
noteNone: c.jointNote([]),
|
|
label: c.labelPoint(ctx.sets[2]),
|
|
labelLong: c.labelPoint(set("x-1", false, [[[0, 0], [10, 0]], [[0, 5], [0, 105]]])),
|
|
};
|
|
writeFileSync(process.argv[2], JSON.stringify(out));
|
|
"""
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def r(tmp_path_factory: pytest.TempPathFactory) -> dict:
|
|
tmp = tmp_path_factory.mktemp("ctx")
|
|
out = tmp / "js"
|
|
subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", str(TSC), str(LOGIC), "--outDir", str(out), "--rootDir", str(ROOT)]
|
|
+ ["--module", "esnext", "--target", "es2022", "--moduleResolution", "bundler"]
|
|
+ ["--ignoreConfig"],
|
|
cwd=str(ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
for emitted in out.rglob("*.js"):
|
|
emitted.write_text(
|
|
re.sub(r'(from\s+"\.\.?/[^"]+)"', r'\1.js"', emitted.read_text(encoding="utf-8")),
|
|
encoding="utf-8",
|
|
)
|
|
(out / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
|
|
result = tmp / "out.json"
|
|
subprocess.run( # noqa: S603
|
|
["node", str(out / "runner.mjs"), str(result)],
|
|
cwd=str(ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
return json.loads(result.read_text(encoding="utf-8"))
|
|
|
|
|
|
def test_흐릴_세트_내_것_빼고_연결_없으면_빈(r):
|
|
assert r["others"] == ["2028-1", "2027-2", "2029-1"]
|
|
assert r["unlinked"] == [] and r["none"] == []
|
|
|
|
|
|
def test_이음점_남_세트_끝_받을_때_이어진_것만(r):
|
|
keys = [(j["key"], j["point"]) for j in r["joints"]]
|
|
assert ("2028-1", [-50, 0]) in keys and ("2027-2", [160, 30]) in keys
|
|
assert ("2029-1", [600, 500]) in keys and len(keys) == 6 # 1 점 꺾은선 빼고
|
|
assert [(j["key"], j["point"]) for j in r["linked"]] == [
|
|
("2028-1", [0, 0.4]),
|
|
("2027-2", [100, 0]),
|
|
]
|
|
|
|
|
|
def test_벗어남_1m_밖만_안내(r):
|
|
assert r["gapsSame"] == [] and r["gapsEmpty"] == []
|
|
assert [(g["key"], round(g["gapM"], 3)) for g in r["gapsMoved"]] == [("2027-2", 10.0)]
|
|
assert "2027-2 10.0 m" in r["note"] and "고정이 아님" in r["note"]
|
|
assert r["noteNone"] is None
|
|
|
|
|
|
def test_이름표_가장_긴_선_가운데(r):
|
|
assert r["labelLong"] == [0, 55]
|
|
step = (30 + 30 * 2**0.5) / 2 - 30 # 반 길이 = 둘째 토막 안
|
|
assert r["label"] == pytest.approx([130 + step / 2**0.5, step / 2**0.5], abs=1e-6)
|
|
|
|
|
|
def test_겹은_보기만_파일_누름_없음():
|
|
src = LAYER.read_text("utf-8")
|
|
assert 'method: "GET"' in src and not re.search(r'method:\s*"(POST|PUT|DELETE|PATCH)"', src)
|
|
assert "sessionStorage" not in src and "press" not in src
|
|
assert not (ROOT / "B05_Profile" / "route").exists() or not any(
|
|
"PlanContext" in p.name for p in (ROOT / "B05_Profile" / "route").rglob("*")
|
|
)
|
|
logic = LOGIC.read_text("utf-8")
|
|
assert not re.search(r"\b(document|window|fetch|sessionStorage)\b", logic)
|
|
|
|
|
|
def test_기능_표_others_B05_보기만_키트_이음점_넘김():
|
|
features = (D03 / "D03_PlanAnalysis_UI_Features.ts").read_text("utf-8")
|
|
row = re.search(r'key: "others",[^}]*pages: \{ d03: "(\w+)", b05: "(\w+)" \}', features, re.S)
|
|
assert row and row.groups() == ("hide", "view")
|
|
kit = (D03 / "D03_PlanAnalysis_UI_Kit.ts").read_text("utf-8")
|
|
assert kit.count("snaps: params.snaps") == 2
|
|
assert 'note: "이음점에 붙음"' in (D03 / "D03_PlanAnalysis_UI_Draw.ts").read_text("utf-8")
|
|
assert "params.snaps?.()" in (D03 / "D03_PlanAnalysis_UI_Edit.ts").read_text("utf-8")
|
|
wire = (ROOT / "B05_Profile" / "B05_Profile_UI_ExpectedDraw.ts").read_text("utf-8")
|
|
assert "snaps: others.joints" in wire and "others.paint(context" in wire
|
|
assert "others.note(line())" in wire and "others.load()" in wire
|