요구 651 · 653 — 계산 공식 참고용 흐름도(① M01 로직 상세 오른쪽 접는 패널)를 걷음. ② M02 일위대가 다이어그램은 그대로.
지움(64 파일 · 18,666 줄 · 되돌릴 길 = git 이력)
- M01 6 파일 1,064 줄 — Store_Flow.py · UI_LogicLab_Flow.ts/.css · FlowRows.ts · FlowView.ts/.css
- M02_MasterTemplete/DataFlow 43 파일 12,799 줄 — 흐름도 화면 · 셈(Compute · Convert · Lookup · Model · Eval …)
- 시험 15 파일 4,803 줄 — test_m01_logic_flow{view,_engine,_project,_save} · test_m02_df_*(값 찾기 values_old 뺌) — 지운 흐름 코드만 지키던 것
남김(바닥) — DataFlow M02_DF_UnitPick(일위대가 셋) · M02_DF_Values(M02_Item_Prices) · M02_DF_Round · _standard 표 ·
M02_DF_Router = GET /api/m02/df/standards 하나 · M02_DF_Api = cfStandards 하나 · CfRound 꼴은 Round 안으로
고침
- M01 Router — /save = Store_Make.save(개정 확인 · 자체 파일 새 줄 GX · added 그대로) · /calc /text /logic/auto = 옛 셈만 · /logic/flow 둘 지움
- master_formula — 흐름 훅 지움 · 줄에 「흐름」 이 남으면 「흐름도 로직은 걷음」 까닭 오류(저장된 흐름 로직 0 — 마스터 · storage 다시 확인)
- Sangeun_Calc._defaults = 옛 자동값 · M02_Logic_Lib_Read /logic/flow 지움 · main.py 주석
- 화면 — LogicLab(startFlow · scheduleFlow · 예시값 잇기 지움 · [신규 로직] 줄 = 입력 · 중간 · 호표 빈 목록 + 끝수 null) ·
Side(원문 패널 하나) · Source(머리 칸 이름 m01lab-src__top/__title · DF_Panel.css 안 씀) · Detail · View(nowInputs 지움) · Sheet(흐름 갈래 지움) · 글
- 시험 — 지운 파일을 가리키던 목록 넷(spreadsheet_size · 208_1 · 588 · input_prefs) · 717_1 흐름 길 두 줄 · values_old 흐름 셈 부분 지움
- test_713_3_list 기대 — 로직 1,461 → 1,515 · 묶음 307 → 335 · 작업량 21 → 65 · 부르는 506 → 512(782-1 · 2 · 3 건설 기초 로직 54 더함 ·
옛 키만 세면 307 · 21 · 506 그대로)
- 새 지킴 test_794_1b_flow_gone — 흐름 길 404 · 표준 표 남음 · 신규 로직 자체 GX · 흐름 남은 줄 까닭 오류
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
93 lines
4.4 KiB
Python
93 lines
4.4 KiB
Python
"""717-1 상세 부품 공용 — 길 주입(TS) · M02 `logic-lib` 읽기 · 셈 길(로그인 · 쓰기 없음).
|
|
|
|
TS = `helper_717_1_logic_api_paths.cjs`(가짜 fetch 주소) · 서버 = 정본 읽기만(파일 지문 같음).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from fastapi import Depends, FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from common_util.common_util_auth import verify_session
|
|
from M01_MasterData import M01_MasterData_Store as store
|
|
from M02_MasterTemplete import M02_Logic_Lib_Read as lib
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
HELPER = Path(__file__).parent / "helper_717_1_logic_api_paths.cjs"
|
|
KEY = "GF000001" # 경계표시 — 보통인부 한 줄
|
|
BASE = "/api/m02/logic-lib"
|
|
|
|
|
|
@pytest.mark.skipif(not (ROOT / "config" / "node_modules").is_dir(), reason="의존성 미설치")
|
|
def test_길_주입_없으면_m01_있으면_넘긴_길() -> None:
|
|
done = subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", str(HELPER)], cwd=str(ROOT), capture_output=True, text=True, encoding="utf-8"
|
|
)
|
|
assert done.returncode == 0, done.stderr
|
|
out = json.loads(done.stdout)
|
|
assert out["before"] == out["after"] # 안 넘기면 지금 그대로
|
|
assert all(u.startswith("/m01/") for u in out["before"])
|
|
shared, admin = out["injected"][:-2], out["injected"][-2:]
|
|
assert all(u.startswith(("/m02/logic-lib/", "/m02/calc-x/")) for u in shared)
|
|
assert [u for u in shared if u.startswith("/m02/calc-x/")] == [
|
|
"/m02/calc-x/calc",
|
|
"/m02/calc-x/text",
|
|
]
|
|
assert admin == ["/m01/groups", "/m01/save"] # 관리자 · 저장 길은 늘 M01
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def user() -> TestClient:
|
|
"""main.py 와 같은 등록(로그인만) · 일반 사용자."""
|
|
app = FastAPI()
|
|
app.include_router(lib.router, dependencies=[Depends(verify_session)])
|
|
app.dependency_overrides[verify_session] = lambda: {
|
|
"user_id": 42, "company_id": 7, "role": "USER", "is_master": False,
|
|
} # fmt: skip
|
|
return TestClient(app)
|
|
|
|
|
|
def _prints() -> dict[str, str]:
|
|
return {p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in store.FOLDER.glob("*.json")}
|
|
|
|
|
|
def test_일반_계정_읽기(user: TestClient) -> None:
|
|
assert user.get(f"{BASE}/logic/auto", params={"key": KEY}).status_code == 200
|
|
assert user.get(f"{BASE}/elements", params={"group": "인력", "q": "LB000002"}).json()["items"]
|
|
assert user.get(f"{BASE}/pick", params={"kind": "price", "q": "시멘트"}).status_code == 200
|
|
assert user.get(f"{BASE}/rows", params={"file": "기계.json", "size": 3}).json()["rows"]
|
|
assert user.get(f"{BASE}/materials", params={"sub": "시멘트"}).status_code == 200
|
|
found = user.get(f"{BASE}/elements", params={"group": "소요량", "q": "QF000025"}).json()
|
|
file = next(i["file"] for i in found["items"] if i["ref"] == "QF000025")
|
|
assert user.get(f"{BASE}/table", params={"file": file, "key": "QF000025"}).json()["table"]
|
|
|
|
|
|
def test_요소_표_밖_파일은_막음(user: TestClient) -> None:
|
|
logic_file = store.logics("", "", "", None, "")[0]["file"]
|
|
assert user.get(f"{BASE}/rows", params={"file": logic_file}).status_code == 403
|
|
assert user.get(f"{BASE}/table", params={"file": "기계.json", "key": "x"}).status_code == 403
|
|
assert user.get(f"{BASE}/rows", params={"file": "재료_../x.json"}).status_code == 404
|
|
|
|
|
|
def test_저장_안_한_줄_셈_쓰기_없음(user: TestClient) -> None:
|
|
before = _prints()
|
|
saved = user.post(f"{BASE}/calc", json={"key": KEY, "inputs": {}}).json()
|
|
row = store.logic(KEY)["logic"]
|
|
same = user.post(f"{BASE}/calc", json={"key": KEY, "inputs": {}, "row": row}).json()
|
|
line = row["호표"][0]
|
|
row2 = {**row, "호표": [{**line, "수량": f"({line['수량']}) * 2"}, *row["호표"][1:]]}
|
|
double = user.post(f"{BASE}/calc", json={"key": KEY, "inputs": {}, "row": row2}).json()
|
|
text = user.post(f"{BASE}/text", json={"key": KEY, "inputs": {}, "row": row2}).json()
|
|
assert saved["ok"] and same["sums"] == saved["sums"]
|
|
# 751-3 — 줄 0.1 원 · 소계 1 원 버림이라 두 배는 1 원 안
|
|
assert double["sums"]["노무비"] == pytest.approx(saved["sums"]["노무비"] * 2, abs=2)
|
|
assert text["ok"] and text["계"] == pytest.approx(double["sums"]["계"], rel=1e-6)
|
|
assert store.logic(KEY)["logic"]["호표"][0]["수량"] == line["수량"] # 정본 그대로
|
|
assert _prints() == before
|