@@ -814,6 +814,81 @@ TEST_F(RaftReplDevTest, ReconcileLeader) {
814814 g_helper->sync_for_cleanup_start ();
815815}
816816
817+ TEST_F (RaftReplDevTest, NuraftStateTransition) {
818+ LOGINFO (" Homestore replica={} setup completed" , g_helper->replica_num ());
819+ g_helper->sync_for_test_start ();
820+ // Get the RaftReplDev instance
821+ auto repl_dev = std::dynamic_pointer_cast< RaftReplDev >(dbs_[0 ]->repl_dev ());
822+ ASSERT_NE (repl_dev, nullptr );
823+ LOGINFO (" Step 0: Got RaftReplDev instance for group_id={}" , repl_dev->group_id_str ());
824+
825+ // Step 1: Manually set legacy "state" field (JSON object format) to simulate old version data
826+ LOGINFO (" Step 1: Setting legacy JSON object format state" );
827+ auto & js = *(repl_dev->m_raft_config_sb );
828+ // Clear any existing nuraft_state to ensure we test legacy format
829+ if (js.contains (" nuraft_state" )) { js.erase (" nuraft_state" ); }
830+ // Set legacy state
831+ js[" state" ] =
832+ nlohmann::json{{" term" , 100 }, {" voted_for" , 5 }, {" election_timer_allowed" , true }, {" catching_up" , false }};
833+ repl_dev->m_raft_config_sb .write ();
834+ LOGINFO (" Step 1: Written legacy state - term=100, voted_for=5, election_timer_allowed=true, catching_up=false" );
835+
836+ // Step 2: Read old state from legacy format
837+ g_helper->sync_for_verify_start ();
838+ LOGINFO (" Step 2: Reading state from legacy JSON object format" );
839+ auto old_state = repl_dev->read_state ();
840+ ASSERT_NE (old_state, nullptr );
841+ ASSERT_EQ (old_state->get_term (), 100 );
842+ ASSERT_EQ (old_state->get_voted_for (), 5 );
843+ ASSERT_EQ (old_state->is_election_timer_allowed (), true );
844+ ASSERT_EQ (old_state->is_catching_up (), false );
845+ // receiving_snapshot should be false (not in legacy format)
846+ ASSERT_EQ (old_state->is_receiving_snapshot (), false );
847+ LOGINFO (" Step 2: Successfully read legacy state - term={}, voted_for={}, election_timer_allowed={}, "
848+ " catching_up={}, receiving_snapshot={}" ,
849+ old_state->get_term (), old_state->get_voted_for (), old_state->is_election_timer_allowed (),
850+ old_state->is_catching_up (), old_state->is_receiving_snapshot ());
851+
852+ // Step 3: Update state and save in new binary format
853+ g_helper->sync_for_test_start ();
854+ LOGINFO (" Step 3: Updating state values and saving in binary format" );
855+ old_state->set_term (150 );
856+ old_state->set_voted_for (10 );
857+ old_state->allow_election_timer (false );
858+ old_state->set_catching_up (true );
859+ old_state->set_receiving_snapshot (true ); // new field
860+ LOGINFO (" Step 3: Saving new state - term=150, voted_for=10, election_timer_allowed=false, "
861+ " catching_up=true, receiving_snapshot=true" );
862+ repl_dev->save_state (*old_state);
863+
864+ // Step 4: Verify fields in JSON superblock
865+ LOGINFO (" Step 4: Verifying JSON superblock fields" );
866+ g_helper->sync_for_verify_start ();
867+ js = *(repl_dev->m_raft_config_sb );
868+ ASSERT_TRUE (js.contains (" nuraft_state" ));
869+ ASSERT_TRUE (js[" nuraft_state" ].is_array ());
870+ LOGINFO (" Step 4: Confirmed 'nuraft_state' field exists and is array (size={} bytes)" , js[" nuraft_state" ].size ());
871+
872+ // Verify legacy "state" field is empty (not removed, but set to null/empty for rollback compatibility)
873+ ASSERT_TRUE (js[" state" ].empty () || js[" state" ].is_null ());
874+ LOGINFO (" Step 4: Confirmed legacy 'state' field is empty/null for rollback compatibility" );
875+
876+ // Step 5: Read back and verify new state from binary format
877+ LOGINFO (" Step 5: Reading back state from binary format (nuraft_state field)" );
878+ auto new_state = repl_dev->read_state ();
879+ ASSERT_NE (new_state, nullptr );
880+ ASSERT_EQ (new_state->get_term (), 150 );
881+ ASSERT_EQ (new_state->get_voted_for (), 10 );
882+ ASSERT_EQ (new_state->is_election_timer_allowed (), false );
883+ ASSERT_EQ (new_state->is_catching_up (), true );
884+ ASSERT_EQ (new_state->is_receiving_snapshot (), true );
885+ LOGINFO (" Step 5: Successfully read new state - term={}, voted_for={}, election_timer_allowed={}, "
886+ " catching_up={}, receiving_snapshot={}" ,
887+ new_state->get_term (), new_state->get_voted_for (), new_state->is_election_timer_allowed (),
888+ new_state->is_catching_up (), new_state->is_receiving_snapshot ());
889+ g_helper->sync_for_cleanup_start ();
890+ }
891+
817892int main (int argc, char * argv[]) {
818893 int parsed_argc = argc;
819894 char ** orig_argv = argv;
0 commit comments