add kiro skills

This commit is contained in:
Rongsong Shen 2026-05-21 03:41:57 +00:00
parent e5fde7b55f
commit 86fe35e671
53 changed files with 8406 additions and 166 deletions

View file

@ -0,0 +1,534 @@
/*
* test_worker_join.c — TDD tests for worker join refactor
*
* Task 1.1: Failing tests for apply_join_state (shared status/resource update)
* Task 1.2: Failing tests for init_worker (first-time join path)
* Task 1.3: Failing tests for rejoin_worker (reconnect update with admin-bit preservation)
*
* RED phase: apply_join_state / init_worker / rejoin_worker are stubs that
* return 0 without modifying state, so all assertions below fail until
* Tasks 2–3 provide real implementations.
*/
/* rtti.h must come first — it defines 'var' and 'typeinfo_t' used by kvmgr.h */
#include <rtti.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unity/unity.h>
#include "cluster.h"
#include "kvmgr.h"
#include "resobj.h"
#include "wobj.h"
#include "worker.h"
#include "xce/cluster_def.h"
#include "xce/types.h"
#include "xstring.h"
#include "zarray.h"
/* ── Stubs for controller functions not under test ───────────────────── */
/*
* collect_confvars is called by on_worker_join → worker_push_config.
* Stubbed here so worker.c links without pulling in the full dataex.c.
*/
void collect_confvars(cluster_t* c, collect_check_fn_t fn, xlist_t* l)
{
(void)c;
(void)fn;
(void)l;
}
/* ── Forward declarations for functions under test ──────────────────── */
/*
* apply_join_state is non-static in worker.c when XCE_TESTING is defined.
*
* RED: the current stub body does nothing and returns 0, so assertions
* that check the modified state will fail.
*/
int apply_join_state(cluster_t* c, worker_t* w, endpoint_t* ep,
xce_hostconfig_t* hconfig);
/*
* init_worker and rejoin_worker are exported from worker.c.
*
* RED: stubs return 0 without registering or modifying any entry.
*/
int init_worker(cluster_t* c, endpoint_t* ep, const char* host,
hostattrs_t* attrs, xce_hostconfig_t* hconfig);
int rejoin_worker(cluster_t* c, worker_t* w, endpoint_t* ep,
hostattrs_t* attrs, xce_hostconfig_t* hconfig);
/* ── Test fixture ────────────────────────────────────────────────────── */
/* Resource names and value types matching add_hostattrs_resvals in worker.c */
#define N_RES 9
static const char* g_res_names[N_RES] = {
"type", "model", "cpuf", "maxcpus", "maxmem",
"maxswap", "maxtmp", "ndisks", "slots"
};
static const int g_res_valtypes[N_RES] = {
XCE_VAL_STRING, XCE_VAL_STRING,
XCE_VAL_NUMERIC, XCE_VAL_NUMERIC, XCE_VAL_NUMERIC,
XCE_VAL_NUMERIC, XCE_VAL_NUMERIC, XCE_VAL_NUMERIC,
XCE_VAL_NUMERIC
};
typedef struct {
cluster_t c;
var workermgr;
var resmgr;
} test_cluster_t;
static test_cluster_t g_tc;
static void setup_test_cluster(void)
{
kvmgr_params_t str_params = {
.key_type = KVMGR_KEY_STRING,
.nslots = 64,
.delta = 16,
.datakiller = NULL,
};
memset(&g_tc, 0, sizeof(g_tc));
g_tc.workermgr = makeInstance(&xkvmgr_type, &str_params);
TEST_ASSERT_NOT_NULL(g_tc.workermgr);
g_tc.resmgr = makeInstance(&xkvmgr_type, &str_params);
TEST_ASSERT_NOT_NULL(g_tc.resmgr);
for (int i = 0; i < N_RES; i++) {
xce_resource_t rdef;
memset(&rdef, 0, sizeof(rdef));
rdef.name = (char*)g_res_names[i];
rdef.valtype = g_res_valtypes[i];
rdef.flags = 0;
rdef.idx = -1;
xce_resource_t* r = makeResource(g_tc.resmgr, &rdef);
TEST_ASSERT_NOT_NULL_MESSAGE(r, g_res_names[i]);
}
g_tc.c.workermgr = g_tc.workermgr;
g_tc.c.resmgr = g_tc.resmgr;
zarray_init(&g_tc.c.sharedinst, 64, NULL);
}
static void teardown_test_cluster(void)
{
zarray_fini(&g_tc.c.sharedinst);
if (g_tc.resmgr) dereference(g_tc.resmgr);
if (g_tc.workermgr) dereference(g_tc.workermgr);
memset(&g_tc, 0, sizeof(g_tc));
}
/* Create a worker in the test workermgr; attrs strings must be makeString'd. */
static worker_t* make_test_worker(const char* name, hostattrs_t* attrs)
{
xce_worker_t host;
memset(&host, 0, sizeof(host));
host.name = (char*)name;
host.attrs = attrs;
return makeWorker(g_tc.workermgr, &host);
}
/* Retrieve a numeric resource value from a worker's resvals by resource name */
static float get_worker_res_fval(worker_t* w, const char* name)
{
xce_resource_t* r = NULL;
lock(g_tc.c.resmgr, RTTI_LOCK_READ);
key_find(g_tc.c.resmgr, (void*)name, (void**)&r);
unlock(g_tc.c.resmgr);
if (r == NULL || r->idx < 0 || r->idx >= w->host.resvals.caps)
return -1.0f;
xce_val_t* v = (xce_val_t*)w->host.resvals.data[r->idx];
return (v != NULL) ? v->u.fvalue : -1.0f;
}
/* ── Task 1.1: Tests for apply_join_state ──────────────────────────── */
/*
* After apply_join_state, XCE_WORKER_UNAVAIL must be cleared. Req 4.1
*/
static void test_apply_join_state_clears_unavail(void)
{
setup_test_cluster();
hostattrs_t attrs;
memset(&attrs, 0, sizeof(attrs));
attrs.hosttype = makeString("LINUX");
attrs.hostmodel = makeString("x86");
attrs.cpufactor = 1.0f;
attrs.maxcpus = 4;
attrs.maxmem = 16;
attrs.maxswap = 32;
attrs.maxtmp = 64;
attrs.ndisks = 1;
worker_t* w = make_test_worker("host-ajs-1", &attrs);
TEST_ASSERT_NOT_NULL(w);
lock(w, RTTI_LOCK_WRITE);
w->host.status |= XCE_WORKER_UNAVAIL;
unlock(w);
xce_hostconfig_t hconfig;
memset(&hconfig, 0, sizeof(hconfig));
hconfig.hosttype = (char*)"!"; /* no type override */
hconfig.hostmodel = (char*)"!"; /* no model override */
hconfig.mxj = 2;
endpoint_t ep;
memset(&ep, 0, sizeof(ep));
lock(w, RTTI_LOCK_WRITE);
int rc = apply_join_state(&g_tc.c, w, &ep, &hconfig);
unlock(w);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_FALSE_MESSAGE(w->host.status & XCE_WORKER_UNAVAIL,
"UNAVAIL must be cleared after apply_join_state");
teardown_test_cluster();
printf("test_apply_join_state_clears_unavail: PASS\n");
}
/*
* When hconfig provides non-"!" overrides, hosttype and hostmodel must
* match those values after apply_join_state. Req 4.2
*/
static void test_apply_join_state_applies_hosttype_override(void)
{
setup_test_cluster();
hostattrs_t attrs;
memset(&attrs, 0, sizeof(attrs));
attrs.hosttype = makeString("OLD_TYPE");
attrs.hostmodel = makeString("OLD_MODEL");
attrs.cpufactor = 1.0f;
attrs.maxcpus = 2;
attrs.maxmem = 8;
attrs.maxswap = 16;
attrs.maxtmp = 32;
attrs.ndisks = 1;
worker_t* w = make_test_worker("host-ajs-2", &attrs);
TEST_ASSERT_NOT_NULL(w);
xce_hostconfig_t hconfig;
memset(&hconfig, 0, sizeof(hconfig));
hconfig.hosttype = (char*)"LINUX64";
hconfig.hostmodel = (char*)"x86_64";
hconfig.mxj = 4;
endpoint_t ep;
memset(&ep, 0, sizeof(ep));
lock(w, RTTI_LOCK_WRITE);
int rc = apply_join_state(&g_tc.c, w, &ep, &hconfig);
unlock(w);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_STRING_MESSAGE("LINUX64", w->host.attrs->hosttype,
"hosttype must be overridden from hconfig");
TEST_ASSERT_EQUAL_STRING_MESSAGE("x86_64", w->host.attrs->hostmodel,
"hostmodel must be overridden from hconfig");
teardown_test_cluster();
printf("test_apply_join_state_applies_hosttype_override: PASS\n");
}
/*
* After apply_join_state, 'slots' equals hconfig.mxj and numeric host-attr
* resources match the worker's attrs. Req 4.2, 4.3
*/
static void test_apply_join_state_updates_resource_values(void)
{
setup_test_cluster();
hostattrs_t attrs;
memset(&attrs, 0, sizeof(attrs));
attrs.hosttype = makeString("LINUX");
attrs.hostmodel = makeString("x86");
attrs.cpufactor = 3.0f;
attrs.maxcpus = 8;
attrs.maxmem = 64;
attrs.maxswap = 128;
attrs.maxtmp = 256;
attrs.ndisks = 4;
worker_t* w = make_test_worker("host-ajs-3", &attrs);
TEST_ASSERT_NOT_NULL(w);
xce_hostconfig_t hconfig;
memset(&hconfig, 0, sizeof(hconfig));
hconfig.hosttype = (char*)"!";
hconfig.hostmodel = (char*)"!";
hconfig.mxj = 6;
endpoint_t ep;
memset(&ep, 0, sizeof(ep));
lock(w, RTTI_LOCK_WRITE);
int rc = apply_join_state(&g_tc.c, w, &ep, &hconfig);
unlock(w);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(6.0f, get_worker_res_fval(w, "slots"),
"slots must equal hconfig.mxj");
TEST_ASSERT_EQUAL_FLOAT(3.0f, get_worker_res_fval(w, "cpuf"));
TEST_ASSERT_EQUAL_FLOAT(8.0f, get_worker_res_fval(w, "maxcpus"));
TEST_ASSERT_EQUAL_FLOAT(64.0f, get_worker_res_fval(w, "maxmem"));
TEST_ASSERT_EQUAL_FLOAT(128.0f, get_worker_res_fval(w, "maxswap"));
TEST_ASSERT_EQUAL_FLOAT(256.0f, get_worker_res_fval(w, "maxtmp"));
TEST_ASSERT_EQUAL_FLOAT(4.0f, get_worker_res_fval(w, "ndisks"));
teardown_test_cluster();
printf("test_apply_join_state_updates_resource_values: PASS\n");
}
/* ── Task 1.2: Tests for init_worker ────────────────────────────────── */
/*
* After init_worker for a new hostname, key_find must find the entry and
* return a non-negative index. Req 2.1, 2.3
*/
static void test_init_worker_creates_new_entry(void)
{
setup_test_cluster();
hostattrs_t attrs;
memset(&attrs, 0, sizeof(attrs));
attrs.hosttype = makeString("LINUX");
attrs.hostmodel = makeString("x86");
attrs.cpufactor = 1.0f;
attrs.maxcpus = 4;
attrs.maxmem = 16;
attrs.maxswap = 32;
attrs.maxtmp = 64;
attrs.ndisks = 1;
xce_hostconfig_t hconfig;
memset(&hconfig, 0, sizeof(hconfig));
hconfig.hosttype = (char*)"!";
hconfig.hostmodel = (char*)"!";
hconfig.mxj = 4;
endpoint_t ep;
memset(&ep, 0, sizeof(ep));
int idx = init_worker(&g_tc.c, &ep, "new-host-1", &attrs, &hconfig);
TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(0, idx,
"init_worker must return non-negative index");
worker_t* w = NULL;
lock(g_tc.c.workermgr, RTTI_LOCK_READ);
int rc = key_find(g_tc.c.workermgr, (void*)"new-host-1", (void**)&w);
unlock(g_tc.c.workermgr);
TEST_ASSERT_GREATER_THAN_MESSAGE(-1, rc,
"worker must be registered in workermgr after init");
TEST_ASSERT_NOT_NULL_MESSAGE(w, "key_find must return a valid worker pointer");
teardown_test_cluster();
printf("test_init_worker_creates_new_entry: PASS\n");
}
/*
* The index returned by init_worker must match w->idx. Req 2.3
*/
static void test_init_worker_returns_correct_idx(void)
{
setup_test_cluster();
hostattrs_t attrs;
memset(&attrs, 0, sizeof(attrs));
attrs.hosttype = makeString("LINUX");
attrs.hostmodel = makeString("x86");
attrs.cpufactor = 1.0f;
attrs.maxcpus = 2;
attrs.maxmem = 8;
attrs.maxswap = 16;
attrs.maxtmp = 32;
attrs.ndisks = 1;
xce_hostconfig_t hconfig;
memset(&hconfig, 0, sizeof(hconfig));
hconfig.hosttype = (char*)"!";
hconfig.hostmodel = (char*)"!";
hconfig.mxj = 2;
endpoint_t ep;
memset(&ep, 0, sizeof(ep));
int idx = init_worker(&g_tc.c, &ep, "new-host-2", &attrs, &hconfig);
TEST_ASSERT_GREATER_OR_EQUAL(0, idx);
worker_t* w = NULL;
lock(g_tc.c.workermgr, RTTI_LOCK_READ);
key_find(g_tc.c.workermgr, (void*)"new-host-2", (void**)&w);
unlock(g_tc.c.workermgr);
TEST_ASSERT_NOT_NULL(w);
TEST_ASSERT_EQUAL_INT_MESSAGE(idx, w->idx,
"returned idx must match the worker's idx field");
teardown_test_cluster();
printf("test_init_worker_returns_correct_idx: PASS\n");
}
/* ── Task 1.3: Tests for rejoin_worker ──────────────────────────────── */
/*
* rejoin_worker must update hostattrs and ep on the existing entry. Req 3.1, 3.2
*/
static void test_rejoin_worker_updates_attrs_and_ep(void)
{
setup_test_cluster();
hostattrs_t old_attrs;
memset(&old_attrs, 0, sizeof(old_attrs));
old_attrs.hosttype = makeString("OLD_TYPE");
old_attrs.hostmodel = makeString("OLD_MODEL");
old_attrs.cpufactor = 1.0f;
old_attrs.maxcpus = 2;
old_attrs.maxmem = 8;
old_attrs.maxswap = 16;
old_attrs.maxtmp = 32;
old_attrs.ndisks = 1;
worker_t* w = make_test_worker("rejoin-host-1", &old_attrs);
TEST_ASSERT_NOT_NULL(w);
hostattrs_t new_attrs;
memset(&new_attrs, 0, sizeof(new_attrs));
new_attrs.hosttype = makeString("NEW_TYPE");
new_attrs.hostmodel = makeString("NEW_MODEL");
new_attrs.cpufactor = 2.0f;
new_attrs.maxcpus = 8;
new_attrs.maxmem = 64;
new_attrs.maxswap = 128;
new_attrs.maxtmp = 256;
new_attrs.ndisks = 2;
xce_hostconfig_t hconfig;
memset(&hconfig, 0, sizeof(hconfig));
hconfig.hosttype = (char*)"!";
hconfig.hostmodel = (char*)"!";
hconfig.mxj = 4;
endpoint_t ep;
memset(&ep, 0, sizeof(ep));
int idx = rejoin_worker(&g_tc.c, w, &ep, &new_attrs, &hconfig);
TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(0, idx,
"rejoin_worker must return non-negative index");
TEST_ASSERT_EQUAL_STRING_MESSAGE("NEW_TYPE", w->host.attrs->hosttype,
"hosttype must be updated to new attrs value");
TEST_ASSERT_EQUAL_STRING_MESSAGE("NEW_MODEL", w->host.attrs->hostmodel,
"hostmodel must be updated to new attrs value");
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(2.0f, w->host.attrs->cpufactor,
"cpufactor must be updated to new value");
TEST_ASSERT_EQUAL_PTR_MESSAGE(&ep, w->ep,
"ep must be updated to the new endpoint");
/* Release the test's own references to the new strings */
dereference(new_attrs.hosttype);
dereference(new_attrs.hostmodel);
teardown_test_cluster();
printf("test_rejoin_worker_updates_attrs_and_ep: PASS\n");
}
/*
* Admin-set status bits must survive a rejoin_worker call. Req 3.3
*/
static void test_rejoin_worker_preserves_admin_bits(void)
{
setup_test_cluster();
hostattrs_t attrs;
memset(&attrs, 0, sizeof(attrs));
attrs.hosttype = makeString("LINUX");
attrs.hostmodel = makeString("x86");
attrs.cpufactor = 1.0f;
attrs.maxcpus = 4;
attrs.maxmem = 16;
attrs.maxswap = 32;
attrs.maxtmp = 64;
attrs.ndisks = 1;
worker_t* w = make_test_worker("rejoin-host-2", &attrs);
TEST_ASSERT_NOT_NULL(w);
/* Set all admin bits before the rejoin */
lock(w, RTTI_LOCK_WRITE);
w->host.status |= (XCE_WORKER_LOCKEDU | XCE_WORKER_CLOSED
| XCE_WORKER_CLOSED_BY_ADMIN);
unlock(w);
hostattrs_t new_attrs;
memset(&new_attrs, 0, sizeof(new_attrs));
new_attrs.hosttype = makeString("LINUX");
new_attrs.hostmodel = makeString("x86");
new_attrs.cpufactor = 2.0f;
new_attrs.maxcpus = 8;
new_attrs.maxmem = 32;
new_attrs.maxswap = 64;
new_attrs.maxtmp = 128;
new_attrs.ndisks = 2;
xce_hostconfig_t hconfig;
memset(&hconfig, 0, sizeof(hconfig));
hconfig.hosttype = (char*)"!";
hconfig.hostmodel = (char*)"!";
hconfig.mxj = 4;
endpoint_t ep;
memset(&ep, 0, sizeof(ep));
rejoin_worker(&g_tc.c, w, &ep, &new_attrs, &hconfig);
TEST_ASSERT_TRUE_MESSAGE(w->host.status & XCE_WORKER_LOCKEDU,
"LOCKEDU must be preserved after rejoin");
TEST_ASSERT_TRUE_MESSAGE(w->host.status & XCE_WORKER_CLOSED,
"CLOSED must be preserved after rejoin");
TEST_ASSERT_TRUE_MESSAGE(w->host.status & XCE_WORKER_CLOSED_BY_ADMIN,
"CLOSED_BY_ADMIN must be preserved after rejoin");
dereference(new_attrs.hosttype);
dereference(new_attrs.hostmodel);
teardown_test_cluster();
printf("test_rejoin_worker_preserves_admin_bits: PASS\n");
}
/* ── Dispatch ───────────────────────────────────────────────────────── */
void test_worker_join(void)
{
printf("\n>> worker join refactor tests\n\n");
/* xstring_table_init must be called once before any makeString usage */
xstring_table_init(139);
/* Task 1.1: apply_join_state (shared status/resource update) */
RUN_TEST(test_apply_join_state_clears_unavail);
RUN_TEST(test_apply_join_state_applies_hosttype_override);
RUN_TEST(test_apply_join_state_updates_resource_values);
/* Task 1.2: init_worker (first-time join) */
RUN_TEST(test_init_worker_creates_new_entry);
RUN_TEST(test_init_worker_returns_correct_idx);
/* Task 1.3: rejoin_worker (reconnect with admin-bit preservation) */
RUN_TEST(test_rejoin_worker_updates_attrs_and_ep);
RUN_TEST(test_rejoin_worker_preserves_admin_bits);
}