From 87ce7be917e40d42f25ba441aaf4b557e13d05b1 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Wed, 29 Jul 2026 15:35:08 +0000 Subject: [PATCH] test(raft-adapter-conformance): dormant AppendEntries sender stays uncalled in production New test drives repl_broadcast and ha_stream_wal_to_standby through real production fixtures (reused from tasks 2.3/6.2), asserts both actually fired (non-vacuous), and asserts ha_send_append_entries_stub_call_count stays 0 throughout -- the Requirement 3.3 non-regression guard. RED-verified: manually invoking ha_cb_send_append_entries_for_test inside the test reliably trips the assertion; reverted for GREEN. 1064 -> 1065 tests, 0 failures. Task 4 (dormant sender documentation + guard) now complete. --- .kiro/specs/raft-adapter-conformance/tasks.md | 2 +- .../testsuite/test_ha_core_safety.c | 94 +++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/.kiro/specs/raft-adapter-conformance/tasks.md b/.kiro/specs/raft-adapter-conformance/tasks.md index 0f08b80..e959c11 100644 --- a/.kiro/specs/raft-adapter-conformance/tasks.md +++ b/.kiro/specs/raft-adapter-conformance/tasks.md @@ -55,7 +55,7 @@ - Observable: the counter exists and is queryable from a test; the doc-comment names both the dormant path and the live substitute explicitly - _Requirements: 3.3_ -- [ ] 4.2 Test that the dormant sender stays uncalled in production +- [x] 4.2 Test that the dormant sender stays uncalled in production - Drive the production heartbeat/replication path end-to-end in a test and assert the new call counter stays at zero - In the same test, confirm `repl_broadcast`/`ha_stream_wal_to_standby` did fire for the same scenario, proving the assertion isn't vacuous - Observable: the test fails if a future change accidentally gives `raft_send_append_entries` a production caller diff --git a/src/controller/testsuite/test_ha_core_safety.c b/src/controller/testsuite/test_ha_core_safety.c index cb42523..a161dbe 100644 --- a/src/controller/testsuite/test_ha_core_safety.c +++ b/src/controller/testsuite/test_ha_core_safety.c @@ -2677,6 +2677,95 @@ static void test_4_1_send_append_entries_stub_call_count_increments(void) printf("test_4_1_send_append_entries_stub_call_count_increments: PASS\n"); } +/* Task 4.2 (raft-adapter-conformance, Requirement 3.3): the fuller + * non-regression guard task 4.1's doc-comment promises — drive BOTH real + * production AppendEntries-equivalent senders (repl_broadcast for live + * in-term replication, ha_stream_wal_to_standby for catch-up streaming) end + * to end and assert ha_send_append_entries_stub_call_count stays at 0 + * throughout, while independently confirming each real sender actually fired + * for this scenario — so the zero-count assertion is not vacuously true + * because nothing ran. Reuses the exact fixtures/hooks task 6.2's + * test_6_2_real_senders_never_produce_termblind_shape already established for + * driving these same two real senders (repl_broadcast_send_hook + + * hcs_6_2_broadcast_hook/g_hcs62_bcast_n for sender 1; + * xce_wal_stream_request_hook + hcs_2_3_capture_hook/g_sends_n for sender 2). + * If a future change ever gives raft_send_append_entries a production + * caller, this test catches it. */ +static void test_4_2_dormant_sender_stays_uncalled_in_production(void) +{ + /* Reset first: an earlier test in this same process (task 4.1's own + * sanity test, which runs immediately before this one in the suite) + * already drove ha_cb_send_append_entries_for_test directly and left the + * counter non-zero — Unity tests share process state. */ + ha_send_append_entries_stub_call_count = 0; + + /* ── Real sender 1: repl_broadcast (live in-term replication) ─────────── */ + replicator_t r; + TEST_ASSERT_EQUAL_INT(0, repl_init(&r)); + int dummy_ep = 0; + TEST_ASSERT_TRUE(repl_add_standby(&r, (void*)&dummy_ep, "standby", 7101) >= 0); + + uint8_t full[HCS_FULL_LEN]; + hcs_fill_body(full); + + wal_entry_hdr_t hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.lsn = 1; + hdr.type = WAL_JOB_SUBMIT; + hdr.term = 3; + + g_hcs62_bcast_n = 0; + repl_broadcast_send_hook = hcs_6_2_broadcast_hook; + int brc = repl_broadcast(&r, &hdr, full, HCS_FULL_LEN, /*prev_log_term=*/0); + repl_broadcast_send_hook = NULL; + repl_fini(&r); + + TEST_ASSERT_EQUAL_INT(0, brc); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, g_hcs62_bcast_n, + "repl_broadcast must have actually sent to the registered standby — " + "otherwise the counter-stays-zero assertion below would be vacuous"); + + /* ── Real sender 2: ha_stream_wal_to_standby (catch-up streaming) ──────── */ + hcs_member_t src, rcv; + TEST_ASSERT_EQUAL_INT(0, hcs_member_up(&src, "42src")); + TEST_ASSERT_EQUAL_INT(0, hcs_member_up(&rcv, "42rcv")); + + TEST_ASSERT_EQUAL_INT(0, hcs_wal_put(&src, 1, /*term=*/3, WAL_JOB_SUBMIT, + full, HCS_FULL_LEN)); + atomic_store_explicit(&src.p->committed_lsn, 1, memory_order_release); + + hcs_2_3_reset(&rcv, /*fault_lsn=*/0 /* no fault injected — happy path */); + xce_wal_stream_request_hook = hcs_2_3_capture_hook; + int src_rc = ha_stream_wal_to_standby((endpoint_t*)NULL, src.wal_dir, src.p, + /*leader_term=*/3, /*standby_lsn=*/0, + /*committed_lsn=*/1); + xce_wal_stream_request_hook = NULL; + TEST_ASSERT_EQUAL_INT(0, src_rc); + + TEST_ASSERT_TRUE_MESSAGE(g_sends_n >= 1, + "ha_stream_wal_to_standby must have actually streamed at least one " + "entry — otherwise the counter-stays-zero assertion below would be " + "vacuous"); + + hcs_member_down(&src); + hcs_member_down(&rcv); + + /* ── The Requirement 3.3 non-regression guard itself ──────────────────── + * Both real production AppendEntries-equivalent senders just fired + * (proven non-vacuous above) — yet the dormant + * ha_cb_send_append_entries_stub callback, reachable only via + * raft_send_append_entries (zero production callers), must never have + * been invoked by driving this real path. */ + TEST_ASSERT_EQUAL_UINT64_MESSAGE(0, ha_send_append_entries_stub_call_count, + "driving the production heartbeat/replication path end-to-end must " + "NEVER increment ha_send_append_entries_stub_call_count — a nonzero " + "count here means raft_send_append_entries acquired a production " + "caller, invalidating the Requirement 3.3 'documented dormant' " + "resolution"); + + printf("test_4_2_dormant_sender_stays_uncalled_in_production: PASS\n"); +} + /* ── Suite entry point ─────────────────────────────────────────────────────*/ void test_ha_core_safety(void) @@ -2777,4 +2866,9 @@ void test_ha_core_safety(void) * itself increments correctly. Task 4.2 owns the fuller "stays at zero * across a real production replication drive" assertion. */ RUN_TEST(test_4_1_send_append_entries_stub_call_count_increments); + /* Task 4.2 (raft-adapter-conformance): the fuller non-regression guard — + * drive repl_broadcast and ha_stream_wal_to_standby (the two real + * production senders) end-to-end and confirm the dormant stub's counter + * never moves off zero, while proving each real sender actually fired. */ + RUN_TEST(test_4_2_dormant_sender_stays_uncalled_in_production); }