"""256-2 B08 거리이정표 경로 그림 = 전체 경로(사용자 162-7). 캡처 · 길찾기는 가짜로: ① 사슬 = 거래처 → 기준점 사슬(254 bases · to) → 시점 · 화면 `mapMarks` 차례 · 좌표 없는 상자 뺌 ② 구간마다 카카오 길 선 · 수기 · 실패 구간 = 곧은 선 · auto/<경로 id>.png ③ [저장] 뒤 지문 비교 — 안 바뀜 = 0 · 기준점 · 시점 옮김 = 그 사슬 경로만 · 안 이은 거래처 = 없음 · 화면 [길찾기] 그림(거래처 구간만)이 들어오면 다음 [저장]에 전체로 """ import sys from pathlib import Path from uuid import uuid4 from fastapi import FastAPI from fastapi.testclient import TestClient ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from B08_Quantity import B08_Quantity_Router_Milestone as ms # noqa: E402 PNG = b"\x89PNG\r\n\x1a\nfake-route" START = [127.5, 35.4] BASES = [ {"id": "b1", "name": "거창IC", "lon": 127.91, "lat": 35.68, "to": "b2"}, {"id": "b2", "name": "함양JC", "lon": 127.72, "lat": 35.52, "manual": True}, ] ROUTES = [ {"id": "r1", "name": "거창레미콘", "lon": 127.95, "lat": 35.70, "to": "b1"}, {"id": "r2", "name": "직송", "lon": 127.60, "lat": 35.45}, {"id": "r3", "name": "안 이음", "lon": 127.61, "lat": 35.46, "linked": False}, {"id": "r4", "name": "좌표 없음", "to": "b2"}, ] DATA = {"bases": BASES, "routes": ROUTES} class Fake: def __init__(self): self.shots = [] # (경로 id, lines, marks) self.asked = [] # 길찾기 구간 def shoot(self, out_dir, route_id, lines, marks): self.shots.append((route_id, lines, marks)) out_dir.mkdir(parents=True, exist_ok=True) (out_dir / f"{route_id}.png").write_bytes(PNG) return {"saved": True, "reason": None} def directions(self, origin, destination): self.asked.append((origin.lon, destination.lon)) if origin.lon == 127.60: # r2 → 시점 = 길찾기 실패 return {"km": None, "reason": "실패"} return {"km": 1.0, "reason": None, "path": [[origin.lat, origin.lon], [0, 0]]} def _setup(tmp_path, monkeypatch, start=START): fake = Fake() box = {"start": start} monkeypatch.setattr(ms, "make_route_image", fake.shoot) monkeypatch.setattr(ms, "directions_km", fake.directions) monkeypatch.setattr(ms, "make_place_image", lambda *a: {"saved": False}) # 256-1 시험 monkeypatch.setattr( ms, "site_info", lambda root, mid: {"start_lonlat": box["start"], "mid_default_m": None} ) return fake, box def _save(root, data): ms.save_data(root, ms.Milestone.model_validate(data)) ms.refresh_images(root) def test_사슬_화면_차례(): data = ms.Milestone.model_validate(DATA).model_dump() chain = ms.route_chain(data, data["routes"][0], START) assert chain == [ ("거창레미콘", 127.95, 35.70, False), ("거창IC", 127.91, 35.68, False), ("함양JC", 127.72, 35.52, True), ("시점", 127.5, 35.4, False), ] assert [c[0] for c in ms.route_chain(data, data["routes"][3], START)] == ["함양JC", "시점"] assert [c[0] for c in ms.route_chain(data, data["routes"][1], None)] == ["직송"] def test_전체_경로_그림_구간_선(tmp_path, monkeypatch): fake, _ = _setup(tmp_path, monkeypatch) _save(tmp_path, DATA) shots = {rid: (lines, marks) for rid, lines, marks in fake.shots} assert sorted(shots) == ["r1", "r2", "r4"] # r3 안 이음 = 없음 lines, marks = shots["r1"] assert [m["name"] for m in marks] == ["거창레미콘", "거창IC", "함양JC", "시점"] assert len(lines) == 3 assert lines[0] == [[35.70, 127.95], [0, 0]] # 카카오 길 선 assert lines[2] == [[35.52, 127.72], [35.4, 127.5]] # 수기 기준점 = 곧은 선 · 길찾기 안 함 assert (127.72, 127.5) not in fake.asked assert shots["r2"][0] == [[[35.45, 127.60], [35.4, 127.5]]] # 길찾기 실패 = 곧은 선 assert (tmp_path / "B08_Quantity/milestone/auto/r1.png").read_bytes() == PNG def test_바뀐_경로만_다시(tmp_path, monkeypatch): fake, box = _setup(tmp_path, monkeypatch) _save(tmp_path, DATA) fake.shots.clear() ms.refresh_images(tmp_path) assert fake.shots == [] # 안 바뀜 moved = {**DATA, "bases": [BASES[0], {**BASES[1], "lat": 35.55}]} _save(tmp_path, moved) assert sorted(s[0] for s in fake.shots) == ["r1", "r4"] # b2 를 지나는 경로만 fake.shots.clear() box["start"] = [127.4, 35.3] # 시점 옮김(노선 수정) = 모두 ms.refresh_images(tmp_path) assert sorted(s[0] for s in fake.shots) == ["r1", "r2", "r4"] fake.shots.clear() (tmp_path / "B08_Quantity/milestone/auto/r2.png").unlink() # 그림 없음 = 다시 ms.refresh_images(tmp_path) assert [s[0] for s in fake.shots] == ["r2"] def test_길찾기_그림_뒤엔_다음_저장에_전체로(tmp_path, monkeypatch): fake, _ = _setup(tmp_path, monkeypatch) async def root(project_id): return tmp_path monkeypatch.setattr(ms, "_project_root", root) monkeypatch.delenv("PYTEST_CURRENT_TEST") # [저장] 뒤 캡처 켬(시험 중엔 꺼 둠) app = FastAPI() app.include_router(ms.router) client = TestClient(app) base = f"/api/projects/{uuid4()}/quantity/milestone" assert client.put(base, json=DATA).status_code == 200 # 답 뒤 캡처 assert sorted(s[0] for s in fake.shots) == ["r1", "r2", "r4"] fake.shots.clear() part = {"lines": [[[35.70, 127.95], [35.68, 127.91]]], "marks": []} assert client.post(base + "/image/auto", params={"route": "r1"}, json=part).status_code == 200 assert [s[0] for s in fake.shots] == ["r1"] # 화면 길찾기 그림(거래처 구간만) fake.shots.clear() ms.refresh_images(tmp_path) assert [(s[0], len(s[2])) for s in fake.shots] == [("r1", 4)] # 전체 경로로 다시