Files
our-claude-skills/custom-skills/35-seo-signal-validation/code/scripts/test_gsc_signal_delta.py
Andrew Yim 87635e4208 fix(skill): absent claim term -> INCONCLUSIVE hint; note surge-tuning (final-review minors #3,#4)
- gsc_signal_delta.py: extract `found` local var; add first branch in verdict_hint
  chain so a term absent from both GSC windows yields INCONCLUSIVE (not ARTIFACT).
  Existing ARTIFACT / CONFIRMED-PARTIAL / PARTIAL branches unchanged (elif chain).
- test_gsc_signal_delta.py: add test_absent_claim_term_inconclusive asserting
  found=False and "INCONCLUSIVE" in verdict_hint for a term in neither fixture.
- code/CLAUDE.md: one-line surge-tuning note — verdict_hint/in_top_movers are
  calibrated for upward claims; for drops, inspect top_decliners directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KuT3W81t88QQFaxY2ruWv2
2026-06-26 10:38:14 +09:00

66 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""Tests for gsc_signal_delta. Run: `python3 test_gsc_signal_delta.py`
(also pytest-compatible). Stdlib only."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from gsc_signal_delta import compute_delta # noqa: E402
# Genesis fixture: JHR "호텔" — flat head term, growth all brand (2026-06 case).
RECENT = [
{"query": "호텔", "clicks": 5, "impressions": 572, "position": 11.6},
{"query": "grand josun busan", "clicks": 250, "impressions": 4000, "position": 1.2},
{"query": "조선호텔", "clicks": 300, "impressions": 6000, "position": 1.1},
]
PRIOR = [
{"query": "호텔", "clicks": 9, "impressions": 371, "position": 18.1},
{"query": "grand josun busan", "clicks": 49, "impressions": 1500, "position": 3.4},
{"query": "조선호텔", "clicks": 150, "impressions": 5000, "position": 1.3},
]
def test_claim_term_flagged_artifact():
out = compute_delta(RECENT, PRIOR, 28, 30, claim_term="호텔")
ct = out["claim_term"]
assert ct["found"] is True
assert ct["in_top_movers"] is False
assert ct["click_share_pct"] < 1.0
assert "ARTIFACT" in out["verdict_hint"]
def test_top_gainer_is_brand_term():
out = compute_delta(RECENT, PRIOR, 28, 30, claim_term="호텔")
assert out["top_gainers"][0]["query"] == "grand josun busan"
assert out["top_gainers"][0]["delta_clicks"] == 201
def test_day_normalization():
out = compute_delta(RECENT, PRIOR, 28, 30)
assert out["site_totals"]["recent"]["clicks_per_day"] == 19.82 # 555/28
assert out["site_totals"]["prior"]["clicks_per_day"] == 6.93 # 208/30
def test_absent_claim_term_inconclusive():
out = compute_delta(RECENT, PRIOR, 28, 30, claim_term="존재하지않는검색어")
assert out["claim_term"]["found"] is False
assert "INCONCLUSIVE" in out["verdict_hint"]
def test_positive_days_required():
try:
compute_delta(RECENT, PRIOR, 0, 30)
except ValueError:
return
raise AssertionError("expected ValueError for non-positive days")
def _run():
fns = [v for k, v in sorted(globals().items()) if k.startswith("test_")]
for fn in fns:
fn(); print(f"PASS {fn.__name__}")
print(f"\n{len(fns)} passed")
if __name__ == "__main__":
_run()