@@ -46,10 +46,13 @@ def _make_report(
4646 return report
4747
4848
49- def _make_requester () -> ReportCreationRequester :
49+ def _make_requester (config : dict = None ) -> ReportCreationRequester :
5050 """Create a ReportCreationRequester instance with mocked internals for unit testing."""
5151 requester = object .__new__ (ReportCreationRequester )
5252
53+ # Set config — defaults to empty dict (max_done_report_age_hours defaults to 0 in component)
54+ requester .config = config or {}
55+
5356 # Mock _request_body_json to return a controlled body
5457 requester ._request_body_json = MagicMock ()
5558
@@ -270,7 +273,7 @@ class TestFindExistingReport:
270273
271274 def test_returns_matching_report (self ):
272275 requester = _make_requester ()
273- matching_report = _make_report (report_id = "rpt-match" , marketplace_ids = ["ATVPDKIKX0DER" ])
276+ matching_report = _make_report (report_id = "rpt-match" , status = "IN_PROGRESS" , marketplace_ids = ["ATVPDKIKX0DER" ])
274277 get_response = _make_get_reports_response ([matching_report ])
275278 requester ._http_client .send_request .return_value = (None , get_response )
276279
@@ -476,9 +479,28 @@ def test_reuses_in_queue_report(self):
476479 assert result is not None
477480 assert result .json ()["reportId" ] == "rpt-in-queue"
478481
482+ def test_skips_done_report_when_max_age_is_zero (self ):
483+ """When max_done_report_age_hours is 0 (default), DONE reports should never be reused."""
484+ requester = _make_requester () # default config, max_done_report_age_hours=0
485+ now = datetime .now (tz = timezone .utc )
486+ report = _make_report (report_id = "rpt-done" , status = "DONE" , created_time = now .isoformat ())
487+ get_response = _make_get_reports_response ([report ])
488+ requester ._http_client .send_request .return_value = (None , get_response )
489+
490+ result = requester ._find_existing_report (
491+ stream_state = None ,
492+ stream_slice = None ,
493+ report_type = "GET_AMAZON_FULFILLED_SHIPMENTS_DATA_GENERAL" ,
494+ requested_start = "2023-01-01T00:00:00Z" ,
495+ requested_end = "2023-01-30T00:00:00Z" ,
496+ requested_marketplace_ids = ["ATVPDKIKX0DER" ],
497+ )
498+
499+ assert result is None
500+
479501 def test_skips_stale_done_report (self ):
480- """DONE reports older than 24h should be skipped."""
481- requester = _make_requester ()
502+ """DONE reports older than max_done_report_age_hours should be skipped."""
503+ requester = _make_requester (config = { "max_done_report_age_hours" : 24 } )
482504 # Report created 48 hours ago
483505 old_time = datetime (2023 , 1 , 1 , 0 , 0 , 0 , tzinfo = timezone .utc ).isoformat ()
484506 report = _make_report (report_id = "rpt-stale" , status = "DONE" , created_time = old_time )
@@ -496,9 +518,9 @@ def test_skips_stale_done_report(self):
496518
497519 assert result is None
498520
499- def test_reuses_fresh_done_report (self ):
500- """DONE reports created within 24h should be reusable."""
501- requester = _make_requester ()
521+ def test_reuses_fresh_done_report_when_max_age_set (self ):
522+ """DONE reports created within max_done_report_age_hours should be reusable."""
523+ requester = _make_requester (config = { "max_done_report_age_hours" : 24 } )
502524 # Use a very recent timestamp
503525 now = datetime .now (tz = timezone .utc )
504526 fresh_time = now .isoformat ()
@@ -519,8 +541,8 @@ def test_reuses_fresh_done_report(self):
519541 assert result .json ()["reportId" ] == "rpt-fresh"
520542
521543 def test_in_progress_report_not_subject_to_staleness (self ):
522- """IN_PROGRESS reports should not be subject to the 24h staleness check."""
523- requester = _make_requester ()
544+ """IN_PROGRESS reports should not be subject to the staleness check even when max_age is 0 ."""
545+ requester = _make_requester () # default config, max_done_report_age_hours=0
524546 old_time = datetime (2023 , 1 , 1 , 0 , 0 , 0 , tzinfo = timezone .utc ).isoformat ()
525547 report = _make_report (report_id = "rpt-old-ip" , status = "IN_PROGRESS" , created_time = old_time )
526548 get_response = _make_get_reports_response ([report ])
@@ -540,7 +562,7 @@ def test_in_progress_report_not_subject_to_staleness(self):
540562
541563 def test_returns_latest_report_by_created_time (self ):
542564 """When multiple matching reports exist, return the most recently created one."""
543- requester = _make_requester ()
565+ requester = _make_requester (config = { "max_done_report_age_hours" : 24 } )
544566 now = datetime .now (tz = timezone .utc )
545567 older_time = (now - timedelta (hours = 2 )).isoformat ()
546568 newer_time = (now - timedelta (hours = 1 )).isoformat ()
@@ -565,7 +587,7 @@ def test_returns_latest_report_by_created_time(self):
565587
566588 def test_returns_latest_in_progress_over_older_done (self ):
567589 """A newer IN_PROGRESS report should be preferred over an older DONE report."""
568- requester = _make_requester ()
590+ requester = _make_requester (config = { "max_done_report_age_hours" : 24 } )
569591 now = datetime .now (tz = timezone .utc )
570592 done_time = (now - timedelta (hours = 10 )).isoformat ()
571593 ip_time = (now - timedelta (hours = 1 )).isoformat ()
@@ -587,9 +609,9 @@ def test_returns_latest_in_progress_over_older_done(self):
587609 assert result is not None
588610 assert result .json ()["reportId" ] == "rpt-ip"
589611
590- def test_done_report_without_created_time_is_still_usable (self ):
591- """DONE reports without createdTime should still be reusable (no staleness check) ."""
592- requester = _make_requester ()
612+ def test_done_report_without_created_time_is_still_usable_when_max_age_set (self ):
613+ """DONE reports without createdTime should still be reusable when max_age > 0 ."""
614+ requester = _make_requester (config = { "max_done_report_age_hours" : 24 } )
593615 report = _make_report (report_id = "rpt-no-time" , status = "DONE" )
594616 get_response = _make_get_reports_response ([report ])
595617 requester ._http_client .send_request .return_value = (None , get_response )
@@ -606,6 +628,96 @@ def test_done_report_without_created_time_is_still_usable(self):
606628 assert result is not None
607629 assert result .json ()["reportId" ] == "rpt-no-time"
608630
631+ def test_done_report_without_created_time_skipped_when_max_age_is_zero (self ):
632+ """DONE reports without createdTime should be skipped when max_age is 0."""
633+ requester = _make_requester () # default config, max_done_report_age_hours=0
634+ report = _make_report (report_id = "rpt-no-time" , status = "DONE" )
635+ get_response = _make_get_reports_response ([report ])
636+ requester ._http_client .send_request .return_value = (None , get_response )
637+
638+ result = requester ._find_existing_report (
639+ stream_state = None ,
640+ stream_slice = None ,
641+ report_type = "GET_AMAZON_FULFILLED_SHIPMENTS_DATA_GENERAL" ,
642+ requested_start = "2023-01-01T00:00:00Z" ,
643+ requested_end = "2023-01-30T00:00:00Z" ,
644+ requested_marketplace_ids = ["ATVPDKIKX0DER" ],
645+ )
646+
647+ assert result is None
648+
649+ def test_reuses_done_report_with_custom_max_age (self ):
650+ """DONE report created 3h ago should be reused when max_done_report_age_hours=6."""
651+ requester = _make_requester (config = {"max_done_report_age_hours" : 6 })
652+ now = datetime .now (tz = timezone .utc )
653+ report = _make_report (
654+ report_id = "rpt-3h" ,
655+ status = "DONE" ,
656+ created_time = (now - timedelta (hours = 3 )).isoformat (),
657+ )
658+ get_response = _make_get_reports_response ([report ])
659+ requester ._http_client .send_request .return_value = (None , get_response )
660+
661+ result = requester ._find_existing_report (
662+ stream_state = None ,
663+ stream_slice = None ,
664+ report_type = "GET_AMAZON_FULFILLED_SHIPMENTS_DATA_GENERAL" ,
665+ requested_start = "2023-01-01T00:00:00Z" ,
666+ requested_end = "2023-01-30T00:00:00Z" ,
667+ requested_marketplace_ids = ["ATVPDKIKX0DER" ],
668+ )
669+
670+ assert result is not None
671+ assert result .json ()["reportId" ] == "rpt-3h"
672+
673+ def test_skips_done_report_exceeding_custom_max_age (self ):
674+ """DONE report created 10h ago should be skipped when max_done_report_age_hours=6."""
675+ requester = _make_requester (config = {"max_done_report_age_hours" : 6 })
676+ now = datetime .now (tz = timezone .utc )
677+ report = _make_report (
678+ report_id = "rpt-10h" ,
679+ status = "DONE" ,
680+ created_time = (now - timedelta (hours = 10 )).isoformat (),
681+ )
682+ get_response = _make_get_reports_response ([report ])
683+ requester ._http_client .send_request .return_value = (None , get_response )
684+
685+ result = requester ._find_existing_report (
686+ stream_state = None ,
687+ stream_slice = None ,
688+ report_type = "GET_AMAZON_FULFILLED_SHIPMENTS_DATA_GENERAL" ,
689+ requested_start = "2023-01-01T00:00:00Z" ,
690+ requested_end = "2023-01-30T00:00:00Z" ,
691+ requested_marketplace_ids = ["ATVPDKIKX0DER" ],
692+ )
693+
694+ assert result is None
695+
696+ def test_default_config_skips_done_but_reuses_in_progress (self ):
697+ """With default config (max_age=0), DONE reports are skipped but IN_PROGRESS is reused."""
698+ requester = _make_requester () # default config, max_done_report_age_hours=0
699+ now = datetime .now (tz = timezone .utc )
700+ done_report = _make_report (report_id = "rpt-done" , status = "DONE" , created_time = now .isoformat ())
701+ ip_report = _make_report (
702+ report_id = "rpt-ip" ,
703+ status = "IN_PROGRESS" ,
704+ created_time = (now - timedelta (hours = 1 )).isoformat (),
705+ )
706+ get_response = _make_get_reports_response ([done_report , ip_report ])
707+ requester ._http_client .send_request .return_value = (None , get_response )
708+
709+ result = requester ._find_existing_report (
710+ stream_state = None ,
711+ stream_slice = None ,
712+ report_type = "GET_AMAZON_FULFILLED_SHIPMENTS_DATA_GENERAL" ,
713+ requested_start = "2023-01-01T00:00:00Z" ,
714+ requested_end = "2023-01-30T00:00:00Z" ,
715+ requested_marketplace_ids = ["ATVPDKIKX0DER" ],
716+ )
717+
718+ assert result is not None
719+ assert result .json ()["reportId" ] == "rpt-ip"
720+
609721
610722class TestSendRequest :
611723 """Tests for ReportCreationRequester.send_request integration."""
@@ -618,7 +730,7 @@ def test_reuses_existing_report_when_found(self):
618730 "dataEndTime" : "2023-01-30T00:00:00Z" ,
619731 "marketplaceIds" : ["ATVPDKIKX0DER" ],
620732 }
621- matching_report = _make_report (report_id = "rpt-existing" )
733+ matching_report = _make_report (report_id = "rpt-existing" , status = "IN_PROGRESS" )
622734 get_response = _make_get_reports_response ([matching_report ])
623735 requester ._http_client .send_request .return_value = (None , get_response )
624736
0 commit comments