Files
Aislo/resources/tester/test_208_1_logic_alias.py
T

186 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""PLAN 208-1 — 단가산출 로직 「별칭」 칸 · 로직 찾기는 이름 + 별칭.
① 서버 찾기 맞춤(`M01_MasterData_Store_Find.hit`) ↔ 화면 공용 맞춤(`ui_template_logic_find.ts`) 거울
② 읽기(없으면 빈칸) · 저장 왕복 — 임시 사본 폴더만(정본 마스터 파일은 안 바뀜)
③ M01 목록 「별칭」 열(이름 옆) · 쓰는 곳마다 공용 맞춤을 부름
"""
from __future__ import annotations
import json
import shutil
import subprocess
from pathlib import Path
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from M01_MasterData import M01_MasterData_Router as router_module
from M01_MasterData import M01_MasterData_Store as store
from M01_MasterData.M01_MasterData_Store_Find import hit
ROOT = Path(__file__).resolve().parents[2]
REAL = store.FOLDER
LOGIC_KEY = "GC000091" # 공통 3-3-1 암발파(미진동굴착 TYPE-Ⅰ)
LOGIC_FILE = "로직_건설품셈_03장_토공사.json"
ROW = {
"키": "GF000001",
"원문번호": "9-1-1",
"이름": "흙깎기(보통토사)",
"별칭": "절토, 터파기 기계",
}
# (찾는 글, 맞음, 별칭으로 맞은 것)
CASES = [
("흙깎기", True, ""),
("흙 깎기", True, ""), # 띄어쓰기 무시 · 낱말마다
("절토", True, "절토"),
("터파기", True, "터파기 기계"),
("터파기기계", True, "터파기 기계"), # 별칭 안 띄어쓰기 무시
("절토 기계", True, "절토, 터파기 기계"), # 낱말이 여러 별칭에 나뉘어 듦
("흙깎기 절토", True, "절토"), # 이름 + 별칭
("gf000001", True, ""),
("9-1", True, ""),
("성토", False, ""),
("", True, ""),
]
def _node(src: str) -> str:
if shutil.which("node") is None:
pytest.skip("node 가 없음")
done = subprocess.run(
["node", "--experimental-strip-types", "--input-type=module", "-e", src],
capture_output=True,
text=True,
encoding="utf-8",
timeout=60,
cwd=ROOT,
)
assert done.returncode == 0, done.stderr
return done.stdout
def test_서버_맞춤_이름_별칭_여러_별칭() -> None:
for q, ok, _ in CASES:
assert hit(ROW, q) is ok, q
assert hit({**ROW, "별칭": ""}, "절토") is False # 별칭 없음 = 빈칸
assert hit({k: v for k, v in ROW.items() if k != "별칭"}, "흙깎기") is True
def test_화면_맞춤_거울_서버와_같음() -> None:
url = (ROOT / "ui_template" / "ui_template_logic_find.ts").as_uri()
src = f"""
import {{ matchLogic, aliasText, aliasesOf, aliasNote }} from "{url}";
const row = {json.dumps(ROW, ensure_ascii=False)};
const cases = {json.dumps([c[0] for c in CASES], ensure_ascii=False)};
console.log(JSON.stringify({{
hits: cases.map((q) => matchLogic(q, row)),
text: aliasText(" 절토 ,, 터파기 기계,"),
none: aliasesOf(undefined),
note: [aliasNote("절토"), aliasNote("")],
chapter: matchLogic("토공", {{ ...row, 상세구분: "09장 토공" }}).ok,
}}));
"""
got = json.loads(_node(src))
for (q, ok, alias), one in zip(CASES, got["hits"]):
assert one == {"ok": ok, "alias": alias}, q
assert one["ok"] is hit(ROW, q), q # 서버와 같은 판정
assert got["text"] == "절토, 터파기 기계"
assert got["none"] == []
assert got["note"] == ["별칭 절토", ""]
assert got["chapter"] is True # 구분 · 상세구분은 화면만 더 봄
@pytest.fixture
def client(tmp_path: Path, monkeypatch) -> TestClient:
for path in REAL.glob("*.json"):
if not path.name.startswith("_") or path.name == store.mk.BOOK.name:
shutil.copy(path, tmp_path / path.name)
monkeypatch.setattr(store, "FOLDER", tmp_path)
app = FastAPI()
app.include_router(router_module.router)
return TestClient(app)
def _logics(client: TestClient, **params) -> list[dict]:
res = client.get("/api/m01/logics", params=params)
assert res.status_code == 200, res.text
return res.json()["logics"]
def _save(client: TestClient, row: dict) -> None:
one = client.get("/api/m01/logic", params={"key": LOGIC_KEY}).json()
part = {
"file": one["file"],
"version": one["version"],
"changes": [{"op": "edit", "key": LOGIC_KEY, "row": row}],
}
res = client.post("/api/m01/save", json={"files": [part]})
assert res.status_code == 200, res.text
def test_읽기_빈칸_저장_왕복_임시_사본만(client: TestClient) -> None:
real = (REAL / LOGIC_FILE).read_bytes()
listed = _logics(client)
assert listed and all(isinstance(x["별칭"], str) for x in listed) # 없으면 빈칸
one = client.get("/api/m01/logic", params={"key": LOGIC_KEY}).json()
base = one["logic"]
assert "별칭" not in base # 마스터 파일에 일괄로 쓰지 않음
_save(client, {**base, "별칭": "미진동 발파, 암깨기"})
got = [x for x in _logics(client, q="암깨기") if x["키"] == LOGIC_KEY]
assert got and got[0]["별칭"] == "미진동 발파, 암깨기"
assert LOGIC_KEY in [x["키"] for x in _logics(client, q="미진동 발파")]
found = client.get("/api/m01/elements", params={"group": "로직", "q": "암깨기"}).json()
assert [x["별칭"] for x in found["items"] if x["ref"] == LOGIC_KEY] == ["미진동 발파, 암깨기"]
saved = json.loads((store.FOLDER / LOGIC_FILE).read_text(encoding="utf-8"))
assert next(r for r in saved["줄"] if r["키"] == LOGIC_KEY)["별칭"] == "미진동 발파, 암깨기"
_save(client, base) # 지우면 칸째 빠짐 · 찾기에도 안 걸림
assert LOGIC_KEY not in [x["키"] for x in _logics(client, q="암깨기")]
after = json.loads((store.FOLDER / LOGIC_FILE).read_text(encoding="utf-8"))
assert after == json.loads(real.decode("utf-8")) # 왕복 뒤 내용 그대로
assert (REAL / LOGIC_FILE).read_bytes() == real # 정본은 안 건드림
def _src(rel: str) -> str:
return (ROOT / rel).read_text(encoding="utf-8")
def test_M01_목록_별칭_열은_이름_옆_고치면_캐시() -> None:
ts = _src("M01_MasterData/M01_MasterData_UI_Logic_List.ts")
cols = ts[ts.index("const COLS") : ts.index("as const")]
names = [x.strip().strip('"') for x in cols[cols.index("[") + 1 :].split(",")]
assert names[names.index("이름") + 1] == "별칭" # 이름 바로 옆
assert "aliasCell(item" in ts and "onAlias(item, next)" in ts
assert "e.stopPropagation()" in ts # 별칭 칸을 눌러도 로직이 안 열림
lab = _src("M01_MasterData/M01_MasterData_UI_LogicLab.ts")
assert "const onAlias = async" in lab and "list.setMarks(marks, extra, aliases)" in lab
# 자동저장 없음 — 캐시(drafts)에만 · [저장] 이 보냄
body = lab[lab.index("const onAlias = async") : lab.index("const onDelete = async")]
assert "saveFiles" not in body and "persist()" in body
@pytest.mark.parametrize(
"rel",
[
"M01_MasterData/M01_MasterData_UI_Logic_List.ts", # M01 로직 목록
"M01_MasterData/M01_MasterData_UI_Logic_Pick.ts", # M01 요소 찾기 창 · 로직 그룹
"M02_MasterTemplete/DataFlow/M02_DF_InputPanel_Build.ts", # M02 흐름도 다른 로직 고르기
"M02_MasterTemplete/DataFlow/M02_DF_InputPanel_Logic.ts", # 〃 결과 별칭 표시
"M02_MasterTemplete/M02_MasterTemplete_Labor_Pick.ts", # M02 · B09 [로직 더하기]
],
)
def test_쓰는_곳마다_공용_맞춤을_부름(rel: str) -> None:
ts = _src(rel)
assert 'from "@ui/ui_template_logic_find"' in ts
assert "matchLogic(" in ts
def test_서버_찾기도_한_벌() -> None:
py = _src("M01_MasterData/M01_MasterData_Store.py")
assert "import hit as _hit" in py and "def _hit" not in py