forked from lei.chen/myxce
Worker (P2.5-5): - Added parse_peers() static helper to src/worker/src/main.c - Switched getopt to getopt_long to support --peers and --ca-cert long options - When --peers is given: parses into xce_cluster_config_t, calls xce_cluster_config_validate(), logs INFO with candidate count/identity/ca_cert - If validation fails: prints clear error and exits(1) - WARN logged when candidates_count > 3 - Backward compatible: no --peers falls back to existing single-controller path Scheduler (P2.5-6): - Added parse_peers() static helper to src/scheduler/src/main.c - Extended argp struct with --peers (-P) and --ca-cert (-C) options - Same validation/logging pattern as worker - Backward compatible: no --peers falls back to existing single-controller path Tests (new): - src/lib/testsuite/test_cluster_parse.c: 8 unit tests for parse_peers logic (single entry, multiple entries, no-name format, empty/null/missing-port errors, name-stripped correctly, MAX_HA_CANDIDATES cap respected) - Registered in test.c and meson.build - All 8 tests GREEN; 3 pre-existing failures in unrelated GC/CSE tests unchanged
160 lines
5.8 KiB
C
160 lines
5.8 KiB
C
/* test_cluster_parse.c — unit tests for the parse_peers() helper used by
|
|
* worker and scheduler main.c (P2.5-5 / P2.5-6).
|
|
*
|
|
* The parser is a static function in the respective main.c files. To keep
|
|
* the test self-contained we duplicate a simplified reference implementation
|
|
* here and test the same contract. Integration with the real binaries is
|
|
* covered by the build (both must compile).
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unity/unity.h>
|
|
|
|
#include "xce/cluster.h"
|
|
|
|
/* ── Reference implementation of parse_peers (mirrors worker/main.c) ─── */
|
|
|
|
static int parse_peers_local(const char* peers_str, xce_cluster_config_t* cfg)
|
|
{
|
|
cfg->candidates_count = 0;
|
|
if (!peers_str || peers_str[0] == '\0')
|
|
return -1;
|
|
|
|
char buf[2048];
|
|
strncpy(buf, peers_str, sizeof(buf) - 1);
|
|
buf[sizeof(buf) - 1] = '\0';
|
|
|
|
char* saveptr;
|
|
char* tok = strtok_r(buf, ",", &saveptr);
|
|
|
|
while (tok && cfg->candidates_count < MAX_HA_CANDIDATES) {
|
|
/* Format: name=host:port OR host:port */
|
|
char* eq = strchr(tok, '=');
|
|
char* addr = eq ? eq + 1 : tok;
|
|
char* colon = strrchr(addr, ':');
|
|
if (!colon)
|
|
return -1;
|
|
*colon = '\0';
|
|
strncpy(cfg->candidates[cfg->candidates_count].hostname, addr,
|
|
XCE_MAXHOSTNAME_LEN - 1);
|
|
cfg->candidates[cfg->candidates_count].hostname[XCE_MAXHOSTNAME_LEN - 1]
|
|
= '\0';
|
|
cfg->candidates[cfg->candidates_count].port = atoi(colon + 1);
|
|
cfg->candidates_count++;
|
|
tok = strtok_r(NULL, ",", &saveptr);
|
|
}
|
|
return cfg->candidates_count > 0 ? 0 : -1;
|
|
}
|
|
|
|
/* ── Test cases ─────────────────────────────────────────────────────── */
|
|
|
|
static void test_parse_peers_single(void)
|
|
{
|
|
xce_cluster_config_t cfg = {0};
|
|
int rc = parse_peers_local("ctrl-1=10.0.1.1:7000", &cfg);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_EQUAL_INT(1, cfg.candidates_count);
|
|
TEST_ASSERT_EQUAL_STRING("10.0.1.1", cfg.candidates[0].hostname);
|
|
TEST_ASSERT_EQUAL_INT(7000, cfg.candidates[0].port);
|
|
printf("test_parse_peers_single: PASS\n");
|
|
}
|
|
|
|
static void test_parse_peers_multiple(void)
|
|
{
|
|
xce_cluster_config_t cfg = {0};
|
|
int rc = parse_peers_local(
|
|
"ctrl-1=10.0.1.1:7000,ctrl-2=10.0.1.2:7001,ctrl-3=10.0.1.3:7002",
|
|
&cfg);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_EQUAL_INT(3, cfg.candidates_count);
|
|
TEST_ASSERT_EQUAL_STRING("10.0.1.2", cfg.candidates[1].hostname);
|
|
TEST_ASSERT_EQUAL_INT(7001, cfg.candidates[1].port);
|
|
TEST_ASSERT_EQUAL_STRING("10.0.1.3", cfg.candidates[2].hostname);
|
|
TEST_ASSERT_EQUAL_INT(7002, cfg.candidates[2].port);
|
|
printf("test_parse_peers_multiple: PASS\n");
|
|
}
|
|
|
|
static void test_parse_peers_no_name(void)
|
|
{
|
|
xce_cluster_config_t cfg = {0};
|
|
int rc = parse_peers_local("10.0.1.1:7000,10.0.1.2:7001", &cfg);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_EQUAL_INT(2, cfg.candidates_count);
|
|
TEST_ASSERT_EQUAL_STRING("10.0.1.1", cfg.candidates[0].hostname);
|
|
TEST_ASSERT_EQUAL_INT(7000, cfg.candidates[0].port);
|
|
TEST_ASSERT_EQUAL_STRING("10.0.1.2", cfg.candidates[1].hostname);
|
|
TEST_ASSERT_EQUAL_INT(7001, cfg.candidates[1].port);
|
|
printf("test_parse_peers_no_name: PASS\n");
|
|
}
|
|
|
|
static void test_parse_peers_empty_returns_error(void)
|
|
{
|
|
xce_cluster_config_t cfg = {0};
|
|
int rc = parse_peers_local("", &cfg);
|
|
TEST_ASSERT_NOT_EQUAL(0, rc);
|
|
printf("test_parse_peers_empty_returns_error: PASS\n");
|
|
}
|
|
|
|
static void test_parse_peers_null_returns_error(void)
|
|
{
|
|
xce_cluster_config_t cfg = {0};
|
|
int rc = parse_peers_local(NULL, &cfg);
|
|
TEST_ASSERT_NOT_EQUAL(0, rc);
|
|
printf("test_parse_peers_null_returns_error: PASS\n");
|
|
}
|
|
|
|
static void test_parse_peers_missing_port_returns_error(void)
|
|
{
|
|
xce_cluster_config_t cfg = {0};
|
|
int rc = parse_peers_local("ctrl-1=10.0.1.1", &cfg); /* no colon:port */
|
|
TEST_ASSERT_NOT_EQUAL(0, rc);
|
|
printf("test_parse_peers_missing_port_returns_error: PASS\n");
|
|
}
|
|
|
|
static void test_parse_peers_first_entry_parsed_correctly(void)
|
|
{
|
|
/* Verify the name= prefix is stripped and only addr:port is stored */
|
|
xce_cluster_config_t cfg = {0};
|
|
int rc = parse_peers_local("primary=192.168.10.1:9000", &cfg);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_EQUAL_STRING("192.168.10.1", cfg.candidates[0].hostname);
|
|
TEST_ASSERT_EQUAL_INT(9000, cfg.candidates[0].port);
|
|
printf("test_parse_peers_first_entry_parsed_correctly: PASS\n");
|
|
}
|
|
|
|
static void test_parse_peers_respects_max_candidates(void)
|
|
{
|
|
/* Build a string with MAX_HA_CANDIDATES + 2 entries; only MAX accepted */
|
|
char big[2048] = "";
|
|
char entry[128] = "";
|
|
int n = MAX_HA_CANDIDATES + 2;
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
snprintf(entry, sizeof(entry), "10.0.1.%d:%d", i + 1, 7000 + i);
|
|
if (i > 0)
|
|
strncat(big, ",", sizeof(big) - strlen(big) - 1);
|
|
strncat(big, entry, sizeof(big) - strlen(big) - 1);
|
|
}
|
|
|
|
xce_cluster_config_t cfg = {0};
|
|
int rc = parse_peers_local(big, &cfg);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_EQUAL_INT(MAX_HA_CANDIDATES, cfg.candidates_count);
|
|
printf("test_parse_peers_respects_max_candidates: PASS\n");
|
|
}
|
|
|
|
/* ── Test group entry point ─────────────────────────────────────────── */
|
|
|
|
void test_cluster_parse(void)
|
|
{
|
|
RUN_TEST(test_parse_peers_single);
|
|
RUN_TEST(test_parse_peers_multiple);
|
|
RUN_TEST(test_parse_peers_no_name);
|
|
RUN_TEST(test_parse_peers_empty_returns_error);
|
|
RUN_TEST(test_parse_peers_null_returns_error);
|
|
RUN_TEST(test_parse_peers_missing_port_returns_error);
|
|
RUN_TEST(test_parse_peers_first_entry_parsed_correctly);
|
|
RUN_TEST(test_parse_peers_respects_max_candidates);
|
|
}
|