feat: wire ha_state_t into cluster_t + HA startup/shutdown lifecycle (P1-5)
This commit is contained in:
parent
652b015458
commit
c627f69d50
6 changed files with 241 additions and 13 deletions
|
|
@ -20,6 +20,7 @@ extern void test_e2e_hooks(void);
|
|||
extern void test_e2e_gc(void);
|
||||
extern void test_e2e_capnp(void);
|
||||
extern void test_ha_role(void);
|
||||
extern void test_ha_cluster(void);
|
||||
|
||||
void setUp(void) { return; }
|
||||
|
||||
|
|
@ -109,6 +110,7 @@ int main(int argc, char* argv[])
|
|||
TEST(test_e2e_gc);
|
||||
TEST(test_e2e_capnp);
|
||||
TEST(test_ha_role);
|
||||
TEST(test_ha_cluster);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
148
src/controller/testsuite/test_ha_cluster.c
Normal file
148
src/controller/testsuite/test_ha_cluster.c
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* test_ha_cluster.c — P1-5: structural tests for ha_state_t embedded in cluster_t
|
||||
*
|
||||
* These tests validate that:
|
||||
* 1. cluster_t has an ha_state_t ha field (compile-time structural check).
|
||||
* 2. ha_promote_to_primary() and ha_demote_to_standby() use c->ha (not no-ops).
|
||||
*
|
||||
* Tests do NOT call cluster_init() — that requires a live config/SSL/network stack.
|
||||
* Instead they manually zero-init cluster_t and call ha_init() on c.ha directly.
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "ha.h"
|
||||
#include "xce/cluster_def.h"
|
||||
|
||||
/* Valid HA config shared across tests */
|
||||
static ha_config_t g_cfg = {
|
||||
.heartbeat_interval_ms = 3000,
|
||||
.heartbeat_timeout_ms = 10000,
|
||||
.election_timeout_ms = 2000,
|
||||
.probe_connect_timeout_ms = 500,
|
||||
.max_promotion_retries = 5,
|
||||
};
|
||||
|
||||
/*
|
||||
* test_cluster_t_has_ha_field:
|
||||
* Compile-time structural check — cluster_t must embed ha_state_t as field `ha`.
|
||||
* If `c.ha` does not exist, this file will not compile → RED.
|
||||
*/
|
||||
static void test_cluster_t_has_ha_field(void)
|
||||
{
|
||||
cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
|
||||
/* Access the field — compile error if absent */
|
||||
(void)atomic_load_explicit(&c.ha.role, memory_order_acquire);
|
||||
|
||||
printf("test_cluster_t_has_ha_field: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* test_ha_promote_to_primary_uses_c_ha:
|
||||
* After ha_init(&c.ha, ...) and calling ha_promote_to_primary(&c), the role
|
||||
* stored in c.ha must be CTRL_PRIMARY (not still CTRL_STANDBY).
|
||||
* This would fail if ha_promote_to_primary() is still the (void)c stub.
|
||||
*/
|
||||
static void test_ha_promote_to_primary_uses_c_ha(void)
|
||||
{
|
||||
cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
|
||||
/* Manually initialise only the HA field — no cluster_init() required */
|
||||
TEST_ASSERT_EQUAL_INT(0, ha_init(&c.ha, &g_cfg, 0 /* my_priority = 0 → primary */));
|
||||
TEST_ASSERT_EQUAL_INT(CTRL_STANDBY,
|
||||
(int)atomic_load_explicit(&c.ha.role, memory_order_acquire));
|
||||
|
||||
/* c.hooksys is zeroed — hooks_registered guard prevents xce_add_hook() call */
|
||||
ha_promote_to_primary(&c);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(CTRL_PRIMARY,
|
||||
(int)atomic_load_explicit(&c.ha.role, memory_order_acquire));
|
||||
|
||||
ha_fini(&c.ha);
|
||||
printf("test_ha_promote_to_primary_uses_c_ha: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* test_ha_demote_to_standby_uses_c_ha:
|
||||
* Promote then demote. Role must end as CTRL_STANDBY.
|
||||
*/
|
||||
static void test_ha_demote_to_standby_uses_c_ha(void)
|
||||
{
|
||||
cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, ha_init(&c.ha, &g_cfg, 0));
|
||||
|
||||
ha_promote_to_primary(&c);
|
||||
TEST_ASSERT_EQUAL_INT(CTRL_PRIMARY,
|
||||
(int)atomic_load_explicit(&c.ha.role, memory_order_acquire));
|
||||
|
||||
ha_demote_to_standby(&c);
|
||||
TEST_ASSERT_EQUAL_INT(CTRL_STANDBY,
|
||||
(int)atomic_load_explicit(&c.ha.role, memory_order_acquire));
|
||||
|
||||
ha_fini(&c.ha);
|
||||
printf("test_ha_demote_to_standby_uses_c_ha: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* test_ha_promote_increments_term_via_cluster:
|
||||
* Each ha_promote_to_primary() call must increment the election term stored
|
||||
* in c.ha.term, because ha_do_promote() is called inside.
|
||||
*/
|
||||
static void test_ha_promote_increments_term_via_cluster(void)
|
||||
{
|
||||
cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, ha_init(&c.ha, &g_cfg, 0));
|
||||
uint64_t term0 = atomic_load_explicit(&c.ha.term, memory_order_acquire);
|
||||
|
||||
ha_promote_to_primary(&c);
|
||||
uint64_t term1 = atomic_load_explicit(&c.ha.term, memory_order_acquire);
|
||||
TEST_ASSERT_TRUE(term1 > term0);
|
||||
|
||||
ha_fini(&c.ha);
|
||||
printf("test_ha_promote_increments_term_via_cluster: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* test_hooks_registered_guard_prevents_double_register:
|
||||
* When hooksys is zeroed (no real hook system) and hooks_registered is 0,
|
||||
* ha_promote_to_primary() must set hooks_registered = 1 so a second
|
||||
* call skips the registration path.
|
||||
*/
|
||||
static void test_hooks_registered_guard_prevents_double_register(void)
|
||||
{
|
||||
cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, ha_init(&c.ha, &g_cfg, 0));
|
||||
TEST_ASSERT_EQUAL_INT(0, c.ha.hooks_registered);
|
||||
|
||||
ha_promote_to_primary(&c);
|
||||
/* hooks_registered must now be 1 (idempotency guard set) */
|
||||
TEST_ASSERT_EQUAL_INT(1, c.ha.hooks_registered);
|
||||
|
||||
/* Second promote: must not crash even with zeroed hooksys */
|
||||
ha_promote_to_primary(&c);
|
||||
TEST_ASSERT_EQUAL_INT(1, c.ha.hooks_registered);
|
||||
|
||||
ha_fini(&c.ha);
|
||||
printf("test_hooks_registered_guard_prevents_double_register: PASS\n");
|
||||
}
|
||||
|
||||
void test_ha_cluster(void)
|
||||
{
|
||||
RUN_TEST(test_cluster_t_has_ha_field);
|
||||
RUN_TEST(test_ha_promote_to_primary_uses_c_ha);
|
||||
RUN_TEST(test_ha_demote_to_standby_uses_c_ha);
|
||||
RUN_TEST(test_ha_promote_increments_term_via_cluster);
|
||||
RUN_TEST(test_hooks_registered_guard_prevents_double_register);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue