@@ -108,6 +108,21 @@ def test_new_papers_sorted_by_date_descending(self):
108108 dates = [p .date for p in result .new_papers ]
109109 assert dates == sorted (dates , reverse = True )
110110
111+ def test_updated_papers_sorted_by_date_descending (self ):
112+ prev = {
113+ "P2300R10" : self ._paper ("P2300R10" , title = "Old A" , date = "2024-01-01" ),
114+ "P2301R0" : self ._paper ("P2301R0" , title = "Old B" , date = "2024-03-01" ),
115+ "P2302R0" : self ._paper ("P2302R0" , title = "Old C" , date = "2024-06-01" ),
116+ }
117+ curr = {
118+ "P2300R10" : self ._paper ("P2300R10" , title = "New A" , date = "2024-01-01" ),
119+ "P2301R0" : self ._paper ("P2301R0" , title = "New B" , date = "2024-06-01" ),
120+ "P2302R0" : self ._paper ("P2302R0" , title = "New C" , date = "2024-03-01" ),
121+ }
122+ result = diff_snapshots (prev , curr )
123+ dates = [p .date for p in result .updated_papers ]
124+ assert dates == sorted (dates , reverse = True )
125+
111126 def test_empty_to_empty (self ):
112127 result = diff_snapshots ({}, {})
113128 assert result .new_papers == [] and result .updated_papers == []
@@ -168,6 +183,7 @@ def _make_scheduler(fake_pool, **cfg_overrides):
168183 index .papers = {}
169184 prober = MagicMock (spec = ISOProber )
170185 prober .run_cycle = AsyncMock (return_value = [])
186+ prober .snapshot_stats = MagicMock (return_value = {})
171187 prober ._stats = {}
172188 user_watchlist = MagicMock (spec = UserWatchlist )
173189 user_watchlist .matches_for_users .return_value = {}
@@ -351,6 +367,57 @@ async def test_poll_once_calls_notify_callback(self, fake_pool):
351367 await scheduler .poll_once () # real poll
352368 assert len (notified ) == 1
353369
370+ async def test_cold_start_first_poll_does_not_notify (self , fake_pool ):
371+ notified = []
372+ scheduler , _ , _ , _ , _ = _make_scheduler (fake_pool )
373+ scheduler .notify_callback = notified .append
374+ result = await scheduler .poll_once ()
375+ assert notified == []
376+ assert result .probe_hits == []
377+
378+ async def test_restart_with_prior_poll_notifies_seed_hits (self , fake_pool ):
379+ notified = []
380+ scheduler , _ , prober , user_watchlist , state = _make_scheduler (fake_pool )
381+ scheduler .notify_callback = notified .append
382+ state .touch_poll ()
383+ hit = _recent_hit ()
384+ prober .run_cycle = AsyncMock (return_value = [hit ])
385+ user_watchlist .matches_for_users .return_value = {
386+ "U123" : PerUserMatches (papers = [], probe_hits = [(hit , "author" )])
387+ }
388+ result = await scheduler .poll_once ()
389+ assert len (notified ) == 1
390+ assert len (result .probe_hits ) == 1
391+ assert result .probe_hits [0 ].is_recent is True
392+
393+ async def test_restart_with_discovered_urls_notifies (self , fake_pool ):
394+ notified = []
395+ scheduler , _ , prober , user_watchlist , state = _make_scheduler (fake_pool )
396+ scheduler .notify_callback = notified .append
397+ state .mark_discovered ("https://isocpp.org/files/papers/D1111R0.pdf" )
398+ hit = _recent_hit ()
399+ prober .run_cycle = AsyncMock (return_value = [hit ])
400+ user_watchlist .matches_for_users .return_value = {
401+ "U123" : PerUserMatches (papers = [], probe_hits = [(hit , "author" )])
402+ }
403+ result = await scheduler .poll_once ()
404+ assert len (notified ) == 1
405+ assert len (result .probe_hits ) == 1
406+
407+ async def test_restart_seed_old_hits_not_in_result (self , fake_pool , caplog ):
408+ import logging
409+
410+ notified = []
411+ scheduler , _ , prober , _ , state = _make_scheduler (fake_pool )
412+ scheduler .notify_callback = notified .append
413+ state .touch_poll ()
414+ old = _old_hit ()
415+ prober .run_cycle = AsyncMock (return_value = [old ])
416+ with caplog .at_level (logging .INFO ):
417+ result = await scheduler .poll_once ()
418+ assert result .probe_hits == []
419+ assert "PROBE-OLD" in caplog .text
420+
354421 async def test_poll_once_skips_refresh_when_disabled (self , fake_pool ):
355422 scheduler , index , _ , _ , _ = _make_scheduler (fake_pool , enable_bulk_wg21 = False )
356423 scheduler ._seeded = True
@@ -368,8 +435,14 @@ async def test_poll_once_skips_probe_when_disabled(self, fake_pool):
368435 async def test_seed_marks_discovered (self , fake_pool ):
369436 scheduler , _ , prober , _ , state = _make_scheduler (fake_pool )
370437 hit = _recent_hit ()
371- prober .run_cycle = AsyncMock (return_value = [hit ])
372- await scheduler .seed ()
438+
439+ async def fake_run_cycle ():
440+ state .mark_discovered (hit .url )
441+ return [hit ]
442+
443+ prober .run_cycle = AsyncMock (side_effect = fake_run_cycle )
444+ seed_result = await scheduler .seed ()
445+ assert seed_result .probe_hits == [hit ]
373446 assert state .is_discovered (hit .url )
374447
375448 async def test_run_forever_calls_poll_and_breaks_on_cancel (self , fake_pool ):
0 commit comments