/* test_ha_bootstrap_support.c — Test fixture for bootstrap probe injection. * * This file is compiled into the controller_unit_test binary under XCE_TESTING. * It installs a mock probe function via __attribute__((constructor)) before any * test runs, so that test_ha_bootstrap.c tests do not need real network peers. * * Mock probe behaviour (keyed by port): * port 59901 → tag = 0xDEAD0001 (non-zero: peer has a config) * port 59902 → tag = 0 (zero: peer has no config) * port 59900 → tag = 0 (zero: peer has no config) * any other → tag = 0 (zero: treat as unconfigured) * * Requirements: 3.2 (any peer tag>0 → defer), 3.3 (all-zero → no defer) */ #ifdef XCE_TESTING #include #include "config_repl.h" static int mock_xce_probe_config_tag(const char *host, uint16_t port, uint64_t *tag_out) { (void)host; /* Port 59901: simulate a peer that already has a config (non-zero tag) */ if (port == 59901) { *tag_out = 0xDEAD000100000001ULL; return 0; } /* All other test ports return tag=0 (unconfigured peer) */ *tag_out = 0; return 0; } __attribute__((constructor)) static void install_bootstrap_probe_mock(void) { xce_probe_config_tag_hook = mock_xce_probe_config_tag; } /* ── Stub: bootstrap_config_if_needed ─────────────────────────────────────── * * The production implementation was removed in task 5.3 (Req 1.1, 9.2). * This stub satisfies the linker for test_ha_bootstrap.c which still exercises * the bootstrap contracts via the hook seam above. * * Behaviour mirrors the simplified production logic: if config_tag is already * non-zero, return immediately; otherwise set defer_config_fetch and return 0. */ #include "cluster.h" int bootstrap_config_if_needed(cluster_t *c) { if (c->myconf->config_version_tag != 0) return 0; c->ha.defer_config_fetch = true; return 0; } /* ── Stub: am_i_primary_quorum ────────────────────────────────────────────── * * The production implementation (blocking pthread election) was removed in * task 5.3 (Req 1.1, 9.2). This stub satisfies the linker for * test_ha_election.c tests that still exercise election contracts via * ha_elect_evaluate_and_signal directly (peers==NULL path). * * Single-node (n_peers==1): self-elect, bump term, return CTRL_PRIMARY. * Multi-node without peers (peers==NULL, unit-test mode): allocate * ha_elect_ctx_t, call ha_elect_evaluate_and_signal, free ctx. * Multi-node with peers != NULL is not exercised in unit tests. */ #include #include #include #include "ha.h" #include "xce/cluster_def.h" ctrl_role_t am_i_primary_quorum(ha_state_t *ha, int n_peers, xce_candidate_t **peers, uint64_t my_config_tag, uint64_t my_lsn, const char *my_name) { (void)peers; (void)my_name; atomic_fetch_add_explicit(&ha->term, 1, memory_order_acq_rel); if (n_peers == 1) return CTRL_PRIMARY; int n_other = n_peers - 1; ha_elect_ctx_t *ctx = calloc(1, sizeof(*ctx) + (size_t)n_other * sizeof(ctx->votes[0])); if (!ctx) return CTRL_STANDBY; ctx->n_peers = n_other; ctx->votes_granted = 1; ctx->c = NULL; if (n_other > 0) { ctx->votes[0].my_tag = my_config_tag; ctx->votes[0].my_lsn = my_lsn; } pthread_mutex_init(&ctx->lock, NULL); sem_init(&ctx->done, 0, 0); ctrl_role_t role = ha_elect_evaluate_and_signal(ha, ctx); pthread_mutex_destroy(&ctx->lock); sem_destroy(&ctx->done); free(ctx); return role; } #endif /* XCE_TESTING */