@@ -367,7 +367,7 @@ def test_course_scope_migration_depends_on_override_choice(
367367 )
368368 else :
369369 mock_run .assert_not_called ()
370- mock_logger .info .assert_called_once_with ("No change in waffle flag, skipping migration" )
370+ mock_logger .info .assert_called_once_with ("No effective change in waffle flag behavior , skipping migration" )
371371
372372 @data (
373373 (WAFFLE_OVERRIDE_FORCE_ON , MigrationType .FORWARD , True ),
@@ -402,7 +402,7 @@ def test_org_scope_migration_depends_on_override_choice(
402402 )
403403 else :
404404 mock_run .assert_not_called ()
405- mock_logger .info .assert_called_once_with ("No change in waffle flag, skipping migration" )
405+ mock_logger .info .assert_called_once_with ("No effective change in waffle flag behavior , skipping migration" )
406406
407407 def test_org_scope_passes_excluded_course_ids_when_course_overrides_oppose_org (self , mock_run ):
408408 """Org forward migration excludes active course rows whose override opposes force-on."""
@@ -467,7 +467,7 @@ def test_skips_when_previous_enabled_record_has_same_override_choice(
467467 trigger_course_authoring_migration (sender_model , instance , scope_key )
468468
469469 mock_run .assert_not_called ()
470- mock_logger .info .assert_called_once_with ("No change in waffle flag, skipping migration" )
470+ mock_logger .info .assert_called_once_with ("No effective change in waffle flag behavior , skipping migration" )
471471
472472 @data (
473473 (
@@ -538,76 +538,154 @@ def test_runs_when_previous_record_disabled_even_if_same_override_choice(
538538
539539
540540class TestGetMigrationType (TestCase ):
541- """Tests for ``get_migration_type``."""
541+ """Tests for ``get_migration_type`` (effective state includes ``global_flag_enabled``) ."""
542542
543543 def create_mock_record (self , enabled : bool , choice : str ):
544544 """Helper to create a mock record object."""
545545 return SimpleNamespace (enabled = enabled , override_choice = choice )
546546
547- def test_creation_new_record_enabled (self ):
548- """Case: No previous record, current is enabled and forced on ."""
547+ def test_creation_new_record_enabled_global_off (self ):
548+ """No prior row: enabling FORCE_ON migrates forward when the global flag is off ."""
549549 current = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_ON )
550550
551- result = get_migration_type (current , None )
551+ result = get_migration_type (current , None , global_flag_enabled = False )
552552
553553 self .assertEqual (result , MigrationType .FORWARD )
554554
555- def test_creation_new_record_disabled (self ):
556- """Case: No previous record, current is disabled ."""
557- current = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_ON )
555+ def test_creation_new_record_enabled_global_on (self ):
556+ """No prior row: FORCE_ON is already the effective state when global is on — no migration ."""
557+ current = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_ON )
558558
559- result = get_migration_type (current , None )
559+ result = get_migration_type (current , None , global_flag_enabled = True )
560560
561561 self .assertIsNone (result )
562562
563- def test_no_change_stay_active (self ):
564- """Case: Both records are enabled and forced on (No change)."""
563+ def test_creation_new_record_disabled_matches_global (self ):
564+ """Disabled row defers to global, no previous row means same effective state as global."""
565+ current = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_ON )
566+
567+ self .assertIsNone (get_migration_type (current , None , global_flag_enabled = False ))
568+ self .assertIsNone (get_migration_type (current , None , global_flag_enabled = True ))
569+
570+ def test_no_change_stay_active_force_on (self ):
571+ """Both enabled FORCE_ON — effective stays on."""
565572 prev = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_ON )
566573 curr = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_ON )
567574
568- result = get_migration_type (curr , prev )
569-
570- self .assertIsNone (result )
575+ self .assertIsNone (get_migration_type (curr , prev , global_flag_enabled = False ))
576+ self .assertIsNone (get_migration_type (curr , prev , global_flag_enabled = True ))
571577
578+ def test_no_change_stay_active_force_off (self ):
579+ """Both enabled FORCE_OFF — effective stays off."""
572580 prev = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_OFF )
573581 curr = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_OFF )
574582
575- result = get_migration_type (curr , prev )
576-
577- self .assertIsNone (result )
583+ self .assertIsNone (get_migration_type (curr , prev , global_flag_enabled = False ))
584+ self .assertIsNone (get_migration_type (curr , prev , global_flag_enabled = True ))
578585
579586 def test_no_change_stay_inactive (self ):
580- """Case: Both records are disabled (No change) ."""
587+ """Both rows disabled — both follow global, so no effective change."""
581588 prev = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_ON )
582589 curr = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_ON )
583590
584- result = get_migration_type (curr , prev )
591+ self .assertIsNone (get_migration_type (curr , prev , global_flag_enabled = False ))
592+ self .assertIsNone (get_migration_type (curr , prev , global_flag_enabled = True ))
585593
586- self .assertIsNone (result )
587-
588- def test_transition_to_forward (self ):
589- """Case: Transition from disabled to enabled/forced on."""
594+ def test_transition_disabled_to_enabled_force_on_global_off (self ):
595+ """Row becomes active FORCE_ON while global is off — effective off → on."""
590596 prev = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_ON )
591597 curr = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_ON )
592598
593- result = get_migration_type (curr , prev )
599+ result = get_migration_type (curr , prev , global_flag_enabled = False )
594600
595601 self .assertEqual (result , MigrationType .FORWARD )
596602
597- def test_transition_to_rollback (self ):
598- """Case: Transition from enabled/forced on to disabled."""
603+ def test_transition_disabled_to_enabled_force_on_global_on (self ):
604+ """Previously inactive row followed global (on), turning FORCE_ON on stays on — no op."""
605+ prev = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_ON )
606+ curr = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_ON )
607+
608+ result = get_migration_type (curr , prev , global_flag_enabled = True )
609+
610+ self .assertIsNone (result )
611+
612+ def test_transition_enabled_force_on_to_disabled_global_off (self ):
613+ """FORCE_ON row disabled, global off — effective on → off (rollback)."""
599614 prev = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_ON )
600615 curr = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_ON )
601616
602- result = get_migration_type (curr , prev )
617+ result = get_migration_type (curr , prev , global_flag_enabled = False )
603618
604619 self .assertEqual (result , MigrationType .ROLLBACK )
605620
606- def test_change_choice_to_rollback (self ):
607- """Case: Enabled remains True, but choice changes from forced to something else."""
621+ def test_transition_enabled_force_on_to_disabled_global_on (self ):
622+ """FORCE_ON row disabled but global still on — effective stays on, no migration."""
623+ prev = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_ON )
624+ curr = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_ON )
625+
626+ result = get_migration_type (curr , prev , global_flag_enabled = True )
627+
628+ self .assertIsNone (result )
629+
630+ def test_change_choice_force_on_to_force_off (self ):
631+ """Enabled FORCE_ON → FORCE_OFF — effective on → off."""
608632 prev = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_ON )
609633 curr = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_OFF )
610634
611- result = get_migration_type (curr , prev )
635+ self .assertEqual (
636+ get_migration_type (curr , prev , global_flag_enabled = False ),
637+ MigrationType .ROLLBACK ,
638+ )
639+ self .assertEqual (
640+ get_migration_type (curr , prev , global_flag_enabled = True ),
641+ MigrationType .ROLLBACK ,
642+ )
643+
644+ def test_change_choice_force_off_to_force_on (self ):
645+ """Enabled FORCE_OFF → FORCE_ON — effective off → on."""
646+ prev = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_OFF )
647+ curr = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_ON )
648+
649+ self .assertEqual (
650+ get_migration_type (curr , prev , global_flag_enabled = False ),
651+ MigrationType .FORWARD ,
652+ )
653+ self .assertEqual (
654+ get_migration_type (curr , prev , global_flag_enabled = True ),
655+ MigrationType .FORWARD ,
656+ )
657+
658+ def test_remove_force_off_override_when_global_on (self ):
659+ """Deleting FORCE_OFF behavior by disabling row restores global on — forward migration."""
660+ prev = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_OFF )
661+ curr = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_OFF )
662+
663+ result = get_migration_type (curr , prev , global_flag_enabled = True )
664+
665+ self .assertEqual (result , MigrationType .FORWARD )
666+
667+ def test_remove_force_off_override_when_global_off (self ):
668+ """Disabling FORCE_OFF row while global off — still off, no migration."""
669+ prev = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_OFF )
670+ curr = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_OFF )
671+
672+ result = get_migration_type (curr , prev , global_flag_enabled = False )
673+
674+ self .assertIsNone (result )
675+
676+ def test_add_force_off_override_when_global_on (self ):
677+ """New active FORCE_OFF while global on — effective on → off."""
678+ prev = self .create_mock_record (False , WAFFLE_OVERRIDE_FORCE_OFF )
679+ curr = self .create_mock_record (True , WAFFLE_OVERRIDE_FORCE_OFF )
680+
681+ result = get_migration_type (curr , prev , global_flag_enabled = True )
612682
613683 self .assertEqual (result , MigrationType .ROLLBACK )
684+
685+ def test_unknown_override_choice_follows_global (self ):
686+ """Non on/off choice falls back to global — toggling only matters vs that baseline."""
687+ prev = self .create_mock_record (True , "unset" )
688+ curr = self .create_mock_record (True , "unset" )
689+
690+ self .assertIsNone (get_migration_type (curr , prev , global_flag_enabled = False ))
691+ self .assertIsNone (get_migration_type (curr , prev , global_flag_enabled = True ))
0 commit comments