- 진행 기록 `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>
93 lines
4.0 KiB
Python
93 lines
4.0 KiB
Python
"""763-4 추천 미리 셈이 끊기면 이어 셈 — 진행 기록 · 한 갈래 409 는 그 갈래만 건너뜀 · 화면 글 짝.
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_763_4_precompute_resume.py -q
|
|
"""
|
|
|
|
import asyncio
|
|
import json
|
|
import time
|
|
from pathlib import Path
|
|
from uuid import uuid4
|
|
|
|
from fastapi.responses import JSONResponse
|
|
from test_224_3_recommend_store import _ask, wired # noqa: F401 — 픽스처
|
|
|
|
from B05_Profile import B05_Profile_Router_RouteInitial as router
|
|
from B05_Profile.B05_Profile_Engine_RouteInitial_Store import DEFAULT_MODES
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def _record(root: Path, running: bool, age_s: float) -> None:
|
|
path = root / router.JOB_FILE
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
body = {"running": running, "done": 3, "total": 10, "mode": "gentle"}
|
|
path.write_text(json.dumps({**body, "updated": time.time() - age_s}), encoding="utf-8")
|
|
|
|
|
|
def test_진행_기록_읽기(tmp_path) -> None:
|
|
pid = uuid4()
|
|
assert router.precompute_progress(pid, tmp_path)["running"] is False
|
|
_record(tmp_path, True, 5) # 다른 서버가 도는 중
|
|
state = router.precompute_progress(pid, tmp_path)
|
|
assert state["running"] and not state["interrupted"] and state["done"] == 3
|
|
_record(tmp_path, True, router.JOB_STALE_S + 1) # 재시동으로 끊김
|
|
state = router.precompute_progress(pid, tmp_path)
|
|
assert not state["running"] and state["interrupted"]
|
|
_record(tmp_path, False, 5)
|
|
assert router.precompute_progress(pid, tmp_path)["interrupted"] is False
|
|
|
|
|
|
def test_끊김_기록_409_이어_셈_한_번(wired, monkeypatch) -> None: # noqa: F811
|
|
scheduled: list = []
|
|
monkeypatch.setattr(router, "schedule_precompute", scheduled.append)
|
|
_record(wired.root, True, router.JOB_STALE_S + 1)
|
|
for _ in range(3): # 화면이 여러 번 물어도 예약 한 번(기록이 곧바로 「도는 중」)
|
|
answer = _ask(False, mode="gentle")
|
|
assert answer.status_code == 409
|
|
assert json.loads(answer.body)["message"] == router.NOT_PREPARED
|
|
assert len(scheduled) == 1
|
|
assert router.precompute_progress(uuid4(), wired.root)["running"] is True
|
|
|
|
|
|
def test_끊김_아니면_예약_없음(wired, monkeypatch) -> None: # noqa: F811
|
|
scheduled: list = []
|
|
monkeypatch.setattr(router, "schedule_precompute", scheduled.append)
|
|
_ask(False, mode="gentle") # 기록 없음
|
|
_record(wired.root, True, 5) # 다른 서버가 셈 중 — 안 겹침
|
|
_ask(False, mode="gentle")
|
|
assert scheduled == []
|
|
|
|
|
|
def test_미리_셈_기록_남기고_끝(wired) -> None: # noqa: F811
|
|
asyncio.run(router.run_precompute(uuid4()))
|
|
saved = json.loads((wired.root / router.JOB_FILE).read_text(encoding="utf-8"))
|
|
assert saved["running"] is False and saved["done"] == saved["total"] == len(DEFAULT_MODES)
|
|
|
|
|
|
def test_한_갈래_409_는_그_갈래만_건너뜀(wired, monkeypatch) -> None: # noqa: F811
|
|
real = router.compute_initial
|
|
asked: list[str] = []
|
|
|
|
async def flaky(project_id, request, precompute=False):
|
|
asked.append(request.mode)
|
|
if request.mode == "gentle":
|
|
return JSONResponse(status_code=409, content={"message": "x"})
|
|
return await real(project_id, request, precompute)
|
|
|
|
monkeypatch.setattr(router, "compute_initial", flaky)
|
|
asyncio.run(router.precompute_defaults(uuid4()))
|
|
assert sorted(asked) == sorted(DEFAULT_MODES)
|
|
assert wired.count == len(DEFAULT_MODES) - 1
|
|
|
|
|
|
def test_화면_머리말_서버_글과_같음() -> None:
|
|
preview = (ROOT / "B05_Profile/B05_Profile_UI_RouteEdit_RecPreview.ts").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
head = preview.split('const NOT_PREPARED_HEAD = "', 1)[1].split('"', 1)[0]
|
|
assert router.NOT_PREPARED.startswith(head)
|
|
assert "fetchPrecomputeProgress(" in preview and "recompute: true" in preview
|
|
assert 'okLabel: "지금 셈"' in preview and "lines.clear()" in preview
|
|
assert "<button" not in preview # 부품 상자만(확인창 = ui_template_modal)
|