test(725-5): M02 낡은 시험 8 파일 고침 - 소스 글·시간 표기 변화를 새 글로 따라감 (코드 안 고침)
- test_m02_item_list: 헬퍼 가짜 모듈에 진짜 common_util_time 연결 · TZ=Asia/Seoul 고정 · 서버 수정일 = UTC Z 20자(593-1)
- test_m02_hopyo_screen: 도면 양식 칸이 조건 없이 놓임(720-4) - 찾는 글 drawing ? 를 drawing, 로
- test_m02_layer_screen: const drawing = opt.isSystem 를 조건 없는 section(L("M02_KindDrawing")) 글로
- test_m02_practice: 차례 확인 글 drawing ? 를 drawing, 로
- test_m02_structure_def_screen: 지난 판 저장일 = UTC Z 초 단위(593-1) - 길이 16 을 꼴 확인으로
- test_m02_structure_option_groups: 옵션 줄은 716-3 에서 좌측 칸을 떠남 - 있다는 단언을 없다는 단언으로
- test_m02_structure_options: 같은 까닭 - 옵션 줄 단언을 없음으로, 구조물 조합 → 도면 양식 → 실무문서 차례만 확인
- test_m02_df_convert: 옛 사본 폴더가 낡아 어긋남 - 정본과 다르면 다시 복사 · 전수 대조는 열 줄에 하나(약 1분, AISLO_DF_FULL=1 이면 전수 약 12분) · 일꾼 동시 Node 호출로 드문 실패는 혼자 한 번 더
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,8 +15,10 @@ const load = (name, stubs) => {
|
||||
return box.exports;
|
||||
};
|
||||
const model = load("M02_MasterTemplete_TreePanel_Model.ts", {});
|
||||
const time = load("../common_util/common_util_time.ts", {}); // 날짜 글 = 진짜(PC 시간대는 시험이 고정)
|
||||
const m = load("M02_MasterTemplete_ItemList.ts", {
|
||||
"./M02_MasterTemplete_TreePanel_Model": model,
|
||||
"../common_util/common_util_time": time,
|
||||
});
|
||||
|
||||
const row = (열, 경로, extra = {}) => ({
|
||||
|
||||
@@ -22,6 +22,9 @@ ROOT = Path(__file__).resolve().parents[2]
|
||||
COPY = ROOT / "tmp" / "master_copy_36"
|
||||
REPORT = ROOT / "tmp" / "convert_report_36.json"
|
||||
WORKERS = 6
|
||||
STEP = (
|
||||
1 if os.environ.get("AISLO_DF_FULL") else 10
|
||||
) # 지킴 = 열 줄에 하나(약 1분) · 전수(약 12분) = AISLO_DF_FULL=1
|
||||
|
||||
# 일꾼(따로 뜬 파이썬 · 사본 폴더) — 표준 입력 = 키 목록 JSON · 표준 출력 = 줄마다 결과 JSON
|
||||
WORKER = r"""
|
||||
@@ -85,11 +88,16 @@ for key in json.loads(sys.stdin.read()):
|
||||
|
||||
|
||||
def _copy() -> None:
|
||||
"""사본 폴더 — 없으면 정본 JSON 을 복사(정본은 읽기만)."""
|
||||
if not any(COPY.glob("*.json")):
|
||||
COPY.mkdir(parents=True, exist_ok=True)
|
||||
for f in (ROOT / "resources" / "master_data").glob("*.json"):
|
||||
shutil.copy2(f, COPY / f.name)
|
||||
"""사본 폴더 — 정본과 다르거나 없는 JSON 만 복사(정본은 읽기만 · 옛 사본이 남아 어긋나지 않게)."""
|
||||
COPY.mkdir(parents=True, exist_ok=True)
|
||||
for f in (ROOT / "resources" / "master_data").glob("*.json"):
|
||||
to = COPY / f.name
|
||||
if (
|
||||
not to.exists()
|
||||
or to.stat().st_mtime != f.stat().st_mtime
|
||||
or to.stat().st_size != f.stat().st_size
|
||||
):
|
||||
shutil.copy2(f, to)
|
||||
|
||||
|
||||
def _env() -> dict:
|
||||
@@ -237,13 +245,23 @@ def test_꼴_상자_선_규칙_배치() -> None:
|
||||
# ── 전수 대조 ────────────────────────────────────────────────────────
|
||||
def test_전수_대조_옛_셈_같음() -> None:
|
||||
_copy()
|
||||
keys = _keys()
|
||||
assert len(keys) >= 1400
|
||||
all_keys = _keys()
|
||||
assert len(all_keys) >= 1400
|
||||
keys = all_keys[::STEP]
|
||||
chunks = [keys[i::WORKERS] for i in range(WORKERS)]
|
||||
with ThreadPoolExecutor(WORKERS) as pool:
|
||||
outs = list(pool.map(lambda c: _run(WORKER, json.dumps(c)), chunks))
|
||||
rows = [json.loads(x) for out in outs for x in out.splitlines() if x.startswith("{")]
|
||||
assert len(rows) == len(keys)
|
||||
# 일꾼이 동시에 Node 계산 묶음을 부르다 드물게 한 번 실패 — 멈춘 줄만 혼자 한 번 더(혼자서도 멈추면 진짜 흠)
|
||||
again = [
|
||||
r["key"]
|
||||
for r in rows
|
||||
if "흐름 셈 멈춤" in (r["시험입력"]["state"], r["기본 입력"]["state"])
|
||||
]
|
||||
if again:
|
||||
redo = {r["key"]: r for r in map(json.loads, _run(WORKER, json.dumps(again)).splitlines())}
|
||||
rows = [redo.get(r["key"], r) for r in rows]
|
||||
report: dict = {"로직": len(rows)}
|
||||
for kind in ("시험입력", "기본 입력"):
|
||||
states = [r[kind]["state"] for r in rows]
|
||||
|
||||
@@ -341,7 +341,7 @@ def test_좌측_판_자리와_권한():
|
||||
# 716-3 차례 — 일위대가 › 구조물 조합 › 구분선 › 일위대가 불러오기 › 도면 양식
|
||||
order = ("precomboSec,", "summary,", "createDivider(),", "section(hopyo.title, hopyo.body),")
|
||||
assert [host.index(k) for k in order] == sorted(host.index(k) for k in order)
|
||||
assert host.index("section(hopyo.title, hopyo.body),") < host.index("drawing ?")
|
||||
assert host.index("section(hopyo.title, hopyo.body),") < host.index("drawing,")
|
||||
assert "canWrite: opt.canWrite," in side # 구조물 조합 층 쓰기 권한(720-4 함수)
|
||||
assert "hopyo.paint();" in side.split("function paint(): void {")[1]
|
||||
hopyo_side = _text("M02_MasterTemplete_Hopyo_Side.ts")
|
||||
|
||||
@@ -11,6 +11,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
@@ -30,6 +31,7 @@ def ts() -> dict:
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
env={**os.environ, "TZ": "Asia/Seoul"}, # 날짜 칸 = 보는 PC 시간대(593-1) — 한국으로 고정
|
||||
)
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
return json.loads(proc.stdout)
|
||||
@@ -95,7 +97,9 @@ def test_서버_목록_칸_수정일_labor(world):
|
||||
root = world["storage"] / "7" / "42" / "templates"
|
||||
ditch, wall = t.item(["배수", "측구"]), t.item(["옹벽"])
|
||||
assert set(ditch["수정일"]) >= {"structure", "area"}
|
||||
assert len(ditch["수정일"]["structure"]) == 19 # 초 단위 ISO
|
||||
assert re.fullmatch(
|
||||
r"\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ", ditch["수정일"]["structure"]
|
||||
) # 초 단위 UTC Z(593-1)
|
||||
assert (
|
||||
ditch["labor"]["로직수"] == 0 and ditch["labor"]["합계"] is None
|
||||
) # 새 열 = 빈 일위대가(39-1b)
|
||||
|
||||
@@ -227,7 +227,7 @@ def test_층_길_접두어와_고정_풀림():
|
||||
)
|
||||
side = _text(M02 / "M02_MasterTemplete_UI_Side.ts")
|
||||
assert 'const layer: Layer = "system";' in side # 도면 양식은 시스템 층에만
|
||||
assert "const drawing = opt.isSystem" in side # 720-4 — 시스템 관리자면 늘
|
||||
assert 'const drawing = section(L("M02_KindDrawing")' in side # 720-4 — 조건 없이 누구나 늘
|
||||
main = _text(M02 / "M02_MasterTemplete_UI_Main.ts")
|
||||
assert 'sel.layer !== "system"' not in main
|
||||
assert 'return layer === "project" || isAdmin;' in main
|
||||
|
||||
@@ -53,7 +53,7 @@ def test_화면_시스템_관리자_마스터만_물음_두_번_읽기만():
|
||||
side = (M02 / "M02_MasterTemplete_UI_Side.ts").read_text("utf-8")
|
||||
assert "const practice = opt.isSystem" in side # 720-4 — 시스템 관리자면 늘
|
||||
order = side.split('className: "m02-side__lists ui-sidebar-panel",')[1].split("});")[0]
|
||||
assert order.index("drawing ?") < order.index("practice ?") # 도면 양식 아래
|
||||
assert order.index("drawing,") < order.index("practice ?") # 도면 양식 아래
|
||||
view = (M02 / "M02_MasterTemplete_Practice.ts").read_text("utf-8")
|
||||
assert view.count("showConfirmDialog(") == 2
|
||||
assert "dropPractice(" in view and "createInputField" not in view # 고칠 칸 없음
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -99,7 +100,9 @@ def test_화면_고침을_그대로_받아_판_더하기_1_지난_판_길(copy)
|
||||
assert got["types"] == [retired(t) for t in sent["types"]]
|
||||
assert list(got["구조물군"]) == list(sent["구조물군"]) # 순서 그대로
|
||||
rows = admin.get(f"{URL}/history").json()
|
||||
assert [r["판"] for r in rows] == [doc["판"]] and len(rows[0]["저장일"]) == 16
|
||||
assert [r["판"] for r in rows] == [doc["판"]] and re.fullmatch(
|
||||
r"\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ", rows[0]["저장일"]
|
||||
)
|
||||
old = admin.get(f"{URL}/history/{doc['판']}").json()
|
||||
assert old == doc
|
||||
assert admin.get(f"{URL}/history/999").status_code == 404
|
||||
|
||||
@@ -105,7 +105,7 @@ def test_틀린_그룹은_422_낡은_판은_409(client) -> None:
|
||||
def test_좌측_정의_줄은_시스템_층에_늘_고쳐_씀은_시스템_관리자만() -> None:
|
||||
"""PLAN 48-2 — 다른 역할은 읽기만(화면이 칸을 잠금 · 서버 PUT 403)."""
|
||||
side = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts").read_text("utf-8")
|
||||
assert "옵션: section(options.title, options.body)," in side
|
||||
assert "section(options.title" not in side # 716-3 — 옵션 줄은 M01 로 옮겨 좌측 칸에서 뺌
|
||||
folder = ROOT / "M02_MasterTemplete/StructOptions"
|
||||
assert "buildGroupBlock(" in (folder / "M02_SO_Def.ts").read_text("utf-8")
|
||||
assert "buildPlaceSelect(" in (folder / "M02_SO_Def_Options.ts").read_text("utf-8")
|
||||
|
||||
@@ -109,20 +109,13 @@ def test_없는_종류_키_참거짓_아님은_422_낡은_판은_409(client) ->
|
||||
|
||||
def test_좌측_줄은_구조물_집계_양식_아래_한_줄_같은_한_곳에서_지움() -> None:
|
||||
side = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts").read_text("utf-8")
|
||||
assert "section(options.title, options.body)" in side
|
||||
# 차례 = 구조물 조합 → 옵션 → 도면 양식(시스템 층에만 · 해시 진입 `reveal` 이 이름으로 · 27-1d)
|
||||
assert side.index("section(options.title") < side.index('도면: section(L("M02_KindDrawing")')
|
||||
# 차례 = 구조물 조합(일위대가는 그 하위 · 39-2) → 옵션 → 도면 양식 → 실무문서
|
||||
# 716-3 — 옵션 줄은 M01 로 옮겨 좌측 칸에서 뺌(차례 = 일위대가 → 구조물 조합 → 불러오기 → 도면 양식 → 실무문서)
|
||||
assert "options.title" not in side and "options.clear" not in side
|
||||
order = side.split('className: "m02-side__lists ui-sidebar-panel",')[1].split("});")[0]
|
||||
assert (
|
||||
order.index("summary") < order.index("extras.옵션, extras.도면") < order.index("practice")
|
||||
)
|
||||
# 실무문서를 열면 이 줄 고름 지움 / 이 줄을 열면 실무문서 고름 지움(자재 조합 · 「전체 항목」 없앰)
|
||||
assert "onOpen: () => (options.clear(), paint())" in side
|
||||
assert order.index("summary") < order.index("drawing,") < order.index("practice")
|
||||
# 실무문서를 열면 이 줄 고름 지움(자재 조합 · 「전체 항목」 없앰)
|
||||
assert "onOpen: () => (practice?.clear(), paint())" in side
|
||||
assert "matset" not in side
|
||||
paint = side.split("function paint(): void {")[1].split("\n }\n")[0]
|
||||
assert "if (opt.getOpen()) options.clear();" in paint # 양식을 열면 이 줄 고름 지움
|
||||
# 화면은 정의 한 벌로 읽고 씀(PLAN 48-2) · 보이기 체크는 정의 화면 옵션 줄
|
||||
api = (ROOT / "M02_MasterTemplete/StructOptions/M02_SO_Api.ts").read_text("utf-8")
|
||||
assert 'call("/structure-definition")' in api
|
||||
|
||||
Reference in New Issue
Block a user