#include #include #include #include #include "ha.h" /* P3-6: Manual failover shim tests. * * Validates the ha_trigger_manual_failover_for_test() shim: * - Role transitions from PRIMARY → STANDBY → PRIMARY * - Term is monotonically increasing across multiple cycles * - hooks_registered is reset after demotion */ static void test_manual_failover_transitions_role_and_increments_term(void) { ha_state_t ha; ha_config_t cfg = { .heartbeat_interval_ms = 3000, .heartbeat_timeout_ms = 10000, .election_timeout_ms = 2000, .probe_connect_timeout_ms = 500, .max_promotion_retries = 5, }; ha_init(&ha, &cfg, 0); /* Start as primary */ ha_do_promote(&ha); uint64_t term_before = atomic_load_explicit(&ha.term, memory_order_acquire); TEST_ASSERT_EQUAL_INT(CTRL_PRIMARY, (int)atomic_load_explicit(&ha.role, memory_order_acquire)); /* Manual failover: demote then re-promote via shim */ uint64_t new_term = ha_trigger_manual_failover_for_test(&ha); /* Term must have incremented (new election) */ TEST_ASSERT_TRUE(new_term > term_before); /* Role must end as PRIMARY (re-promoted) */ TEST_ASSERT_EQUAL_INT(CTRL_PRIMARY, (int)atomic_load_explicit(&ha.role, memory_order_acquire)); ha_fini(&ha); printf("test_manual_failover_transitions_role_and_increments_term: PASS\n"); } static void test_manual_failover_resets_hooks_registered(void) { ha_state_t ha; ha_config_t cfg = { .heartbeat_interval_ms = 3000, .heartbeat_timeout_ms = 10000, .election_timeout_ms = 2000, .probe_connect_timeout_ms = 500, .max_promotion_retries = 5, }; ha_init(&ha, &cfg, 0); ha_do_promote(&ha); ha.hooks_registered = 1; /* Simulate hooks registered */ ha_trigger_manual_failover_for_test(&ha); /* After demote+re-promote: hooks_registered was cleared by demote. * Demotion ALWAYS resets hooks_registered to 0. */ TEST_ASSERT_EQUAL_INT(CTRL_PRIMARY, (int)atomic_load_explicit(&ha.role, memory_order_acquire)); /* Verify that demote reset the flag (no real cluster to re-register with) */ TEST_ASSERT_EQUAL_INT(0, ha.hooks_registered); ha_fini(&ha); printf("test_manual_failover_resets_hooks_registered: PASS\n"); } static void test_manual_failover_monotonic_term_across_multiple_cycles(void) { ha_state_t ha; ha_config_t cfg = { .heartbeat_interval_ms = 3000, .heartbeat_timeout_ms = 10000, .election_timeout_ms = 2000, .probe_connect_timeout_ms = 500, .max_promotion_retries = 5, }; ha_init(&ha, &cfg, 0); uint64_t prev_term = 0; for (int i = 0; i < 5; i++) { uint64_t new_term = ha_trigger_manual_failover_for_test(&ha); TEST_ASSERT_TRUE(new_term > prev_term); prev_term = new_term; } TEST_ASSERT_EQUAL_INT(CTRL_PRIMARY, (int)atomic_load_explicit(&ha.role, memory_order_acquire)); ha_fini(&ha); printf("test_manual_failover_monotonic_term_across_multiple_cycles: PASS\n"); } void test_p3_manual_failover(void) { RUN_TEST(test_manual_failover_transitions_role_and_increments_term); RUN_TEST(test_manual_failover_resets_hooks_registered); RUN_TEST(test_manual_failover_monotonic_term_across_multiple_cycles); }