test(scheduler-core): controller-side admin opcode dispatch tests (task 10.2)

Closes the two genuine gaps beyond task 10.1's own coverage (standby and
disconnected-scheduler rejection were already tested):

- Forward timeout: adds an XCE_TESTING-only timeout-override seam
  (SCHEDADMIN_TIMEOUT_MS) in cluster.c, verified inert in production
  builds (-DXCE_TESTING absent from xce_controller's target and every
  project-wide arg; the non-test branch is a bare 5000L literal).
  test_schedadmin_forward_timeout drives the real 5s (shrunk to 200ms
  under test) pthread_cond_timedwait to genuine expiry and asserts the
  exact {ok=false, error="request timeout"} shape from AC 10.

- Round-trip coverage: two more outcome-branch tests
  (rpc_status_only, business_error) using the same real xce_encode/
  xce_decode functions production uses. A literal two-binary
  controller<->scheduler integration was judged out of scope (separate
  meson targets, incompatible fixtures) since task 9.4's own
  test_admin_request.c already white-box-drives the real scheduler
  handler rigorously.

Controller suite 3/3 (1093/1093 assertions, up from 1090), scheduler
26/26 unaffected, clang-format clean, zero diff-hygiene noise (git diff
matches git diff -w for cluster.c).
This commit is contained in:
Rongsong Shen 2026-08-06 04:19:58 +00:00
parent 045be6b3d6
commit 30ff995cac
Signed by: rshen
SSH key fingerprint: SHA256:8Vf2pZ3h6rJa7EhGK+WaAQ9Mta/apZvteW6g9U/WKKk
3 changed files with 346 additions and 2 deletions

View file

@ -309,6 +309,34 @@ static int schedadmin_signal(int status, sched_admin_reply_t* sar)
return 1;
}
#ifdef XCE_TESTING
/*
* schedadmin_set_timeout_ms_for_test / SCHEDADMIN_TIMEOUT_MS (task 10.2, Req
* 5.10 edge case "Scheduler disconnects mid-request"): the OPCode_schedAdmin
* case's forward-and-block-for-reply deadline is a fixed 5s in production
* (requirements.md AC 10: "await scheduler reply (timeout: 5s)"). Proving
* that deadline genuinely expires needs a REAL pthread_cond_timedwait
* timeout to fire -- not a shortcut that bypasses the wait -- but a real 5s
* wait per test run has no precedent in this suite (the longest real waits
* elsewhere are sub-second, e.g. DEFAULT_SYNC_TIMEOUT_MS=500ms in
* test_commit_resolve.c). This test-only override shrinks the deadline so
* the SAME wait-then-give-up code path below executes for real, just
* faster. Guarded by XCE_TESTING like myreq_handler_for_test below -- never
* compiled into production builds, where SCHEDADMIN_TIMEOUT_MS is a bare
* 5000 with zero indirection and the deadline math below folds to exactly
* the prior `deadline.tv_sec += 5` (5000 / 1000 == 5, 5000 % 1000 == 0).
*/
static long g_schedadmin_timeout_ms = 5000;
void schedadmin_set_timeout_ms_for_test(long ms)
{
g_schedadmin_timeout_ms = ms;
}
#define SCHEDADMIN_TIMEOUT_MS g_schedadmin_timeout_ms
#else
#define SCHEDADMIN_TIMEOUT_MS 5000L
#endif
static int myreq_handler(vtask_t* task)
{
vtask_data_t* data = (vtask_data_t*)task->data;
@ -869,8 +897,14 @@ static int myreq_handler(vtask_t* task)
}
struct timespec deadline;
long schedadmin_timeout_ms = SCHEDADMIN_TIMEOUT_MS;
clock_gettime(CLOCK_REALTIME, &deadline);
deadline.tv_sec += 5;
deadline.tv_sec += schedadmin_timeout_ms / 1000;
deadline.tv_nsec += (schedadmin_timeout_ms % 1000) * 1000000L;
if (deadline.tv_nsec >= 1000000000L) {
deadline.tv_sec += 1;
deadline.tv_nsec -= 1000000000L;
}
pthread_mutex_lock(&pending.mu);
while (!pending.done) {