036bcd2ae3
- Remove duplicate detect_query_type from query.py (divergent 5-type version); canonical 7-type version lives in query_type.py - Fix reddit.py import to use query_type.detect_query_type - Clean unused STOPWORDS/SYNONYMS/tokenize imports from youtube_yt, instagram, tiktok, scrapecreators_x, bird_x after relevance consolidation - Fix _relevance_filter default from 0.7 to 0.0 (items without relevance should not silently pass the filter) - Remove --dateafter from yt-dlp (returns 0 results for evergreen topics) - Remove restrictSearchableAttributes from HN search (misses Ask/Show HN) - Lower HN points filter from >5 to >2 (avoids filtering niche posts) - Add error logging to select_openai_model HTTP failures - Remove mise.toml and internal planning doc from repo - Update module docstrings to describe current purpose, not migration history - Update tests to import from canonical relevance module
111 lines
3.7 KiB
Python
111 lines
3.7 KiB
Python
"""Tests for YouTube relevance scoring."""
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
# Add lib to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))
|
|
|
|
from lib.relevance import token_overlap_relevance as _compute_relevance, tokenize as _tokenize
|
|
|
|
|
|
class TestTokenize(unittest.TestCase):
|
|
def test_basic(self):
|
|
tokens = _tokenize("Claude Code Tutorial")
|
|
self.assertIn("claude", tokens)
|
|
self.assertIn("code", tokens)
|
|
self.assertIn("tutorial", tokens)
|
|
|
|
def test_removes_stopwords(self):
|
|
tokens = _tokenize("how to use Claude Code for beginners")
|
|
self.assertNotIn("how", tokens)
|
|
self.assertNotIn("to", tokens)
|
|
self.assertNotIn("for", tokens)
|
|
self.assertIn("claude", tokens)
|
|
self.assertIn("code", tokens)
|
|
self.assertIn("beginners", tokens)
|
|
|
|
def test_strips_punctuation(self):
|
|
tokens = _tokenize("What's new in Claude?")
|
|
self.assertIn("claude", tokens)
|
|
self.assertNotIn("what's", tokens)
|
|
|
|
def test_drops_single_char(self):
|
|
tokens = _tokenize("a b c Claude")
|
|
self.assertNotIn("a", tokens)
|
|
self.assertNotIn("b", tokens)
|
|
self.assertIn("claude", tokens)
|
|
|
|
def test_empty(self):
|
|
tokens = _tokenize("")
|
|
self.assertEqual(tokens, set())
|
|
|
|
def test_all_stopwords(self):
|
|
tokens = _tokenize("the a an to for how")
|
|
self.assertEqual(tokens, set())
|
|
|
|
|
|
class TestComputeRelevance(unittest.TestCase):
|
|
def test_exact_match(self):
|
|
result = _compute_relevance("Claude Code", "Claude Code")
|
|
self.assertEqual(result, 1.0)
|
|
|
|
def test_full_match_in_longer_title(self):
|
|
result = _compute_relevance("Claude Code", "Claude Code Tutorial")
|
|
self.assertEqual(result, 1.0)
|
|
|
|
def test_partial_match(self):
|
|
result = _compute_relevance("Claude Code Tips", "Claude Tips for Beginners")
|
|
self.assertGreater(result, 0.5)
|
|
self.assertLess(result, 1.0)
|
|
|
|
def test_no_match(self):
|
|
result = _compute_relevance("Claude Code", "Python Web Scraping")
|
|
self.assertEqual(result, 0.1) # Floor
|
|
|
|
def test_empty_query_returns_neutral(self):
|
|
result = _compute_relevance("", "Some Video Title")
|
|
self.assertEqual(result, 0.5)
|
|
|
|
def test_stopword_only_query(self):
|
|
result = _compute_relevance("how to the", "Some Video Title")
|
|
self.assertEqual(result, 0.5)
|
|
|
|
def test_empty_title(self):
|
|
result = _compute_relevance("Claude Code", "")
|
|
self.assertEqual(result, 0.1) # Floor
|
|
|
|
def test_case_insensitive(self):
|
|
result = _compute_relevance("claude code", "CLAUDE CODE Tutorial")
|
|
self.assertEqual(result, 1.0)
|
|
|
|
def test_stopwords_in_title_dont_inflate(self):
|
|
# "Claude Code" query against a title with lots of stopwords
|
|
# Should still match well since Claude and Code are present
|
|
result = _compute_relevance(
|
|
"Claude Code",
|
|
"How To Use Claude Code For Complete Beginners"
|
|
)
|
|
self.assertEqual(result, 1.0)
|
|
|
|
def test_floor_at_0_1(self):
|
|
result = _compute_relevance("quantum computing", "cat videos compilation")
|
|
self.assertEqual(result, 0.1)
|
|
|
|
def test_cap_at_1_0(self):
|
|
result = _compute_relevance("AI", "AI AI AI AI AI")
|
|
self.assertLessEqual(result, 1.0)
|
|
|
|
def test_single_word_query(self):
|
|
result = _compute_relevance("Seedance", "Seedance AI Video Generator Review")
|
|
self.assertEqual(result, 1.0)
|
|
|
|
def test_single_word_no_match(self):
|
|
result = _compute_relevance("Seedance", "Random cooking video")
|
|
self.assertEqual(result, 0.1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|