|
|
|
@@ -1,10 +1,13 @@
|
|
|
|
|
"""D02 기준 지정(PLAN 529-3) — 경위도 ↔ TM 변환 · 지점 정본 API(잇기 · 빈 연도 · 권한).
|
|
|
|
|
"""D02 기준 지정(PLAN 529-3 · 560-2) — 경위도 ↔ TM 변환 · 전체 두 점 정본 API · 옛 연도 행 접기
|
|
|
|
|
· 연도 잇기(D03 [저장]이 씀).
|
|
|
|
|
|
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_529_3_plan_points.py -q
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
from fastapi import FastAPI
|
|
|
|
|
from fastapi import FastAPI, HTTPException
|
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
|
|
|
|
|
|
from common_util.common_util_auth import require_company
|
|
|
|
@@ -59,21 +62,16 @@ def api(monkeypatch):
|
|
|
|
|
"project": {"project_kind": "PLAN", "project_year": 2026, "project_year_end": 2027},
|
|
|
|
|
"rows": {},
|
|
|
|
|
"session": {"role": "USER", "email": "kim@forest.go.kr", "company_id": 1},
|
|
|
|
|
"stores": [],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async def load(project_id):
|
|
|
|
|
return state["project"], {y: dict(r) for y, r in state["rows"].items()}
|
|
|
|
|
|
|
|
|
|
async def store(project_id, epsg, writes, drops):
|
|
|
|
|
state["stores"].append((epsg, writes, drops))
|
|
|
|
|
for row in writes:
|
|
|
|
|
state["rows"][row["year"]] = {**row, "epsg": epsg}
|
|
|
|
|
for year in drops:
|
|
|
|
|
state["rows"].pop(year)
|
|
|
|
|
async def store_base(project_id, base):
|
|
|
|
|
state["project"] = {**state["project"], "plan_base_json": json.dumps(base)}
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(router_mod, "_load", load)
|
|
|
|
|
monkeypatch.setattr(router_mod, "_store", store)
|
|
|
|
|
monkeypatch.setattr(router_mod, "_store_base", store_base)
|
|
|
|
|
app = FastAPI()
|
|
|
|
|
app.include_router(router_mod.router)
|
|
|
|
|
app.dependency_overrides[require_company] = lambda: state["session"]
|
|
|
|
@@ -82,68 +80,58 @@ def api(monkeypatch):
|
|
|
|
|
return client
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_put_chains_years(api) -> None:
|
|
|
|
|
# 2027 시점을 엉뚱하게 보내도 서버가 2026 종점으로 맞춤
|
|
|
|
|
body = {"years": [{"year": 2026, "start": A, "end": B}, {"year": 2027, "start": A, "end": C}]}
|
|
|
|
|
got = api.put(URL, json=body)
|
|
|
|
|
def test_put_two_points(api) -> None:
|
|
|
|
|
got = api.put(URL, json={"start": A, "end": C})
|
|
|
|
|
assert got.status_code == 200
|
|
|
|
|
data = got.json()
|
|
|
|
|
assert data["epsg"] == 5186 and data["changed_years"] == [2026, 2027]
|
|
|
|
|
y26, y27 = data["years"]
|
|
|
|
|
assert y27["start"]["x"] == y26["end"]["x"] and y27["start"]["y"] == y26["end"]["y"]
|
|
|
|
|
assert abs(y27["start"]["lon"] - B["lon"]) < 1e-8 and abs(y27["start"]["lat"] - B["lat"]) < 1e-8
|
|
|
|
|
assert (y26["start"]["x"], y26["start"]["y"]) == pytest.approx(
|
|
|
|
|
assert data["epsg"] == 5186
|
|
|
|
|
assert (data["start"]["x"], data["start"]["y"]) == pytest.approx(
|
|
|
|
|
lonlat_to_tm(127.10, 37.50, 5186)
|
|
|
|
|
)
|
|
|
|
|
assert abs(data["end"]["lon"] - C["lon"]) < 1e-8 and abs(data["end"]["lat"] - C["lat"]) < 1e-8
|
|
|
|
|
stored = json.loads(api.state["project"]["plan_base_json"])
|
|
|
|
|
assert set(stored) == {"epsg", "start", "end"} and set(stored["start"]) == {"x", "y"}
|
|
|
|
|
assert api.state["rows"] == {} # 연도 행은 D02 가 안 만듦
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_put_rejects_empty_and_out_of_range(api) -> None:
|
|
|
|
|
only_first = {"years": [{"year": 2026, "start": A, "end": B}]}
|
|
|
|
|
got = api.put(URL, json=only_first)
|
|
|
|
|
assert got.status_code == 400 and "2027년 종점" in got.json()["detail"]
|
|
|
|
|
no_start = {"years": [{"year": 2026, "end": B}, {"year": 2027, "end": C}]}
|
|
|
|
|
assert "2026년 시점" in api.put(URL, json=no_start).json()["detail"]
|
|
|
|
|
outside = {"years": [{"year": 2026, "start": A, "end": B}, {"year": 2028, "end": C}]}
|
|
|
|
|
assert api.put(URL, json=outside).status_code == 400
|
|
|
|
|
assert api.state["stores"] == []
|
|
|
|
|
def test_put_one_point_and_empty(api) -> None:
|
|
|
|
|
data = api.put(URL, json={"start": A}).json()
|
|
|
|
|
assert data["start"] and data["end"] is None and data["epsg"] == 5186
|
|
|
|
|
data = api.put(URL, json={}).json()
|
|
|
|
|
assert data == {"status": "success", "epsg": None, "start": None, "end": None}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_put_forest_only(api) -> None:
|
|
|
|
|
body = {"years": [{"year": 2026, "start": A, "end": B}, {"year": 2027, "end": C}]}
|
|
|
|
|
api.state["session"] = {"role": "USER", "email": "lee@company.com", "company_id": 1}
|
|
|
|
|
assert api.put(URL, json=body).status_code == 403
|
|
|
|
|
assert api.put(URL, json={"start": A, "end": B}).status_code == 403
|
|
|
|
|
api.state["session"] = {"role": "SYSTEM_ADMIN", "email": "admin@x.com", "company_id": None}
|
|
|
|
|
assert api.put(URL, json=body).status_code == 200
|
|
|
|
|
assert api.put(URL, json={"start": A, "end": B}).status_code == 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_put_clears_only_changed_years(api) -> None:
|
|
|
|
|
body = {"years": [{"year": 2026, "start": A, "end": B}, {"year": 2027, "end": C}]}
|
|
|
|
|
api.put(URL, json=body)
|
|
|
|
|
# 같은 지점 다시 저장 = 쓰기 없음(분석 노선 그대로) · 2027 종점만 옮기면 2027 만
|
|
|
|
|
assert api.put(URL, json=body).json()["changed_years"] == []
|
|
|
|
|
moved = {"years": [{"year": 2026, "start": A, "end": B}, {"year": 2027, "end": A}]}
|
|
|
|
|
assert api.put(URL, json=moved).json()["changed_years"] == [2027]
|
|
|
|
|
assert len(api.state["stores"]) == 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_lists_every_year_and_drops_outside(api) -> None:
|
|
|
|
|
api.state["rows"][2030] = {
|
|
|
|
|
"year": 2030,
|
|
|
|
|
"start_x": 1,
|
|
|
|
|
"start_y": 1,
|
|
|
|
|
"end_x": 2,
|
|
|
|
|
"end_y": 2,
|
|
|
|
|
"epsg": 5186,
|
|
|
|
|
}
|
|
|
|
|
def test_get_folds_old_year_rows(api) -> None:
|
|
|
|
|
epsg = 5186
|
|
|
|
|
a, b, c = (lonlat_to_tm(p["lon"], p["lat"], epsg) for p in (A, B, C))
|
|
|
|
|
for year, s, e in ((2026, a, b), (2027, b, c)):
|
|
|
|
|
api.state["rows"][year] = {
|
|
|
|
|
"year": year,
|
|
|
|
|
**{"start_x": s[0], "start_y": s[1], "end_x": e[0], "end_y": e[1]},
|
|
|
|
|
"epsg": epsg,
|
|
|
|
|
}
|
|
|
|
|
data = api.get(URL).json()
|
|
|
|
|
assert [y["year"] for y in data["years"]] == [2026, 2027]
|
|
|
|
|
assert data["years"][0]["start"] is None
|
|
|
|
|
body = {"years": [{"year": 2026, "start": A, "end": B}, {"year": 2027, "end": C}]}
|
|
|
|
|
api.put(URL, json=body)
|
|
|
|
|
assert api.state["stores"][-1][2] == [2030] and 2030 not in api.state["rows"]
|
|
|
|
|
assert data["epsg"] == epsg
|
|
|
|
|
assert (data["start"]["x"], data["end"]["x"]) == pytest.approx((a[0], c[0]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_not_plan_project(api) -> None:
|
|
|
|
|
api.state["project"] = {"project_kind": "ROAD", "project_year": 2026, "project_year_end": None}
|
|
|
|
|
# _load 대역이 그대로 돌려주므로 진짜 _load 의 거절은 서버 시험(ORCA)에서
|
|
|
|
|
assert api.get(URL).status_code == 200
|
|
|
|
|
def test_get_empty_new_project(api) -> None:
|
|
|
|
|
assert api.get(URL).json() == {"status": "success", "epsg": None, "start": None, "end": None}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_chain_years_for_d03() -> None:
|
|
|
|
|
items = [
|
|
|
|
|
router_mod.YearPoints(year=2026, start=A, end=B),
|
|
|
|
|
router_mod.YearPoints(year=2027, start=A, end=C),
|
|
|
|
|
]
|
|
|
|
|
rows = router_mod.chain_years(2026, 2027, items)
|
|
|
|
|
assert rows[1]["start"].lon == B["lon"] # 시점 = 직전 종점
|
|
|
|
|
with pytest.raises(HTTPException):
|
|
|
|
|
router_mod.chain_years(2026, 2027, items[:1])
|
|
|
|
|