Files
Aislo/resources/tester/test_597_point_edit.py
T

112 lines
5.0 KiB
Python

"""597-2 예정노선 지점 칸 손 고침 계산 — 이름 규칙 · 보이는 이름 · 경위도 읽기 · 더함 · 이름 바꿈 · 경위도로 옮김
(요구 463 7 · 8) — TS `logic/D03_PlanAnalysis_PointEdit.ts`.
./venv/Scripts/python.exe -m pytest resources/tester/test_597_point_edit.py -q
"""
from __future__ import annotations
import json
import re
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
TS_FILE = ROOT / "D03_PlanAnalysis" / "logic" / "D03_PlanAnalysis_PointEdit.ts"
TSC = ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
_RUNNER = """
import { writeFileSync } from "node:fs";
import * as plan from "./D03_PlanAnalysis/logic/D03_PlanAnalysis_Plan.js";
import * as pe from "./D03_PlanAnalysis/logic/D03_PlanAnalysis_PointEdit.js";
import * as tmx from "./D03_PlanAnalysis/logic/D03_PlanAnalysis_Tm.js";
const Y = 2026;
const tm = tmx.tmParams(5187);
// 지점 (100,0) 에서 시작하는 선 · 지점 둘
let n = plan.newLine(plan.emptyNetwork(), [100, 0]);
n = plan.normalizePieces(plan.appendVertex(n, 0, [200, 0]), Y);
const state = { points: [[100, 0], [500, 500]], names: ["", "가"], network: n };
const added = pe.addPoint(state, [7, 8]);
const full = { points: Array.from({ length: 40 }, (_, i) => [i, 0]), names: [], network: n };
const [lon, lat] = tmx.tmToLonLat(tm, 100, 0);
const moved = pe.movePointTo(state, 0, tm, lon + 0.001, lat);
const back = tmx.tmToLonLat(tm, ...moved.points[0]);
writeFileSync(process.argv[2], JSON.stringify({
errors: ["", "가나다", "a/b", "a" + String.fromCharCode(92) + "b", "a:b", "a*b", "a?b", 'a"b', "a<b", "a>b", "a|b", "x".repeat(20), "x".repeat(21)]
.map((s) => pe.pointNameError(s)),
labels: [pe.pointLabel(["", " ", "가"], 0), pe.pointLabel(["", " ", "가"], 1), pe.pointLabel(["", " ", "가"], 2), pe.pointLabel([], 3)],
parse: [pe.parseLonLat("128.5", "36.9"), pe.parseLonLat("", "36.9"), pe.parseLonLat("128.5", "abc"), pe.parseLonLat("181", "0"), pe.parseLonLat("0", "-91")],
added: [added.points, added.names, added.network === n],
full: pe.addPoint(full, [1, 1]) === full,
renamed: [pe.renamePoint(state, 1, " 나 ").names, pe.renamePoint({ ...state, names: [] }, 1, "다").names],
moved: { points: moved.points, lines: moved.network.lines.map((l) => l.vertices), back, expect: [lon + 0.001, lat], untouched: moved.points[1], names: moved.names },
}));
"""
@pytest.fixture(scope="module")
def got(tmp_path_factory: pytest.TempPathFactory) -> dict:
tmp_path = tmp_path_factory.mktemp("pe")
out = tmp_path / "js"
subprocess.run( # noqa: S603 — 고정 실행 파일
["node", str(TSC), str(TS_FILE), "--outDir", str(out), "--rootDir", str(ROOT),
"--module", "esnext", "--target", "es2022", "--moduleResolution", "bundler",
"--ignoreConfig"],
cwd=str(ROOT), check=True, capture_output=True,
) # fmt: skip
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_path / "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_name_rule(got: dict) -> None:
ok, bad = got["errors"][:2], got["errors"][2:11]
assert ok == [None, None] # 빈 이름 · 한글 = 됨
assert all(msg and "못 씁니다" in msg for msg in bad) # 파일 이름 못 쓰는 글자 아홉
assert got["errors"][11] is None # 20 자
assert "20 자" in got["errors"][12] # 21 자 = 막음
def test_label_blank_falls_back(got: dict) -> None:
assert got["labels"] == ["지점 1", "지점 2", "가", "지점 4"]
def test_parse_lonlat(got: dict) -> None:
ok, no_lon, bad_lat, big_lon, big_lat = got["parse"]
assert ok == [128.5, 36.9]
assert no_lon.startswith("경도") and bad_lat.startswith("위도")
assert big_lon.startswith("경도") and big_lat.startswith("위도")
def test_add_and_limit(got: dict) -> None:
points, names, network_same = got["added"]
assert points == [[100, 0], [500, 500], [7, 8]] and names == ["", "가", ""]
assert network_same # 더해도 노선 그대로
assert got["full"] # 40 개 = 그대로
def test_rename(got: dict) -> None:
assert got["renamed"][0] == ["", "나"] # 앞뒤 빈칸 뺌
assert got["renamed"][1] == ["", "다"] # 이름 목록이 짧아도 차례 맞춤
def test_move_by_lonlat_drags_node(got: dict) -> None:
moved = got["moved"]
assert moved["back"] == pytest.approx(moved["expect"], abs=1e-7) # 경위도 왕복
assert moved["lines"][0][0] == moved["points"][0] # 그 자리 노선 노드도 같이
assert moved["lines"][0][1] == [200, 0] # 다른 노드는 그대로
assert moved["untouched"] == [500, 500] and moved["names"] == ["", "가"]