Files
Aislo/resources/tester/test_547_1_project_summary.py
T
eomsangdonandClaude Opus 5.5 29512c001b feat(D 예정노선): D02 기준 지정 걷음 · 지점 마킹 = D03 노선분석 · 기준점 둘레 1 km 처음 받기 · 조용히 이어 받기 (PLAN 591-2 · 요구 452 · 453)
- D02 걷음 — 길 · 단계(PLAN 단계 둘 = 노선 분석 · 확정) · [>] · 대시보드 단계 점 · 서버 길 · 폴더 만들기 · 캐시 등록 · 글 · 가이드 폴더
- 등록 [등록] · 받은 예정노선 [보기] = D03 으로 곧바로
- D03 지점 = 긋기에 붙는 점 · 목록 칸(여섯 색 · 「지점 N」 · 🗑) · 핀 끌기 = 그 자리 노선 노드 같이 · [저장] 몸에 실음 · [초기화] = 선만 지우고 지점 남김
- 지도 우클릭 메뉴(부품 상자) — 여기서 긋기 시작(빈 땅 = 새 본선) · 갈래 시작 · 이어 긋기 · 지점 놓기 · 지점 지우기 · 긋기 끝
- 받기 — 처음 = 등록 기준점 둘레 ± 1 km(지점 · 선 없을 때) · 지점 · 선 · 지도 보기 가운데가 받은 범위 밖이면 조용히 이어 받음(단추 · 막는 창 걷음 · 실패만 [다시 받기])
- 좌표계 = 파일 머리 · 없으면 기준점 경도 TM 띠
- 시험: 529_3(D02 지점 API) 지움 → 591_2 로 갈음 · 280_2 · 528 · 530_2 · 547_1 · 548 · 553_4 · 556_2 · 560_4 · 401 · 54_2 = D02 단계 · 페이지 걷음에 맞춤 · 590_1 · 530_2_analyze = 사업연도 · 기준점 한 번에 읽음 · 591_1 D04 씨앗 시험 = 590-1 이 씨앗 줄 걷음에 맞춤

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:19:34 +09:00

144 lines
5.0 KiB
Python

"""547-1 프로젝트 카드 요약 — 썸네일 줄임 · 단계 · 없는 값 빼기 · 보이는 프로젝트만."""
import asyncio
import json
from datetime import datetime, timedelta
import B01_Dashboard.B01_Dashboard_Router_Home as home
import B01_Dashboard.B01_Dashboard_Summary as summary
ROUTE = "B05_Profile/route/r.geojson"
def test_thumb_line_fits_unit_box_and_caps_points():
line = [[1000.0 + i, 2000.0 + i * 0.5] for i in range(500)]
thumb = summary.thumb_line(line)
assert len(thumb) == 100
assert thumb[0] == [0.0, 0.75] and thumb[-1] == [1.0, 0.25] # y 아래로 · 세로 가운데
assert all(0 <= x <= 1 and 0 <= y <= 1 for x, y in thumb)
assert summary.thumb_line([[0.0, 0.0]]) is None
def test_stage_names_follow_step_bar():
assert len(summary.stage_names("ROAD")) == 7
assert summary.stage_names("PLAN")[0] == "노선 분석" # 591-2 기준 지정 걷음
assert summary.stage_names("EROSION") == summary.stage_names("ROAD")
class _Cursor:
def __init__(self, db):
self.db, self.rows = db, []
async def __aenter__(self):
return self
async def __aexit__(self, *_):
return False
async def execute(self, sql, args=()):
self.db["queries"] += 1
key = next(k for k in self.db["answers"] if k in sql)
self.rows = self.db["answers"][key]
async def fetchall(self):
return self.rows
class _Pool:
def __init__(self, db):
self.db = db
def acquire(self):
pool = self
class _Conn:
async def __aenter__(self):
return self
async def __aexit__(self, *_):
return False
def cursor(self, *_):
return _Cursor(pool.db)
return _Conn()
def test_summaries_in_batch(monkeypatch, tmp_path):
now = datetime.now()
route_dir = tmp_path / "B05_Profile" / "route"
route_dir.mkdir(parents=True)
coords = [[0, 0, 1], [10, 0, 1], [10, 5, 1]]
(route_dir / "r.geojson").write_text(json.dumps({"geometry": {"coordinates": coords}}))
(route_dir / "structures.json").write_text(json.dumps({"structures": [{}, {}]}))
db = {
"queries": 0,
"answers": {
"FROM projects p LEFT JOIN users": [
{
"id": "a",
"name": "가",
"project_kind": "ROAD",
"region": "강원",
"updated_at": now - timedelta(days=20),
"storage_path": "storage/a",
"company_id": 1,
"user_id": 7,
"owner_name": "엄",
},
{
"id": "b",
"name": "나",
"project_kind": "PLAN",
"region": None,
"updated_at": now,
"storage_path": None,
"company_id": 1,
"user_id": 7,
"owner_name": "엄",
},
],
"project_workflow_stages": [
{"project_id": "a", "stage_no": n, "stage_key": "", "state": s}
| {"progress_percent": 0, "params": None, "message": None}
| {"started_at": None, "completed_at": None}
for n, s in enumerate(["COMPLETE", "COMPLETE", "IN_PROGRESS"] + ["NOT"] * 4)
],
"project_members": [{"project_id": "a", "name": "박"}],
"FROM routes": [
{"id": 9, "project_id": "a", "total_length_m": 15.04, "route_data_path": ROUTE},
{"id": 5, "project_id": "a", "total_length_m": 1.0, "route_data_path": "old"},
],
"cross_sections": [{"route_id": 9, "n": 3}],
},
}
monkeypatch.setattr(summary, "get_db_pool", lambda: _Pool(db))
monkeypatch.setattr(summary, "project_root", lambda p: tmp_path if p else None)
out = asyncio.run(summary.project_summaries(["b", "a", "zz"]))
assert [s["id"] for s in out] == ["b", "a"] # 넣은 차례 · 없는 id 빠짐
b, a = out
assert (a["stage_index"], a["stage_total"], a["stage_name"]) == (2, 7, "노선설계")
assert a["stale_days"] == 20 and a["owner_names"] == ["엄", "박"]
assert a["metrics"] == {"length_m": 15.0, "stations": 3, "structures": 2}
assert len(a["thumb"]) == 3 and a["done"] is False
assert b["thumb"] is None and b["metrics"] == {} and b["stage_name"] == "노선 분석"
assert db["queries"] == 5
def test_summary_api_drops_invisible_ids(monkeypatch):
seen = {}
async def fake_visible(session):
return ["a", "b"]
async def fake_summaries(ids):
seen["ids"] = ids
return []
monkeypatch.setattr(home, "visible_project_ids", fake_visible)
monkeypatch.setattr(home, "project_summaries", fake_summaries)
asyncio.run(home.projects_summary(ids="b, x,a,b", session={}))
assert seen["ids"] == ["b", "a"]
asyncio.run(home.projects_summary(ids=None, session={}))
assert seen["ids"] == ["a", "b"]