"""내 사이트 목록 — 로그인한 사장님이 자기 사이트 전부를 보는 화면의 뒷단. 이 경로가 절대 하면 안 되는 것: - 사이트가 아직 없는 사업장을 빼는 것 — 위저드를 걸어오다 만 가게가 목록에서 사라지면 사장님은 그걸 다시 찾을 길이 없다(에디터 주소를 아무도 기억하지 않는다). - 회사 스코프를 놓치는 것 — 남의 가게가 내 목록에 섞이면 그건 목록이 아니라 사고다. - 단건(GET /v1/place/{id}/site)과 다른 재빌드 판정을 내는 것 — 목록과 에디터가 서로 다른 답을 하면 사장님은 어느 쪽을 믿을지 알 수 없다. """ import uuid from sqlalchemy import text from common.enums import ErrorType, SiteStatus async def _place(client, headers, name): r = await client.post("/v1/place", headers=headers, json={"name": name, "category": 1}) return r.json()["place"]["place_id"] async def _list(client, headers, **params): return (await client.get("/v1/site/list", headers=headers, params=params)).json() async def test_place_without_site_is_still_listed(auth_headers, client): """검증: 사이트 행이 없는 사업장(위저드만 걸어온 것)도 목록에 나온다. 기대결과: 줄은 있고 site_id 는 없다 — 화면이 '만드는 중'으로 그릴 근거다.""" h = await auth_headers("my1") await _place(client, h, "아직펜션") body = await _list(client, h) assert body["result"]["code"] == ErrorType.SUCCESS.value assert body["total"] == 1 row = body["sites"][0] assert row["name"] == "아직펜션" assert row.get("site_id") is None assert row.get("status") is None async def test_site_row_is_joined_into_the_line(auth_headers, client): """검증: 사업장과 사이트가 한 줄로 합쳐져 온다(줄마다 사이트를 다시 묻지 않는다). 기대결과: 템플릿·주소가 목록에 그대로 보인다.""" h = await auth_headers("my2") pid = await _place(client, h, "합쳐진펜션") await client.post(f"/v1/place/{pid}/site/template", headers=h, json={"template_id": "stay-quiet-margin"}) await client.post(f"/v1/place/{pid}/site/slug", headers=h, json={"slug": "joined-stay"}) row = (await _list(client, h))["sites"][0] assert row["site_id"] assert row["template_id"] == "stay-quiet-margin" assert row["domain"] == "joined-stay" assert row["status"] == SiteStatus.DRAFT.value async def test_other_company_sites_are_not_listed(auth_headers, client, other_company_id): """검증: 회사(테넌트) 스코프. 남의 회사 사업장은 보이지 않는다. 기대결과: 각자 자기 것만 1건.""" mine = await auth_headers("my3") theirs = await auth_headers("my3b", other_company_id) await _place(client, mine, "내펜션") await _place(client, theirs, "남의펜션") assert [r["name"] for r in (await _list(client, mine))["sites"]] == ["내펜션"] assert [r["name"] for r in (await _list(client, theirs))["sites"]] == ["남의펜션"] async def test_needs_rebuild_matches_the_single_site_answer(auth_headers, client, db_engine): """검증: 재빌드 판정이 단건 조회와 같은 답을 낸다. 기대결과: 노출값이 바뀐 사업장은 목록에서도 needs_rebuild=true.""" h = await auth_headers("my4") pid = await _place(client, h, "고친펜션") # 템플릿 저장이 사이트 행을 만든다. 그 뒤 노출값이 바뀐 것으로 표시한다. await client.post(f"/v1/place/{pid}/site/template", headers=h, json={"template_id": "t"}) async with db_engine.begin() as conn: await conn.execute( text("UPDATE places SET content_updated_at = now() WHERE place_id = :pid"), {"pid": uuid.UUID(pid)}, ) single = (await client.get(f"/v1/place/{pid}/site", headers=h)).json() row = (await _list(client, h))["sites"][0] assert row["needs_rebuild"] is True assert row["needs_rebuild"] == single["needs_rebuild"] async def test_list_requires_login(client): """검증: 내 것을 보는 화면이므로 토큰 없이는 열리지 않는다. 기대결과: 401.""" assert (await client.get("/v1/site/list")).status_code == 401