fix(news): 304 를 redirect 로 오인하던 버그 — is_redirect → has_redirect_location

httpx 의 Response.is_redirect 는 3xx 전체(304 Not Modified 포함)에 True 라,
조건부 GET 으로 304 를 받으면 location 없는 같은 URL 을 3회 재요청 후
'redirect 3회 초과'로 오류 처리 → ETag/Last-Modified 받는 안정 피드(SEP/HSE/OSHA
/철학 RSS 등)가 2번째 사이클부터 전멸하던 systematic 버그.

- 304 처리를 redirect 루프보다 앞으로 이동.
- redirect 판별을 has_redirect_location(=location 헤더 있는 진짜 redirect)으로 교체.
  news_collector._fetch_rss + crawl_politeness.fetch_page 동일 함정 양쪽 수정.
- 사이클 1 파일럿(경향)은 304 를 받은 적 없어 잠복했고, 안정 피드 첫 304 에서 발현.
- 회귀 테스트 3건(304 비-redirect / 진짜 redirect / 코드 패턴 audit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
hyungi
2026-06-11 06:32:15 +09:00
parent 69db9bcb94
commit f4e5db9723
3 changed files with 43 additions and 12 deletions
+24
View File
@@ -108,6 +108,30 @@ class TestSkipVideoQuirk:
assert not self.PATTERN.search("https://psyche.co/ideas/how-to-think")
class TestRedirect304Distinction:
"""httpx is_redirect 가 304(3xx 전체)에 True 라 redirect 로 오인 → 조건부 GET
안정 피드가 'redirect 3회 초과'로 전멸하던 버그. has_redirect_location 으로 구분."""
def test_304_is_not_a_redirect_location(self):
import httpx
r = httpx.Response(304, request=httpx.Request("GET", "https://x/"))
assert r.is_redirect is True # httpx 함정: 304 도 is_redirect
assert r.has_redirect_location is False # 우리가 써야 하는 정확한 판별
def test_real_redirect_has_location(self):
import httpx
r = httpx.Response(301, headers={"location": "https://y/"},
request=httpx.Request("GET", "https://x/"))
assert r.has_redirect_location is True
def test_collector_uses_has_redirect_location(self):
import inspect
from workers import news_collector
src = inspect.getsource(news_collector._fetch_rss)
assert "has_redirect_location" in src
assert "while resp.is_redirect" not in src # 옛 버그 패턴 부재
class TestArticleHashStability:
def test_static_corpus_hash_deterministic(self):
a = _article_hash("Creep and Creep Failures", "static", "National Board 기술 아티클")