@@ -509,6 +509,155 @@ def test_comparison_with_sampling_leap_year():
509509 assert deterministic .days >= 365
510510
511511
512+ # ==============================================================================
513+ # Test 12: _get_smallest_gap helper function tests
514+ # ==============================================================================
515+
516+
517+ def test_get_smallest_gap_two_values ():
518+ """Test _get_smallest_gap with exactly two values."""
519+ from dagster ._utils .schedules import _get_smallest_gap
520+
521+ # Simple case: [5, 10] -> gap is 5
522+ assert _get_smallest_gap ([5 , 10 ]) == 5
523+
524+ # Reverse order (should still work due to sorting): [10, 5] -> gap is 5
525+ assert _get_smallest_gap ([10 , 5 ]) == 5
526+
527+ # Large gap: [0, 50] -> gap is 50
528+ assert _get_smallest_gap ([0 , 50 ]) == 50
529+
530+
531+ def test_get_smallest_gap_multiple_values ():
532+ """Test _get_smallest_gap with multiple values."""
533+ from dagster ._utils .schedules import _get_smallest_gap
534+
535+ # Three values: [0, 15, 30] -> gaps are 15, 15 -> min is 15
536+ assert _get_smallest_gap ([0 , 15 , 30 ]) == 15
537+
538+ # Irregular gaps: [0, 5, 15] -> gaps are 5, 10 -> min is 5
539+ assert _get_smallest_gap ([0 , 5 , 15 ]) == 5
540+
541+ # Gaps at the end: [0, 10, 20, 25] -> gaps are 10, 10, 5 -> min is 5
542+ assert _get_smallest_gap ([0 , 10 , 20 , 25 ]) == 5
543+
544+ # Unsorted input: [30, 0, 15] -> sorted to [0, 15, 30] -> gaps are 15, 15 -> min is 15
545+ assert _get_smallest_gap ([30 , 0 , 15 ]) == 15
546+
547+
548+ def test_get_smallest_gap_with_wrap_around ():
549+ """Test _get_smallest_gap with wrap-around at specified boundary."""
550+ from dagster ._utils .schedules import _get_smallest_gap
551+
552+ # Wrap-around for minutes: [50, 5, 20] with wrap_at=60
553+ # Sorted: [5, 20, 50]
554+ # Gaps: 20-5=15, 50-20=30, wrap: (60-50)+5=15
555+ # Min: 15
556+ assert _get_smallest_gap ([50 , 5 , 20 ], wrap_at = 60 ) == 15
557+
558+ # Wrap-around for minutes: [55, 5] with wrap_at=60
559+ # Gaps: 5-55 wrap = (60-55)+5=10
560+ assert _get_smallest_gap ([55 , 5 ], wrap_at = 60 ) == 10
561+
562+ # Wrap-around for minutes: [0, 59] with wrap_at=60
563+ # Sorted: [0, 59]
564+ # Gaps: 59-0=59, wrap: (60-59)+0=1
565+ # Min: 1
566+ assert _get_smallest_gap ([0 , 59 ], wrap_at = 60 ) == 1
567+
568+ # Wrap-around for hours: [22, 2] with wrap_at=24
569+ # Sorted: [2, 22]
570+ # Gaps: 22-2=20, wrap: (24-22)+2=4
571+ # Min: 4
572+ assert _get_smallest_gap ([22 , 2 ], wrap_at = 24 ) == 4
573+
574+ # Wrap-around for hours: [20, 4, 12] with wrap_at=24
575+ # Sorted: [4, 12, 20]
576+ # Gaps: 12-4=8, 20-12=8, wrap: (24-20)+4=8
577+ # Min: 8
578+ assert _get_smallest_gap ([20 , 4 , 12 ], wrap_at = 24 ) == 8
579+
580+ # Wrap-around for days of week: [6, 1] with wrap_at=7 (Saturday to Monday)
581+ # Sorted: [1, 6]
582+ # Gaps: 6-1=5, wrap: (7-6)+1=2
583+ # Min: 2
584+ assert _get_smallest_gap ([6 , 1 ], wrap_at = 7 ) == 2
585+
586+ # Wrap-around for days of week: [0, 5] with wrap_at=7 (Sunday and Friday)
587+ # Sorted: [0, 5]
588+ # Gaps: 5-0=5, wrap: (7-5)+0=2
589+ # Min: 2
590+ assert _get_smallest_gap ([0 , 5 ], wrap_at = 7 ) == 2
591+
592+ # Wrap-around for days of week: [4, 0] with wrap_at=7 (Thursday and Sunday)
593+ # Sorted: [0, 4]
594+ # Gaps: 4-0=4, wrap: (7-4)+0=3
595+ # Min: 3
596+ assert _get_smallest_gap ([4 , 0 ], wrap_at = 7 ) == 3
597+
598+
599+ def test_get_smallest_gap_edge_cases ():
600+ """Test _get_smallest_gap with edge cases."""
601+ from dagster ._utils .schedules import _get_smallest_gap
602+
603+ # Single value: returns None (no gaps possible)
604+ assert _get_smallest_gap ([5 ]) is None
605+
606+ # Empty list: returns None
607+ assert _get_smallest_gap ([]) is None
608+
609+ # Consecutive integers: [0, 1, 2, 3] -> all gaps are 1
610+ assert _get_smallest_gap ([0 , 1 , 2 , 3 ]) == 1
611+
612+ # All same value (after sorting duplicate scenario): [5, 5] -> gap is 0
613+ assert _get_smallest_gap ([5 , 5 ]) == 0
614+
615+
616+ def test_get_smallest_gap_wrap_around_equal_to_non_wrap ():
617+ """Test cases where wrap-around gap equals non-wrap gap."""
618+ from dagster ._utils .schedules import _get_smallest_gap
619+
620+ # [0, 30] with wrap_at=60
621+ # Gaps: 30-0=30, wrap: (60-30)+0=30
622+ # Min: 30
623+ assert _get_smallest_gap ([0 , 30 ], wrap_at = 60 ) == 30
624+
625+ # [0, 12] with wrap_at=24
626+ # Gaps: 12-0=12, wrap: (24-12)+0=12
627+ # Min: 12
628+ assert _get_smallest_gap ([0 , 12 ], wrap_at = 24 ) == 12
629+
630+
631+ def test_get_smallest_gap_real_world_cron_scenarios ():
632+ """Test _get_smallest_gap with real-world cron schedule scenarios."""
633+ from dagster ._utils .schedules import _get_smallest_gap
634+
635+ # Every 15 minutes: 0, 15, 30, 45
636+ assert _get_smallest_gap ([0 , 15 , 30 , 45 ], wrap_at = 60 ) == 15
637+
638+ # Every 5 minutes: 0, 5, 10, 15, 20, ...
639+ assert _get_smallest_gap ([0 , 5 , 10 , 15 , 20 , 25 , 30 , 35 , 40 , 45 , 50 , 55 ], wrap_at = 60 ) == 5
640+
641+ # Business hours (9 AM to 5 PM): 9, 10, 11, 12, 13, 14, 15, 16, 17
642+ assert _get_smallest_gap ([9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 ]) == 1
643+
644+ # Twice daily: midnight and noon [0, 12]
645+ assert _get_smallest_gap ([0 , 12 ], wrap_at = 24 ) == 12
646+
647+ # Weekdays: Mon(1), Tue(2), Wed(3), Thu(4), Fri(5)
648+ # Gaps: all 1, but wrap from Fri to Mon is (7-5)+1 = 3
649+ assert _get_smallest_gap ([1 , 2 , 3 , 4 , 5 ], wrap_at = 7 ) == 1
650+
651+ # Mon, Wed, Fri: 1, 3, 5
652+ # Gaps: 3-1=2, 5-3=2, wrap: (7-5)+1=3
653+ assert _get_smallest_gap ([1 , 3 , 5 ], wrap_at = 7 ) == 2
654+
655+ # Weekend: Sat(6), Sun(0)
656+ # Sorted: [0, 6]
657+ # Gaps: 6-0=6, wrap: (7-6)+0=1
658+ assert _get_smallest_gap ([0 , 6 ], wrap_at = 7 ) == 1
659+
660+
512661def test_comparison_with_sampling_complex_patterns ():
513662 """Test complex patterns match between methods."""
514663 complex_patterns = [
0 commit comments