feat(controller-peers-env-fallback): add the shared "which candidate is me" lookup (task 1.2)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7328b06c53
commit
d4858e1fe9
4 changed files with 131 additions and 1 deletions
|
|
@ -148,6 +148,21 @@ int xce_parse_peers(const char* peers_str, xce_cluster_config_t* cfg);
|
|||
*/
|
||||
const char* xce_controller_peers_env(void);
|
||||
|
||||
/**
|
||||
* xce_peers_find_identity() — the one "which candidate is me" rule.
|
||||
*
|
||||
* A candidate's key is its name when non-empty, otherwise its hostname
|
||||
* (a named candidate is never matched by its hostname). The first
|
||||
* candidate whose key matches name (compared over XCE_MAXNAME_LEN - 1
|
||||
* characters) wins. An empty name is compared like any other, so it
|
||||
* matches an unnamed candidate with an empty hostname.
|
||||
*
|
||||
* Returns the matching candidate's index, or -1 when cfg or name is NULL
|
||||
* or nothing matches. No default is applied; each caller keeps its own
|
||||
* no-match handling.
|
||||
*/
|
||||
int xce_peers_find_identity(const xce_cluster_config_t* cfg, const char* name);
|
||||
|
||||
/* 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);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,20 @@ const char* xce_controller_peers_env(void)
|
|||
return (v && v[0] != '\0') ? v : NULL;
|
||||
}
|
||||
|
||||
int xce_peers_find_identity(const xce_cluster_config_t* cfg, const char* name)
|
||||
{
|
||||
if (!cfg || !name)
|
||||
return -1;
|
||||
for (int i = 0; i < cfg->candidates_count; i++) {
|
||||
const char* key = cfg->candidates[i].name[0]
|
||||
? cfg->candidates[i].name
|
||||
: cfg->candidates[i].hostname;
|
||||
if (strncmp(key, name, XCE_MAXNAME_LEN - 1) == 0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ── Test seam ─────────────────────────────────────────────────── */
|
||||
|
||||
static xce_connect_fn_t s_connect_fn = NULL;
|
||||
|
|
|
|||
|
|
@ -326,6 +326,100 @@ static void test_peers_env_nonempty_returned_unchanged(void)
|
|||
printf("test_peers_env_nonempty_returned_unchanged: PASS\n");
|
||||
}
|
||||
|
||||
/* ── xce_peers_find_identity() ──────────────────────────────────── */
|
||||
|
||||
/* Fill candidate i of cfg directly: name may be "" (unnamed entry),
|
||||
* hostname may be "" (only reachable by filling the struct by hand). */
|
||||
static void set_candidate(xce_cluster_config_t* cfg, int i, const char* name,
|
||||
const char* hostname, int port)
|
||||
{
|
||||
snprintf(cfg->candidates[i].name, XCE_MAXNAME_LEN, "%s", name);
|
||||
snprintf(cfg->candidates[i].hostname, XCE_MAXHOSTNAME_LEN, "%s",
|
||||
hostname);
|
||||
cfg->candidates[i].port = port;
|
||||
}
|
||||
|
||||
/* Req 1.6: a named candidate is found by its name. */
|
||||
static void test_find_identity_named_match(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
set_candidate(&cfg, 0, "c1", "localhost", 9991);
|
||||
set_candidate(&cfg, 1, "c2", "localhost", 9992);
|
||||
set_candidate(&cfg, 2, "c3", "localhost", 9993);
|
||||
cfg.candidates_count = 3;
|
||||
TEST_ASSERT_EQUAL_INT(1, xce_peers_find_identity(&cfg, "c2"));
|
||||
printf("test_find_identity_named_match: PASS\n");
|
||||
}
|
||||
|
||||
/* Req 1.6: an unnamed candidate is found by its hostname. */
|
||||
static void test_find_identity_unnamed_matched_by_hostname(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
set_candidate(&cfg, 0, "", "node1", 9990);
|
||||
set_candidate(&cfg, 1, "", "node2", 9990);
|
||||
cfg.candidates_count = 2;
|
||||
TEST_ASSERT_EQUAL_INT(1, xce_peers_find_identity(&cfg, "node2"));
|
||||
printf("test_find_identity_unnamed_matched_by_hostname: PASS\n");
|
||||
}
|
||||
|
||||
/* Req 1.6: a named candidate's key is its name only -- its hostname
|
||||
* does not identify it. */
|
||||
static void test_find_identity_named_not_matched_by_hostname(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
set_candidate(&cfg, 0, "c1", "node1", 9991);
|
||||
set_candidate(&cfg, 1, "c2", "node2", 9992);
|
||||
cfg.candidates_count = 2;
|
||||
TEST_ASSERT_EQUAL_INT(-1, xce_peers_find_identity(&cfg, "node2"));
|
||||
printf("test_find_identity_named_not_matched_by_hostname: PASS\n");
|
||||
}
|
||||
|
||||
/* Req 1.6: no candidate matches -> -1 (no default applied). */
|
||||
static void test_find_identity_no_match(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
set_candidate(&cfg, 0, "c1", "localhost", 9991);
|
||||
set_candidate(&cfg, 1, "", "node2", 9992);
|
||||
cfg.candidates_count = 2;
|
||||
TEST_ASSERT_EQUAL_INT(-1, xce_peers_find_identity(&cfg, "c9"));
|
||||
printf("test_find_identity_no_match: PASS\n");
|
||||
}
|
||||
|
||||
/* Req 1.6: the first of duplicate names wins (not the last). */
|
||||
static void test_find_identity_first_duplicate_wins(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
set_candidate(&cfg, 0, "c0", "localhost", 9990);
|
||||
set_candidate(&cfg, 1, "dup", "localhost", 9991);
|
||||
set_candidate(&cfg, 2, "dup", "localhost", 9992);
|
||||
cfg.candidates_count = 3;
|
||||
TEST_ASSERT_EQUAL_INT(1, xce_peers_find_identity(&cfg, "dup"));
|
||||
printf("test_find_identity_first_duplicate_wins: PASS\n");
|
||||
}
|
||||
|
||||
/* Req 1.6: no name given -> -1, even against an entry with empty keys. */
|
||||
static void test_find_identity_null_name(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
set_candidate(&cfg, 0, "c1", "localhost", 9991);
|
||||
set_candidate(&cfg, 1, "", "", 9992);
|
||||
cfg.candidates_count = 2;
|
||||
TEST_ASSERT_EQUAL_INT(-1, xce_peers_find_identity(&cfg, NULL));
|
||||
printf("test_find_identity_null_name: PASS\n");
|
||||
}
|
||||
|
||||
/* Req 1.6: an empty name is compared like any other, so it matches an
|
||||
* unnamed empty-hostname entry exactly as the controller's loops do. */
|
||||
static void test_find_identity_empty_name_matches_empty_hostname(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
set_candidate(&cfg, 0, "c1", "localhost", 9991);
|
||||
set_candidate(&cfg, 1, "", "", 9992);
|
||||
cfg.candidates_count = 2;
|
||||
TEST_ASSERT_EQUAL_INT(1, xce_peers_find_identity(&cfg, ""));
|
||||
printf("test_find_identity_empty_name_matches_empty_hostname: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Test group entry point ─────────────────────────────────────── */
|
||||
void test_cluster(void)
|
||||
{
|
||||
|
|
@ -349,4 +443,11 @@ void test_cluster(void)
|
|||
RUN_TEST(test_peers_env_unset_returns_null);
|
||||
RUN_TEST(test_peers_env_empty_returns_null);
|
||||
RUN_TEST(test_peers_env_nonempty_returned_unchanged);
|
||||
RUN_TEST(test_find_identity_named_match);
|
||||
RUN_TEST(test_find_identity_unnamed_matched_by_hostname);
|
||||
RUN_TEST(test_find_identity_named_not_matched_by_hostname);
|
||||
RUN_TEST(test_find_identity_no_match);
|
||||
RUN_TEST(test_find_identity_first_duplicate_wins);
|
||||
RUN_TEST(test_find_identity_null_name);
|
||||
RUN_TEST(test_find_identity_empty_name_matches_empty_hostname);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue