Files
Aislo/resources/tester/test_364_4_precompute_on_open.py
eomsangdonandClaude Opus 5.5 e2b7133225 feat(763-4): 추천 미리 셈 끊기면 이어 셈 · 화면 진행 n/m · 저장본 없으면 [지금 셈]
- 진행 기록 `B05_Profile/route_recommend_job.json`(저장본 폴더 밖 — 저장본 glob 에 안 섞임) · `note_job` 재사용 · 갈래마다 갱신 · 끝 = running 거짓
- 진행 묻기 = 이 서버 메모리 먼저 · 없으면 기록(10 분 안 = 다른 서버가 셈 · 넘음 = 끊김 `interrupted`)
- 추천 요청 409 + 「끊김」 기록 = 이어 셈 예약 한 번(기록을 곧바로 「도는 중」으로 · 뒤 요청은 안 겹침)
- 미리 셈 한 갈래 409 = 그 갈래만 건너뜀(옛: 전부 멈춤)
- 409 글 = 「추천 저장본 없음 — 지표면이 바뀌어 다시 셈해야 함」(없는 [재계산] 단추 안내 걷음)
- 화면(RecPreview) — 셈 중이면 줄 마우스 글 「추천 셈 중 n/m」 · 알림 한 번 · 3 초마다 다시 물어 끝나면 그림 · 아니면 확인창 [지금 셈](그 갈래만 recompute) · 지형 바뀌면 받아 둔 선 비움
- 시험 309_3 · 364_4 · 509_5 · 626_1 을 새 글 · 진행 꼴 · [지금 셈]에 맞춤

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:01:28 +09:00

116 lines
4.2 KiB
Python

"""364-4 노선 추천 미리 셈 — B05 열 때 예약 · 겹쳐 셈 막음 · 모달 진행 표시.
./venv/Scripts/python.exe -m pytest resources/tester/test_364_4_precompute_on_open.py -q
옛 프로젝트 · 판이 바뀐 저장본은 사용자가 처음 누를 때 기다렸음(요구 239 · 토공 62.9 s).
`GET /route/plan`(B05 열기)이 미리 셈을 예약 · 도는 동안 또 오면 끝난 뒤 한 번 더 · 진행은
`GET /route/initial/progress` · 모달 상태줄이 그것을 붙임.
"""
import asyncio
from pathlib import Path
from uuid import uuid4
from B05_Profile import B05_Profile_Router_RouteInitial as router
ROOT = Path(__file__).resolve().parents[2]
def test_겹쳐_셈_막음_끝난_뒤_한_번_더(monkeypatch) -> None:
calls: list[str] = []
seen: list[dict] = []
pid = uuid4()
async def precompute(project_id):
calls.append(str(project_id))
progress = router._RUNNING[str(project_id)]
progress.update(total=5, done=2, mode="earthwork")
seen.append(router.precompute_progress(project_id))
await asyncio.sleep(0.05)
monkeypatch.setattr(router, "precompute_defaults", precompute)
async def main() -> None:
first = asyncio.create_task(router.run_precompute(pid))
await asyncio.sleep(0.01)
# 도는 중 두 번 더 와도 셈은 한 번 더만
await router.run_precompute(pid)
await router.run_precompute(pid)
await first
asyncio.run(main())
assert calls == [str(pid), str(pid)]
assert seen[0] == {
"running": True,
"interrupted": False, # 끊김 기록(763-4)
"done": 2,
"total": 5,
"mode": "earthwork",
}
assert router.precompute_progress(pid) == {
"running": False,
"interrupted": False,
"done": 0,
"total": 0,
"mode": None,
}
def test_실패해도_진행_비움(monkeypatch) -> None:
async def broken(_pid):
raise RuntimeError("셈 실패")
monkeypatch.setattr(router, "precompute_defaults", broken)
pid = uuid4()
try:
asyncio.run(router.run_precompute(pid))
except RuntimeError:
pass
assert not router.precompute_progress(pid)["running"]
def test_b05_열기_예약_모달_진행_배선() -> None:
replan = (ROOT / "B05_Profile/B05_Profile_Router_Replan.py").read_text(encoding="utf-8")
plan = replan[replan.index("async def read_route_plan") :]
# 505-1 — B05 열기는 미리 셈 안 부름(전처리 체인에서만)
assert "schedule_precompute(" not in plan[: plan.index("\n@router")]
source = (ROOT / "B05_Profile/B05_Profile_Router_RouteInitial.py").read_text(encoding="utf-8")
assert '"/{project_id}/route/initial/progress"' in source
# 체인 · B01 범위도 같은 한 줄(run_precompute)로
assert "run_precompute(project_id), task_name" in source
assert "await run_precompute(project_id)" in source
api = (ROOT / "B05_Profile/B05_Profile_Api_RouteInitial.ts").read_text(encoding="utf-8")
assert "/route/initial/progress" in api
def test_모달이_여는_갈래_먼저(monkeypatch) -> None:
"""저장된 갈래(추적 아님)를 먼저 셈 — 모달이 열자마자 그 갈래를 물음."""
from B05_Profile.B05_Profile_Engine_RouteInitial_Store import DEFAULT_MODES
asked: list[str] = []
async def paths(_pid):
return (ROOT, None)
async def compute(_pid, request, precompute=False):
assert precompute # 체인 셈 = 저장본 없어도 셈(505-1)
asked.append(request.mode)
assert request.with_profile # 다섯 갈래 모두 종단 켬(420)
return {}
monkeypatch.setattr(router.replan, "_project_paths", paths)
monkeypatch.setattr(
router,
"read_initial_choice",
lambda _root: {"logic": "gentle", "weights": None, "criteria": None},
)
monkeypatch.setattr(router, "compute_initial", compute)
async def terrains(_pid, _root):
return ["las"] # 지형 하나(509-5 지형마다 = test_509_5)
monkeypatch.setattr(router, "precompute_terrains", terrains)
asyncio.run(router.precompute_defaults(uuid4()))
assert asked[0] == "gentle" and asked[-1] == "follow"
assert sorted(asked) == sorted(DEFAULT_MODES)