@@ -17,6 +17,7 @@ limitations under the License.
1717package cmd
1818
1919import (
20+ "errors"
2021 "fmt"
2122 "os"
2223 "testing"
@@ -482,3 +483,132 @@ func TestUpdatePatchSchedule(t *testing.T) {
482483 })
483484 }
484485}
486+
487+ func TestUpdatePatchScheduleMaintenanceWindow (t * testing.T ) {
488+ refTime := time .Date (2026 , 5 , 24 , 0 , 0 , 0 , 0 , time .UTC )
489+
490+ // Four supported minors with the oldest (1.30) sitting in its
491+ // maintenance window relative to refTime. Each Next.TargetDate is
492+ // after refTime so the per-schedule patch-bump loop short-circuits.
493+ baseSchedule := func () PatchSchedule {
494+ return PatchSchedule {
495+ Schedules : []* Schedule {
496+ {
497+ Release : "1.33" ,
498+ Next : & PatchRelease {
499+ Release : "1.33.1" ,
500+ CherryPickDeadline : "2026-06-05" ,
501+ TargetDate : "2026-06-09" ,
502+ },
503+ EndOfLifeDate : "2027-06-01" ,
504+ MaintenanceModeStartDate : "2027-03-01" ,
505+ },
506+ {
507+ Release : "1.32" ,
508+ Next : & PatchRelease {
509+ Release : "1.32.5" ,
510+ CherryPickDeadline : "2026-06-05" ,
511+ TargetDate : "2026-06-09" ,
512+ },
513+ EndOfLifeDate : "2027-02-01" ,
514+ MaintenanceModeStartDate : "2026-11-01" ,
515+ },
516+ {
517+ Release : "1.31" ,
518+ Next : & PatchRelease {
519+ Release : "1.31.10" ,
520+ CherryPickDeadline : "2026-06-05" ,
521+ TargetDate : "2026-06-09" ,
522+ },
523+ EndOfLifeDate : "2026-10-01" ,
524+ MaintenanceModeStartDate : "2026-08-01" ,
525+ },
526+ {
527+ Release : "1.30" ,
528+ Next : & PatchRelease {
529+ Release : "1.30.15" ,
530+ CherryPickDeadline : "2026-06-05" ,
531+ TargetDate : "2026-06-09" ,
532+ },
533+ EndOfLifeDate : "2026-07-01" ,
534+ MaintenanceModeStartDate : "2026-04-01" ,
535+ },
536+ },
537+ }
538+ }
539+
540+ for _ , tc := range []struct {
541+ name string
542+ confirm func (* Schedule ) (bool , error )
543+ mutate func (PatchSchedule ) PatchSchedule
544+ wantUpcomingCount int
545+ wantErr string
546+ }{
547+ {
548+ name : "overlap accepted - extends upcoming to 4" ,
549+ confirm : func (* Schedule ) (bool , error ) { return true , nil },
550+ wantUpcomingCount : 4 ,
551+ },
552+ {
553+ name : "overlap declined - upcoming stays at 3" ,
554+ confirm : func (* Schedule ) (bool , error ) { return false , nil },
555+ wantUpcomingCount : 3 ,
556+ },
557+ {
558+ name : "TBD maintenance date - prompter never called" ,
559+ confirm : func (* Schedule ) (bool , error ) {
560+ t .Fatal ("confirmExtraUpcoming must not be called when MaintenanceModeStartDate is TBD" )
561+
562+ return false , nil
563+ },
564+ mutate : func (s PatchSchedule ) PatchSchedule {
565+ s .Schedules [3 ].MaintenanceModeStartDate = "TBD"
566+
567+ return s
568+ },
569+ wantUpcomingCount : 3 ,
570+ },
571+ {
572+ name : "prompt error bubbles up" ,
573+ confirm : func (* Schedule ) (bool , error ) { return false , errors .New ("boom" ) },
574+ wantErr : "boom" ,
575+ },
576+ } {
577+ t .Run (tc .name , func (t * testing.T ) {
578+ orig := confirmExtraUpcoming
579+
580+ t .Cleanup (func () {
581+ confirmExtraUpcoming = orig
582+ })
583+
584+ confirmExtraUpcoming = tc .confirm
585+
586+ sched := baseSchedule ()
587+ if tc .mutate != nil {
588+ sched = tc .mutate (sched )
589+ }
590+
591+ scheduleFile , err := os .CreateTemp (t .TempDir (), "schedule-" )
592+ require .NoError (t , err )
593+ require .NoError (t , scheduleFile .Close ())
594+
595+ err = updatePatchSchedule (refTime , sched , EolBranches {}, scheduleFile .Name (), "" )
596+ if tc .wantErr != "" {
597+ require .ErrorContains (t , err , tc .wantErr )
598+
599+ return
600+ }
601+
602+ require .NoError (t , err )
603+
604+ scheduleYamlBytes , err := os .ReadFile (scheduleFile .Name ()) //nolint:gosec // G304 - temp file path is safe
605+ require .NoError (t , err )
606+
607+ patchRes := PatchSchedule {}
608+ require .NoError (t , yaml .UnmarshalStrict (scheduleYamlBytes , & patchRes ))
609+
610+ assert .Len (t , patchRes .UpcomingReleases , tc .wantUpcomingCount )
611+ assert .Len (t , patchRes .Schedules , 4 , "schedules[] count must be unchanged" )
612+ })
613+ }
614+ }
0 commit comments