"""PLAN 511-A (요구 320-4) — 브라우저 「서버」 길(`design_calc_server.ts`) · 판 따라 길 · 자리. 못박는 것 - 판 both = 브라우저 값 · 서버 부름 0(넣어도 바뀌는 것 0) · server = 잠잠 400 ms 뒤 한 번 (앞 부름 끊음). - 대조 = 같으면 그대로 · 다르면 apply(서버 값) · 그리기 자리 = 답 오면 redraw · 같은 입력 = fresh. - 끊긴 부름 = undefined · 왕복 흉내 delayMs · 창구 주소 = /projects/{id}/calc. - 자리마다 판 열쇠 · [저장] · [확정] 길은 서버 길을 안 부름. ./venv/Scripts/python.exe -m pytest resources/tester/test_511_a_calc_browser.py -q """ from __future__ import annotations import json import shutil import subprocess from pathlib import Path import pytest HERE = Path(__file__).resolve().parent ROOT = HERE.parents[1] def _text(rel: str) -> str: return (ROOT / rel).read_text(encoding="utf-8") @pytest.mark.skipif(shutil.which("node") is None, reason="node 없음") def test_판_따라_길_고름_잠잠_대조_기록(): proc = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", str(HERE / "helper_511_a_browser.cjs")], cwd=ROOT, capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr out = json.loads(proc.stdout) assert out["both"] == 5 and out["bothFetched"] == 0 and out["local"] == 1 assert out["server"] == 7 and out["beforeQuiet"] == 0 and out["afterQuiet"] == 1 assert out["same"] is True and out["applied"] == 0 assert out["diffApplied"] == 99 and out["diffSame"] is False draw = out["draw"] assert draw["first"] == {"fresh": False} and draw["redraws"] == 1 assert draw["second"] == {"value": ["s", 1], "fresh": True} assert draw["third"] == {"value": ["s", 1], "fresh": False} # 새 입력 = 앞 답 + 다시 물음 assert out["aborted"] is True assert out["onlyValue"] == [1] and out["delayed"] and out["roundMs"] >= 80 assert out["url"] == "http://fake/projects/p1/calc" # API_BASE_URL(가짜) + 창구 def test_자리마다_판_열쇠_저장_길_안_부름(): refresh = _text("B06_Section/B06_Section_Cross_Refresh.ts") assert 'calcOnServerNow("quantity")' in refresh and '"F7.saved"' in refresh shared = _text("A00_Common/design/design_shared.ts") assert 'calcChecked("quantity", "structure.layouts"' in shared assert 'calcChecked("quantity", "cross.berm_spans"' in shared lane = _text("A00_Common/design/design_facility_lane.ts") assert '"C4.copy", bermRequest(structures)' in lane mass = _text("B05_Profile/B05_Profile_UI_Profile_MassHaul.ts") assert 'calcFromServer("quantity", "masshaul.series"' in mass assert "openCalcSites(projectId)" in _text("A00_Common/design/design_page.ts") for rel in ( "A00_Common/design/design_save.ts", "A00_Common/design/design_save_api.ts", "A00_Common/design/design_reset.ts", ): assert "design_calc_server" not in _text(rel), rel page = _text("A00_Common/design/design_pipeline_page.ts") assert len(page.splitlines()) <= 699 and "design_calc_server" not in page # 연결 = 다른 파일 def test_이월_R9_자리_판_열쇠(): keys = _text("B06_Section/B06_Section_Station_Keys.ts") assert '"carry",\n "stations.carry_keyed",' in keys assert "carryStationDrafts(projectId, routeId, NEXT_ROUTE, { applyTo: routeId })" in keys def test_고정_브라우저_자리_판_열쇠(): sites = { "B05_Profile/B05_Profile_UI_RouteEdit_Pointer.ts": '"fixed_browser", "route.handle"', "B05_Profile/B05_Profile_UI_RouteEdit.ts": '"fixed_browser", "route.station_fit"', "B06_Section/B06_Section_Section_Store.ts": '"fixed_browser", "stations.swap"', "B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts": '"fixed_browser",\n "P7",', "B05_Profile/B05_Profile_UI_RouteEdit_Cross_Draw.ts": '"fixed_browser", "cross.fit"', "B05_Profile/B05_Profile_Footprint_Server.ts": '"fixed_browser", "D4"', } for rel, needle in sites.items(): assert needle in _text(rel), rel page = _text("A00_Common/design/design_pipeline_page.ts") assert "footprint: footprintStageSited," in page