myxce/src/controller/testsuite/test_p1_integration.c
Rongsong Shen e31673dd88 chore: checkpoint all prior work for clean state
Captures all uncommitted work from ha-config-bootstrap,
ha-heartbeat-liveness, and associated spec/tooling updates:

- src: ha.c, ha.h, cluster.c, config_repl.c, myapp.c, raft.c —
  heartbeat liveness, RDHUP integration, committing phase fixes
- tests: test_ha_role.c, test_config_repl.c, test_ha_rdhup_integration.c,
  test_p1_integration.c, test_p3_hardening.c, scheduler test
- specs: election-iomgr-refactor, ha-heartbeat-liveness,
  ha-peer-connection-reuse, raft-library (new); ha-config-bootstrap
  and election-iomgr-refactor task/research updates
- tooling: .wolf/ state, ha/data cluster fixtures, raft/raft.tla,
  src/build symlink, xce-build skill update
2026-06-04 13:04:16 +00:00

129 lines
4.3 KiB
C

/* P1-7: HA Foundation integration tests — assembled system */
#include <stdatomic.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unity/unity.h>
#include "ha.h"
/* Test 1: Single-node startup: after ha_init + ha_do_promote(priority=0),
* role must be PRIMARY */
static void test_single_node_starts_as_primary(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 /* priority 0 = highest */);
/* Simulate am_i_primary() decision for priority-0 node */
ha_do_promote(&ha);
TEST_ASSERT_EQUAL_INT(CTRL_PRIMARY,
(int)atomic_load_explicit(&ha.role, memory_order_acquire));
ha_fini(&ha);
printf("test_single_node_starts_as_primary: PASS\n");
}
/* Test 2: Term fencing — promote increments term; second promote increments again */
static void test_term_fencing_monotonic(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 t0 = atomic_load_explicit(&ha.term, memory_order_acquire);
ha_do_promote(&ha);
uint64_t t1 = atomic_load_explicit(&ha.term, memory_order_acquire);
ha_do_demote(&ha);
ha_do_promote(&ha);
uint64_t t2 = atomic_load_explicit(&ha.term, memory_order_acquire);
TEST_ASSERT_TRUE(t1 > t0);
TEST_ASSERT_TRUE(t2 > t1);
ha_fini(&ha);
printf("test_term_fencing_monotonic: PASS\n");
}
/* Test 4: Redirect guard — primary never redirects; standby with known
* primary redirects; standby with no known primary reports nomaster */
static void test_redirect_guard_integration(void)
{
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,
};
/* Primary: no redirect */
ha_state_t ha_primary;
ha_init(&ha_primary, &cfg, 0);
atomic_store_explicit(&ha_primary.role, (int32_t)CTRL_PRIMARY,
memory_order_release);
TEST_ASSERT_EQUAL_INT(0, ha_should_redirect(&ha_primary));
/* Standby with known primary: redirect */
ha_state_t ha_standby;
ha_init(&ha_standby, &cfg, 1);
strncpy(ha_standby.primary_host, "10.0.0.1",
sizeof(ha_standby.primary_host) - 1);
ha_standby.primary_port = 7000;
TEST_ASSERT_EQUAL_INT(1, ha_should_redirect(&ha_standby));
TEST_ASSERT_EQUAL_INT(0, ha_no_primary_known(&ha_standby));
/* Standby with no known primary: no redirect, nomaster */
ha_state_t ha_cold;
ha_init(&ha_cold, &cfg, 2);
TEST_ASSERT_EQUAL_INT(0, ha_should_redirect(&ha_cold));
TEST_ASSERT_EQUAL_INT(1, ha_no_primary_known(&ha_cold));
ha_fini(&ha_primary);
ha_fini(&ha_standby);
ha_fini(&ha_cold);
printf("test_redirect_guard_integration: PASS\n");
}
/* Test 5: Hook idempotency — double-promote does not register hooks twice
* (full hook registration needs a cluster_t — this tests the hooks_registered
* guard) */
static void test_hooks_registered_guard_integration(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);
TEST_ASSERT_EQUAL_INT(0, ha.hooks_registered);
/* Simulate idempotency: hooks_registered = 1 after first promote */
ha.hooks_registered = 1;
/* Second promote should see guard and skip re-registration */
TEST_ASSERT_TRUE(ha.hooks_registered == 1); /* guard still set */
/* Demote resets guard */
ha_do_demote(&ha);
TEST_ASSERT_EQUAL_INT(0, ha.hooks_registered);
ha_fini(&ha);
printf("test_hooks_registered_guard_integration: PASS\n");
}
void test_p1_integration(void)
{
RUN_TEST(test_single_node_starts_as_primary);
RUN_TEST(test_term_fencing_monotonic);
RUN_TEST(test_redirect_guard_integration);
RUN_TEST(test_hooks_registered_guard_integration);
}