o2o-site-AEO/solution/backend/tests/test_indexnow.py
Mina Choi 11d30bb3d1 [chore] solution,admin,ontology: 코드 주석을 한 줄로 — 히스토리 주석 삭제
여러 줄 주석이 설명보다 경위(예전·실측·지적)를 적고 있어 읽는 사람이 결론을 찾기 어려웠다.

- 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>
2026-09-28 16:05:19 +09:00

101 lines
3.5 KiB
Python

"""발행 URL 의 색인 통보(IndexNow)."""
from pathlib import Path
import httpx
import pytest
from services import indexnow
SITEMAP = """<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://w4ai.o2o.kr/s/butter/</loc></url>
<url><loc>https://w4ai.o2o.kr/s/butter/faq</loc></url>
</urlset>
"""
def _out(tmp_path: Path, slug: str, body: str = SITEMAP) -> Path:
site = tmp_path / "s" / slug
site.mkdir(parents=True)
(site / "sitemap.xml").write_text(body)
return tmp_path
def test_키가_없으면_통보하지_않는다(monkeypatch):
monkeypatch.delenv("INDEXNOW_KEY", raising=False)
assert indexnow.is_configured() is False
def test_보낼_URL_은_사이트맵에서_읽는다(tmp_path, monkeypatch):
monkeypatch.setenv("SITE_OUTPUT_DIR", str(_out(tmp_path, "butter")))
assert indexnow.site_urls("butter") == [
"https://w4ai.o2o.kr/s/butter/",
"https://w4ai.o2o.kr/s/butter/faq",
]
def test_사이트맵이_없으면_빈_목록(tmp_path, monkeypatch):
monkeypatch.setenv("SITE_OUTPUT_DIR", str(tmp_path))
assert indexnow.site_urls("없는사이트") == []
def test_깨진_사이트맵은_예외를_올리지_않는다(tmp_path, monkeypatch):
monkeypatch.setenv("SITE_OUTPUT_DIR", str(_out(tmp_path, "butter", "<urlset>깨짐")))
assert indexnow.site_urls("butter") == []
@pytest.mark.asyncio
async def test_키파일_위치와_호스트를_URL에서_뽑는다(tmp_path, monkeypatch):
monkeypatch.setenv("SITE_OUTPUT_DIR", str(_out(tmp_path, "butter")))
monkeypatch.setenv("INDEXNOW_KEY", "abc12345")
sent = {}
async def fake_post(self, url, json=None, **kwargs):
sent["url"] = url
sent["body"] = json
return httpx.Response(200, request=httpx.Request("POST", url))
monkeypatch.setattr(httpx.AsyncClient, "post", fake_post)
result = await indexnow.submit("butter")
assert sent["url"] == indexnow.ENDPOINT
assert sent["body"]["host"] == "w4ai.o2o.kr"
assert sent["body"]["key"] == "abc12345"
# 키 파일은 오리진 루트에 있다 — 사이트 경로(/s/butter) 아래가 아니다.
assert sent["body"]["keyLocation"] == "https://w4ai.o2o.kr/abc12345.txt"
assert sent["body"]["urlList"] == [
"https://w4ai.o2o.kr/s/butter/",
"https://w4ai.o2o.kr/s/butter/faq",
]
assert result == {"ok": True, "status": 200, "urls": 2}
@pytest.mark.asyncio
async def test_거절되어도_예외를_올리지_않는다(tmp_path, monkeypatch):
monkeypatch.setenv("SITE_OUTPUT_DIR", str(_out(tmp_path, "butter")))
monkeypatch.setenv("INDEXNOW_KEY", "abc12345")
async def fake_post(self, url, json=None, **kwargs):
# 403 = 키 파일 대조 실패.
return httpx.Response(403, text="key not found", request=httpx.Request("POST", url))
monkeypatch.setattr(httpx.AsyncClient, "post", fake_post)
assert await indexnow.submit("butter") == {"ok": False, "status": 403, "urls": 2}
@pytest.mark.asyncio
async def test_네트워크_실패도_삼킨다(tmp_path, monkeypatch):
monkeypatch.setenv("SITE_OUTPUT_DIR", str(_out(tmp_path, "butter")))
monkeypatch.setenv("INDEXNOW_KEY", "abc12345")
async def fake_post(self, url, json=None, **kwargs):
raise httpx.ConnectTimeout("timed out")
monkeypatch.setattr(httpx.AsyncClient, "post", fake_post)
result = await indexnow.submit("butter")
assert result["ok"] is False
assert "ConnectTimeout" in result["error"]