"
p = parse_search_html(html)[0]
assert (p.shipping_fee, p.shipping_type) == (3000, "paid")
def test_parse_shipping_name_free_text_no_false_positive():
# 상품명에 '무료배송' 이 들어가도 배송 무료로 오인하지 않는다
html = f"
{_card('무료배송 텀블러 굿즈', '12,000원')}
"
p = parse_search_html(html)[0]
assert (p.shipping_fee, p.shipping_type) == (None, None)
# --- 차단 감지(detect_block): 여러 flavor 를 모두 blocked 로 잡아야 IP 회전이 걸린다 ---
def test_detect_block_none_when_products_exist():
# 상품이 파싱됐으면 HTML 내용과 무관하게 차단 아님
assert detect_block("Access Denied", product_count=3) is None
def test_detect_block_akamai_challenge():
assert detect_block("
" + "x" * 20000, 0) == "sec-if-cpt-container"
def test_detect_block_edge_access_denied():
# 실제로 잡힌 303B edge deny
html = ('Access Denied
Access Denied
'
"You don't have permission to access ... errors.edgesuite.net")
assert detect_block(html, 0) in ("errors.edgesuite.net", "You don't have permission to access")
def test_detect_block_permission_restricted_page():
html = "쿠팡을 찾아주신 고객님, 입력하신 페이지주소는 사용권한이 제한된 페이지입니다."
assert detect_block(html, 0) in ("사용권한이 제한된", "쿠팡을 찾아주신 고객님")
def test_detect_block_short_html_fallback():
# 마커 없어도 비정상적으로 짧은 0건 응답은 미지의 차단으로 폴백
marker = detect_block("oops", 0)
assert marker is not None and marker.startswith("short_html(")
def test_detect_block_genuine_empty_large_page_not_blocked():
# 정상 '검색결과 없음'(전체 chrome 포함, 큰 HTML)은 차단 아님 → not_found 로 흘러야 함
big = "검색결과가 없습니다" + "x" * 20000 + ""
assert detect_block(big, 0) is None