feat(controller-peers-env-fallback): add the environment-variable reader for the default candidate list (task 1.1)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-03 05:12:34 +00:00
parent ab21c1622d
commit 7328b06c53
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
4 changed files with 57 additions and 1 deletions

View file

@ -1,7 +1,7 @@
# Implementation Plan
- [ ] 1. Foundation: shared libxce primitives and the regression script
- [ ] 1.1 Add the environment-variable reader for the default candidate list
- [x] 1.1 Add the environment-variable reader for the default candidate list
- Write the failing libxce unit tests first: variable unset gives no value, set to an empty string gives no value, set to a non-empty string returns it unchanged
- Add the reader to the shared cluster module and declare it next to the existing peers parser, so every C daemon and the harness can call it with no new build dependency
- Each test restores the variable to unset afterwards, so later libxce tests are unaffected

View file

@ -137,6 +137,17 @@ void xce_reconnect_fini(xce_reconnect_ctx_t* rctx);
*/
int xce_parse_peers(const char* peers_str, xce_cluster_config_t* cfg);
/**
* xce_controller_peers_env() — default controller candidate list from the
* XCE_CONTROLLER_PEERS environment variable.
*
* Returns the variable's value unchanged (same format as --peers, not parsed
* here) when it is set and non-empty; NULL when it is unset or empty, so an
* empty value behaves exactly like an unset one. The returned pointer is
* owned by the process environment; do not free or modify it.
*/
const char* xce_controller_peers_env(void);
/* Test seam: set to non-NULL to override the real connect (unit tests only) */
typedef void* (*xce_connect_fn_t)(const char* host, int port);
void xce_cluster_set_connect_fn(xce_connect_fn_t fn);

View file

@ -59,6 +59,12 @@ int xce_parse_peers(const char* peers_str, xce_cluster_config_t* cfg)
return cfg->candidates_count > 0 ? 0 : -1;
}
const char* xce_controller_peers_env(void)
{
const char* v = getenv("XCE_CONTROLLER_PEERS");
return (v && v[0] != '\0') ? v : NULL;
}
/* ── Test seam ─────────────────────────────────────────────────── */
static xce_connect_fn_t s_connect_fn = NULL;

View file

@ -1,5 +1,6 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unity/unity.h>
@ -290,6 +291,41 @@ static void test_convert_does_not_set_identity_fields(void)
printf("test_convert_does_not_set_identity_fields: PASS\n");
}
/* ── xce_controller_peers_env() ─────────────────────────────────── */
/* Req 1.2/1.3: an unset variable supplies no candidate list. */
static void test_peers_env_unset_returns_null(void)
{
unsetenv("XCE_CONTROLLER_PEERS");
TEST_ASSERT_NULL(xce_controller_peers_env());
printf("test_peers_env_unset_returns_null: PASS\n");
}
/* Req 1.3/1.7: an empty variable is treated exactly like an unset one. */
static void test_peers_env_empty_returns_null(void)
{
TEST_ASSERT_EQUAL_INT(0, setenv("XCE_CONTROLLER_PEERS", "", 1));
const char* v = xce_controller_peers_env();
unsetenv("XCE_CONTROLLER_PEERS");
TEST_ASSERT_NULL(v);
printf("test_peers_env_empty_returns_null: PASS\n");
}
/* Req 1.2: a non-empty variable is returned unchanged (no parsing here). */
static void test_peers_env_nonempty_returned_unchanged(void)
{
const char* val = "c1=host1:9991,host2:9992";
TEST_ASSERT_EQUAL_INT(0, setenv("XCE_CONTROLLER_PEERS", val, 1));
const char* v = xce_controller_peers_env();
char copy[64] = "";
if (v)
snprintf(copy, sizeof(copy), "%s", v);
unsetenv("XCE_CONTROLLER_PEERS");
TEST_ASSERT_NOT_NULL(v);
TEST_ASSERT_EQUAL_STRING(val, copy);
printf("test_peers_env_nonempty_returned_unchanged: PASS\n");
}
/* ── Test group entry point ─────────────────────────────────────── */
void test_cluster(void)
{
@ -310,4 +346,7 @@ void test_cluster(void)
RUN_TEST(test_convert_rejects_zero_candidates);
RUN_TEST(test_convert_truncates_over_max_candidates);
RUN_TEST(test_convert_does_not_set_identity_fields);
RUN_TEST(test_peers_env_unset_returns_null);
RUN_TEST(test_peers_env_empty_returns_null);
RUN_TEST(test_peers_env_nonempty_returned_unchanged);
}