여러 줄 주석이 설명보다 경위(예전·실측·지적)를 적고 있어 읽는 사람이 결론을 찾기 어려웠다. - ts·tsx·js·mjs·css·py 478개: 여러 줄 주석은 첫 문장 한 줄로, 과거형·날짜 문장은 삭제 - 주석 위치는 TypeScript 파서·파이썬 tokenize/ast 로 찾는다 — 문자열 안의 # · /* 는 건드리지 않는다 - eslint·ts·noqa·type: ignore 같은 지시 주석은 그대로 둔다 파이썬 275개 정리 전후 AST 동일, TS 298개 주석 뺀 토큰 동일(빈 JSX 주석 10곳만 차이). site·frontend·admin tsc, site vitest 105 passed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
105 lines
4.4 KiB
Python
105 lines
4.4 KiB
Python
"""템플릿 선택 저장."""
|
|
import uuid
|
|
|
|
from sqlalchemy import text
|
|
|
|
from common.enums import ErrorType, PlaceCategory, SiteStatus
|
|
from common.template_catalog import INDUSTRIES
|
|
from services.site_payload import to_site_payload
|
|
|
|
|
|
async def _place(client, headers, name="템플릿펜션", category=1):
|
|
r = await client.post("/v1/place", headers=headers, json={"name": name, "category": category})
|
|
return r.json()["place"]["place_id"]
|
|
|
|
|
|
async def _set_template(client, headers, pid, template_id):
|
|
return (await client.post(f"/v1/place/{pid}/site/template", headers=headers,
|
|
json={"template_id": template_id})).json()
|
|
|
|
|
|
async def _get_site(client, headers, pid):
|
|
return (await client.get(f"/v1/place/{pid}/site", headers=headers)).json()
|
|
|
|
|
|
async def test_set_template_creates_site_row(auth_headers, client):
|
|
"""검증: 사이트 행이 없어도 템플릿을 먼저 고를 수 있고, 조회에도 그대로 나온다."""
|
|
h = await auth_headers("tpl1")
|
|
pid = await _place(client, h)
|
|
|
|
saved = await _set_template(client, h, pid, "paper")
|
|
assert saved["result"]["code"] == ErrorType.SUCCESS.value
|
|
assert saved["site"]["template_id"] == "paper"
|
|
assert (await _get_site(client, h, pid))["site"]["template_id"] == "paper"
|
|
|
|
|
|
async def test_unknown_template_is_refused(auth_headers, client):
|
|
"""검증: 등록되지 않은 id는 거절한다."""
|
|
h = await auth_headers("tpl2")
|
|
pid = await _place(client, h)
|
|
|
|
refused = await _set_template(client, h, pid, "stay-retro")
|
|
assert refused["result"]["code"] == ErrorType.INVALID_REQUEST_DATA.value
|
|
assert "site" not in refused
|
|
|
|
|
|
async def test_template_outside_industry_list_is_refused(auth_headers, client):
|
|
"""검증: 등록된 템플릿이라도 그 업종 허용 목록에 없으면 거절한다(병원은 레트로를 못 쓴다)."""
|
|
h = await auth_headers("tpl3")
|
|
pid = await _place(client, h, name="템플릿의원", category=PlaceCategory.CLINIC.value)
|
|
|
|
refused = await _set_template(client, h, pid, "retro")
|
|
assert refused["result"]["code"] == ErrorType.INVALID_REQUEST_DATA.value
|
|
|
|
|
|
async def test_empty_value_clears_to_default(auth_headers, client):
|
|
"""검증: 빈 값은 '고르지 않음'이다 — NULL로 돌아가 업종 기본 템플릿으로 나간다."""
|
|
h = await auth_headers("tpl4")
|
|
pid = await _place(client, h)
|
|
await _set_template(client, h, pid, "paper")
|
|
|
|
cleared = await _set_template(client, h, pid, "")
|
|
assert cleared["result"]["code"] == ErrorType.SUCCESS.value
|
|
assert "template_id" not in cleared["site"]
|
|
|
|
|
|
async def test_published_site_template_is_not_locked(auth_headers, client, db_engine):
|
|
"""검증: 발행된 사이트도 템플릿을 바꿀 수 있고, 재빌드 표시가 선다."""
|
|
h = await auth_headers("tpl5")
|
|
pid = await _place(client, h)
|
|
await _set_template(client, h, pid, "simple")
|
|
|
|
async with db_engine.begin() as conn:
|
|
await conn.execute(
|
|
text("UPDATE sites SET status = :st, published_at = now() WHERE place_id = :p"),
|
|
{"st": SiteStatus.PUBLISHED.value, "p": uuid.UUID(pid)},
|
|
)
|
|
|
|
changed = await _set_template(client, h, pid, "paper")
|
|
assert changed["result"]["code"] == ErrorType.SUCCESS.value
|
|
assert changed["site"]["template_id"] == "paper"
|
|
assert changed["needs_rebuild"] is True
|
|
|
|
|
|
async def test_other_owners_place_is_blocked(auth_headers, client):
|
|
"""검증: 남의 사업장의 템플릿은 바꿀 수 없다(PLACE_NOT_FOUND)."""
|
|
h = await auth_headers("tpl6")
|
|
intruder = await auth_headers("tpl7")
|
|
pid = await _place(client, h)
|
|
|
|
blocked = await _set_template(client, intruder, pid, "paper")
|
|
assert blocked["result"]["code"] == ErrorType.PLACE_NOT_FOUND.value
|
|
|
|
|
|
def test_payload_uses_saved_template_and_falls_back():
|
|
"""검증: payload의 templateId는 저장값, 없으면 업종 기본 템플릿이다."""
|
|
place = {"place_id": uuid.uuid4(), "category": PlaceCategory.LODGING.value, "name": "스테이,머뭄"}
|
|
snapshot = {"place": {"name": "스테이,머뭄", "category": PlaceCategory.LODGING.value}}
|
|
version = {"version": 1}
|
|
|
|
chosen = to_site_payload(place, snapshot, {"template_id": "paper"}, version, [])
|
|
assert chosen["theme"]["templateId"] == "paper"
|
|
|
|
fallback = to_site_payload(place, snapshot, {"template_id": None}, version, [])
|
|
assert fallback["theme"]["templateId"] == INDUSTRIES["stay"]["defaultTemplate"]
|