| 1 | """Reddit engagement keepers: the month's most-discussed on-topic threads survive |
| 2 | per-stream truncation, and the fused pool reserves slots for them.""" |
| 3 | |
| 4 | from lib import fusion, pipeline, schema |
| 5 | |
| 6 | |
| 7 | def _reddit_item(i, *, score=1, ncmt=0, relevance=0.5, title="Kanye West thread", rank=None): |
| 8 | item = schema.SourceItem( |
| 9 | item_id=f"R{i}", |
| 10 | source="reddit", |
| 11 | title=f"{title} {i}", |
| 12 | body=f"{title} {i}", |
| 13 | url=f"https://www.reddit.com/r/Kanye/comments/t{i:03d}/", |
| 14 | engagement={"score": score, "num_comments": ncmt}, |
| 15 | ) |
| 16 | item.local_relevance = relevance |
| 17 | item.local_rank_score = rank if rank is not None else relevance |
| 18 | return item |
| 19 | |
| 20 | |
| 21 | def _stream(n=36): |
| 22 | items = [_reddit_item(i, relevance=0.5 - i * 0.01, rank=0.5 - i * 0.01) for i in range(n)] |
| 23 | return items |
| 24 | |
| 25 | |
| 26 | class TestStreamKeepers: |
| 27 | def test_high_engagement_thread_ranked_23rd_survives_truncation(self): |
| 28 | items = _stream() |
| 29 | big = _reddit_item(99, score=16180, ncmt=1450, relevance=0.19, rank=0.36, |
| 30 | title="Kanye West Heads to Putin's Russia") |
| 31 | items.insert(22, big) |
| 32 | kept = pipeline._apply_reddit_stream_keepers("reddit", items, 12, "Kanye West") |
| 33 | assert len(kept) == 12 |
| 34 | assert big in kept |
| 35 | |
| 36 | def test_zero_relevance_thread_is_not_kept(self): |
| 37 | items = _stream() |
| 38 | big = _reddit_item(99, score=16180, ncmt=1450, relevance=0.0, rank=0.0, title="Something else") |
| 39 | items.insert(22, big) |
| 40 | kept = pipeline._apply_reddit_stream_keepers("reddit", items, 12, "Kanye West") |
| 41 | assert big not in kept |
| 42 | |
| 43 | def test_thread_without_entity_head_token_is_not_kept(self): |
| 44 | items = _stream() |
| 45 | big = _reddit_item(99, score=16180, ncmt=1450, relevance=0.19, rank=0.36, |
| 46 | title="Ye Heads to Putin's Russia") |
| 47 | items.insert(22, big) |
| 48 | kept = pipeline._apply_reddit_stream_keepers("reddit", items, 12, "Kanye West") |
| 49 | assert big not in kept |
| 50 | |
| 51 | def test_short_stream_is_returned_whole(self): |
| 52 | items = _stream(2) |
| 53 | kept = pipeline._apply_reddit_stream_keepers("reddit", items, 12, "Kanye West") |
| 54 | assert kept == items |
| 55 | |
| 56 | def test_non_reddit_stream_is_plain_truncation(self): |
| 57 | items = _stream() |
| 58 | for it in items: |
| 59 | it.source = "x" |
| 60 | kept = pipeline._apply_reddit_stream_keepers("x", items, 12, "Kanye West") |
| 61 | assert kept == items[:12] |
| 62 | |
| 63 | def test_generic_head_token_requires_higher_relevance(self): |
| 64 | items = _stream() |
| 65 | big = _reddit_item(99, score=9000, ncmt=900, relevance=0.15, rank=0.2, |
| 66 | title="AI second brain setups") |
| 67 | items.insert(22, big) |
| 68 | kept = pipeline._apply_reddit_stream_keepers("reddit", items, 12, "AI second brain") |
| 69 | assert big not in kept |
| 70 | big.local_relevance = 0.3 |
| 71 | kept = pipeline._apply_reddit_stream_keepers("reddit", items, 12, "AI second brain") |
| 72 | assert big in kept |
| 73 | |
| 74 | |
| 75 | def _cand(i, source, rrf, *, relevance=0.5, score=0, ncmt=0, title="Kanye West item"): |
| 76 | item = schema.SourceItem( |
| 77 | item_id=f"{source}{i}", |
| 78 | source=source, |
| 79 | title=f"{title} {i}", |
| 80 | body=f"{title} {i}", |
| 81 | url=f"https://example.com/{source}/{i}", |
| 82 | engagement={"score": score, "num_comments": ncmt}, |
| 83 | ) |
| 84 | return schema.Candidate( |
| 85 | candidate_id=f"{source}-{i}", |
| 86 | item_id=item.item_id, |
| 87 | source=source, |
| 88 | title=item.title, |
| 89 | url=item.url, |
| 90 | snippet="", |
| 91 | subquery_labels=["primary"], |
| 92 | native_ranks={f"primary:{source}": i + 1}, |
| 93 | local_relevance=relevance, |
| 94 | freshness=80, |
| 95 | engagement=5.0, |
| 96 | source_quality=0.7, |
| 97 | rrf_score=rrf, |
| 98 | sources=[source], |
| 99 | source_items=[item], |
| 100 | ) |
| 101 | |
| 102 | |
| 103 | class TestPoolReservation: |
| 104 | def _fused(self): |
| 105 | fused = [_cand(i, "tiktok", 0.05 - i * 0.0005) for i in range(45)] |
| 106 | fused += [_cand(i, "reddit", 0.001 - i * 0.00001, score=1, ncmt=1) for i in range(5)] |
| 107 | return fused |
| 108 | |
| 109 | def test_top_engagement_reddit_candidate_is_reserved_at_default_depth(self): |
| 110 | fused = self._fused() |
| 111 | big = _cand(99, "reddit", 0.0001, relevance=0.19, score=16180, ncmt=1450) |
| 112 | fused.append(big) |
| 113 | pool = fusion._diversify_pool(fused, 40, entity="kanye west") |
| 114 | assert len(pool) == 40 |
| 115 | assert big in pool |
| 116 | |
| 117 | def test_out_of_window_reddit_candidate_is_not_reserved(self): |
| 118 | fused = self._fused() |
| 119 | big = _cand(99, "reddit", 0.0001, relevance=0.19, score=16180, ncmt=1450) |
| 120 | big.metadata["range_from"] = "2026-08-01" |
| 121 | big.metadata["range_to"] = "2026-08-31" |
| 122 | big.source_items[0].published_at = "2025-01-01" |
| 123 | big.source_items[0].date_confidence = "high" |
| 124 | fused.append(big) |
| 125 | pool = fusion._diversify_pool(fused, 40, entity="kanye west") |
| 126 | assert big not in pool |
| 127 | |
| 128 | def test_entity_miss_reddit_candidate_is_not_reserved(self): |
| 129 | fused = self._fused() |
| 130 | big = _cand(99, "reddit", 0.0001, relevance=0.19, score=16180, ncmt=1450, title="Unrelated viral thread") |
| 131 | fused.append(big) |
| 132 | pool = fusion._diversify_pool(fused, 40, entity="kanye west") |
| 133 | assert big not in pool |
| 134 | |
| 135 | def test_reservation_scales_with_pool_limit(self): |
| 136 | assert fusion._reddit_reserve_for(15) == 2 |
| 137 | assert fusion._reddit_reserve_for(40) == 3 |
| 138 | assert fusion._reddit_reserve_for(60) == 4 |
| 139 | |
| 140 | |
| 141 | class TestKeeperAndReservationBounds: |
| 142 | def test_keepers_never_exceed_the_stream_limit(self): |
| 143 | items = [_reddit_item(i, score=1000 - i, ncmt=50, relevance=0.5, rank=0.5 - i * 0.01) for i in range(10)] |
| 144 | kept = pipeline._apply_reddit_stream_keepers("reddit", items, 1, "Kanye West") |
| 145 | assert len(kept) == 1 |
| 146 | kept = pipeline._apply_reddit_stream_keepers("reddit", items, 0, "Kanye West") |
| 147 | assert kept == [] |
| 148 | |
| 149 | def test_reservation_survives_many_qualifying_sources(self): |
| 150 | fused = [] |
| 151 | for source in ("tiktok", "x", "youtube", "hackernews", "grounding", "instagram", "github", "linkedin"): |
| 152 | fused += [_cand(i, source, 0.05 - i * 0.0005) for i in range(4)] |
| 153 | big = _cand(99, "reddit", 0.0001, relevance=0.19, score=16180, ncmt=1450) |
| 154 | fused.append(big) |
| 155 | pool = fusion._diversify_pool(fused, 15, entity="kanye west") |
| 156 | assert len(pool) == 15 |
| 157 | assert big in pool |
| 158 |