@@ -740,6 +740,39 @@ def test_publish_one_keeps_limited_error_logs() -> None:
740740 assert len (compact ["stderr" ]) < len ("error output\n " * 200 )
741741
742742
743+ def test_publish_one_recent_cycle_guard_blocks_old_retry_window (tmp_path : Path , monkeypatch : pytest .MonkeyPatch ) -> None :
744+ state_path = tmp_path / "news_pipeline/data/state/heartbeat-publish-one.json"
745+ state_path .parent .mkdir (parents = True )
746+ state_path .write_text ('{"last_started_at": 1000}\n ' , encoding = "utf-8" )
747+ monkeypatch .setattr (heartbeat_publish_one .time , "time" , lambda : 1915 )
748+
749+ allowed , info = heartbeat_publish_one ._recent_cycle_guard (
750+ tmp_path ,
751+ heartbeat_publish_one .DEFAULT_MIN_INTERVAL_SECONDS ,
752+ False ,
753+ )
754+
755+ assert allowed is False
756+ assert info ["ageSeconds" ] == 915
757+ assert info ["minIntervalSeconds" ] == 3000
758+
759+
760+ def test_publish_one_recent_cycle_guard_allows_next_hour (tmp_path : Path , monkeypatch : pytest .MonkeyPatch ) -> None :
761+ state_path = tmp_path / "news_pipeline/data/state/heartbeat-publish-one.json"
762+ state_path .parent .mkdir (parents = True )
763+ state_path .write_text ('{"last_started_at": 1000}\n ' , encoding = "utf-8" )
764+ monkeypatch .setattr (heartbeat_publish_one .time , "time" , lambda : 4100 )
765+
766+ allowed , info = heartbeat_publish_one ._recent_cycle_guard (
767+ tmp_path ,
768+ heartbeat_publish_one .DEFAULT_MIN_INTERVAL_SECONDS ,
769+ False ,
770+ )
771+
772+ assert allowed is True
773+ assert info ["ageSeconds" ] == 3100
774+
775+
743776def test_publish_one_auto_build_skips_clean_content_only_publish (monkeypatch : pytest .MonkeyPatch ) -> None :
744777 monkeypatch .setattr (
745778 heartbeat_publish_one ,
0 commit comments