148 lines
4.6 KiB
C
148 lines
4.6 KiB
C
/*
|
|
* 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);
|
|
}
|