feat(ha-term-safety): add WAL_CONFIG_MARKER entry type + reference payload codec (task 4.1)

This commit is contained in:
Rongsong Shen 2026-06-22 09:37:23 +00:00
parent f1783f7952
commit f447ad4183
5 changed files with 215 additions and 1 deletions

View file

@ -504,6 +504,92 @@ static void test_conf_save_config_path_consistency(void)
printf("test_conf_save_config_path_consistency: PASS\n");
}
/* ── WAL_CONFIG_MARKER codec (ha-term-safety task 4.1) ───────────────
* The config-change marker payload carries ONLY {version_tag, sha256} —
* a reference, never the configuration bytes (R7.2/R7.4). These tests
* exercise the fixed-size encode/decode codec that rides inside the
* raft_entry_blob_t envelope with type == WAL_CONFIG_MARKER. */
/* Round-trip: encode {version_tag, sha256} then decode → identical, and the
* encoded form is exactly the fixed marker size (tag + 32-byte hash) with NO
* configuration bytes appended. */
static void test_config_marker_encode_decode_roundtrip(void)
{
const uint64_t version_tag = 0x0123456789abcdefULL;
uint8_t sha256[32];
for (int i = 0; i < 32; i++)
sha256[i] = (uint8_t)(0xA0 + i);
uint8_t buf[64];
size_t enc_len = 0;
int rc = config_marker_encode(version_tag, sha256, buf, sizeof(buf), &enc_len);
TEST_ASSERT_EQUAL_INT(0, rc);
/* Encoded form carries NO config bytes: exactly 8 (tag) + 32 (sha256). */
TEST_ASSERT_EQUAL_UINT(WAL_CONFIG_MARKER_PAYLOAD_LEN, enc_len);
TEST_ASSERT_EQUAL_UINT(8u + 32u, enc_len);
uint64_t got_tag = 0;
uint8_t got_sha[32];
memset(got_sha, 0, sizeof(got_sha));
rc = config_marker_decode(buf, enc_len, &got_tag, got_sha);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_HEX64(version_tag, got_tag);
TEST_ASSERT_EQUAL_HEX8_ARRAY(sha256, got_sha, 32);
printf("test_config_marker_encode_decode_roundtrip: PASS\n");
}
/* The WAL entry type value must be exactly 50 and must not collide with the
* neighbouring WAL_NOOP (40) or WAL_CHECKPOINT (100) types (design.md). */
static void test_config_marker_enum_value_is_50(void)
{
TEST_ASSERT_EQUAL_INT(50, WAL_CONFIG_MARKER);
TEST_ASSERT_NOT_EQUAL(WAL_NOOP, WAL_CONFIG_MARKER);
TEST_ASSERT_NOT_EQUAL(WAL_CHECKPOINT, WAL_CONFIG_MARKER);
/* Strictly between WAL_NOOP and WAL_CHECKPOINT. */
TEST_ASSERT_TRUE(WAL_CONFIG_MARKER > WAL_NOOP);
TEST_ASSERT_TRUE(WAL_CONFIG_MARKER < WAL_CHECKPOINT);
printf("test_config_marker_enum_value_is_50: PASS\n");
}
/* A truncated (short) buffer must fail cleanly with -EINVAL, never read past
* the end of the supplied buffer. */
static void test_config_marker_decode_truncated_fails(void)
{
uint8_t sha256[32];
memset(sha256, 0x5c, sizeof(sha256));
uint8_t buf[WAL_CONFIG_MARKER_PAYLOAD_LEN];
size_t enc_len = 0;
TEST_ASSERT_EQUAL_INT(0, config_marker_encode(0xdeadbeefcafef00dULL, sha256,
buf, sizeof(buf), &enc_len));
uint64_t tag = 0;
uint8_t sha_out[32];
/* One byte short of a full marker payload → reject, no OOB read. */
int rc = config_marker_decode(buf, enc_len - 1, &tag, sha_out);
TEST_ASSERT_EQUAL_INT(-EINVAL, rc);
/* Zero-length buffer → reject. */
rc = config_marker_decode(buf, 0, &tag, sha_out);
TEST_ASSERT_EQUAL_INT(-EINVAL, rc);
printf("test_config_marker_decode_truncated_fails: PASS\n");
}
/* A too-small output buffer must fail with -ENOSPC and not overflow. */
static void test_config_marker_encode_short_buffer_fails(void)
{
uint8_t sha256[32];
memset(sha256, 0x11, sizeof(sha256));
uint8_t small[WAL_CONFIG_MARKER_PAYLOAD_LEN - 1];
size_t enc_len = 123; /* sentinel — must be left untouched on failure */
int rc = config_marker_encode(7, sha256, small, sizeof(small), &enc_len);
TEST_ASSERT_EQUAL_INT(-ENOSPC, rc);
printf("test_config_marker_encode_short_buffer_fails: PASS\n");
}
/* ── wait_all_with_timeout (XCE_TESTING shim) ───── */
#ifdef XCE_TESTING
@ -2260,6 +2346,11 @@ void test_config_repl(void)
RUN_TEST(test_sanitise_cn_control_chars);
RUN_TEST(test_sanitise_cn_truncated);
RUN_TEST(test_conf_save_config_path_consistency);
/* Task 4.1: WAL_CONFIG_MARKER type + reference-only codec */
RUN_TEST(test_config_marker_enum_value_is_50);
RUN_TEST(test_config_marker_encode_decode_roundtrip);
RUN_TEST(test_config_marker_decode_truncated_fails);
RUN_TEST(test_config_marker_encode_short_buffer_fails);
#ifdef XCE_TESTING
RUN_TEST(test_load_primary_config_missing_returns_error);
RUN_TEST(test_wait_all_with_timeout_slow_threads);