test(raft-sim-conformance): focused Log Matching checker test for R3.2 (task 3.2)
This commit is contained in:
parent
25b14a6dca
commit
d9b5fd51e0
2 changed files with 72 additions and 1 deletions
|
|
@ -63,7 +63,7 @@
|
|||
- _Boundary: raft_sim.c, test_raft.c_
|
||||
- _Depends: 1.1_
|
||||
|
||||
- [ ] 3.2 RED+GREEN: Log Matching checker (P)
|
||||
- [x] 3.2 RED+GREEN: Log Matching checker (P)
|
||||
- Test: two logs sharing `(idx, term)` but differing earlier fail; identical-prefix logs pass
|
||||
- Implement `raft_sim_check_log_matching` (pairwise; shared `(idx, term)` ⇒ identical `1..idx`)
|
||||
- Observable completion: both directions assert correctly
|
||||
|
|
|
|||
|
|
@ -3622,6 +3622,76 @@ static void test_sim_check_log_matching_fail_on_divergence(void)
|
|||
TEST_ASSERT_EQUAL_INT(0, raft_sim_check_log_matching(&c));
|
||||
}
|
||||
|
||||
/*
|
||||
* test_sim_3_2_log_matching_shared_idx_term_requires_identical_prefix (task 3.2)
|
||||
*
|
||||
* Spec R3.2 (Log Matching): "for any two nodes whose logs contain an entry at
|
||||
* the same index with the same term, all entries at and before that index are
|
||||
* identical (same term and value) across those two logs". This test exercises
|
||||
* raft_sim_check_log_matching over hand-built clusters in BOTH directions, with
|
||||
* the shared (idx, term) at a LATER index (3) and the divergence at an EARLIER
|
||||
* index (1) — a distinct shape from the task-8.3 tests (which share index 2 and
|
||||
* diverge at index 1) so the spec's R3.2 traceability is explicit.
|
||||
*
|
||||
* It is a pure checker unit test: roles/terms/bytes are set directly (no
|
||||
* protocol drive) per Req 3.6 (observable per-node log state only). The checker
|
||||
* pre-exists (raft-log-replication); this test confirms — and guards — that it
|
||||
* correctly flags an earlier-prefix divergence and passes identical prefixes.
|
||||
*/
|
||||
static void test_sim_3_2_log_matching_shared_idx_term_requires_identical_prefix(void)
|
||||
{
|
||||
/* ---- Direction A: shared (idx=3, term=9), differ at earlier idx=1 ⇒ FAIL.
|
||||
* Both nodes carry the SAME (term, value) at indices 2 and 3, and the SAME
|
||||
* term at index 1, but index-1 VALUE differs. Because index 3 is shared with
|
||||
* the same term, the prefix 1..3 must be identical — index 1 breaks it. */
|
||||
{
|
||||
raft_sim_cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
TEST_ASSERT_EQUAL_INT(0, raft_sim_init(&c, 3, 2000));
|
||||
|
||||
/* node 0: idx1=(t3,"AAA") idx2=(t6,"MMM") idx3=(t9,"ZZZ") */
|
||||
c.nodes[0].log[0].term = 3; memcpy(c.nodes[0].log[0].bytes, "AAA", 3); c.nodes[0].log[0].len = 3;
|
||||
c.nodes[0].log[1].term = 6; memcpy(c.nodes[0].log[1].bytes, "MMM", 3); c.nodes[0].log[1].len = 3;
|
||||
c.nodes[0].log[2].term = 9; memcpy(c.nodes[0].log[2].bytes, "ZZZ", 3); c.nodes[0].log[2].len = 3;
|
||||
c.nodes[0].log_count = 3;
|
||||
|
||||
/* node 1: SAME (idx3,t9) and (idx2,t6) but idx1 term matches with a
|
||||
* DIFFERENT value ("CCC") — earlier-prefix divergence under a shared tip. */
|
||||
c.nodes[1].log[0].term = 3; memcpy(c.nodes[1].log[0].bytes, "CCC", 3); c.nodes[1].log[0].len = 3;
|
||||
c.nodes[1].log[1].term = 6; memcpy(c.nodes[1].log[1].bytes, "MMM", 3); c.nodes[1].log[1].len = 3;
|
||||
c.nodes[1].log[2].term = 9; memcpy(c.nodes[1].log[2].bytes, "ZZZ", 3); c.nodes[1].log[2].len = 3;
|
||||
c.nodes[1].log_count = 3;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, raft_sim_check_log_matching(&c));
|
||||
}
|
||||
|
||||
/* ---- Direction B: identical prefixes ⇒ PASS. node 1 is a strict prefix of
|
||||
* node 0 (idx 1..2 byte-identical); node 2 holds the full identical prefix. */
|
||||
{
|
||||
raft_sim_cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
TEST_ASSERT_EQUAL_INT(0, raft_sim_init(&c, 3, 2000));
|
||||
|
||||
c.nodes[0].log[0].term = 3; memcpy(c.nodes[0].log[0].bytes, "AAA", 3); c.nodes[0].log[0].len = 3;
|
||||
c.nodes[0].log[1].term = 6; memcpy(c.nodes[0].log[1].bytes, "MMM", 3); c.nodes[0].log[1].len = 3;
|
||||
c.nodes[0].log[2].term = 9; memcpy(c.nodes[0].log[2].bytes, "ZZZ", 3); c.nodes[0].log[2].len = 3;
|
||||
c.nodes[0].log_count = 3;
|
||||
|
||||
/* node 1: shorter but byte-identical prefix (idx 1..2). */
|
||||
c.nodes[1].log[0].term = 3; memcpy(c.nodes[1].log[0].bytes, "AAA", 3); c.nodes[1].log[0].len = 3;
|
||||
c.nodes[1].log[1].term = 6; memcpy(c.nodes[1].log[1].bytes, "MMM", 3); c.nodes[1].log[1].len = 3;
|
||||
c.nodes[1].log_count = 2;
|
||||
|
||||
/* node 2: full identical prefix (idx 1..3). */
|
||||
c.nodes[2].log[0].term = 3; memcpy(c.nodes[2].log[0].bytes, "AAA", 3); c.nodes[2].log[0].len = 3;
|
||||
c.nodes[2].log[1].term = 6; memcpy(c.nodes[2].log[1].bytes, "MMM", 3); c.nodes[2].log[1].len = 3;
|
||||
c.nodes[2].log[2].term = 9; memcpy(c.nodes[2].log[2].bytes, "ZZZ", 3); c.nodes[2].log[2].len = 3;
|
||||
c.nodes[2].log_count = 3;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, raft_sim_check_log_matching(&c));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* test_sim_check_election_safety_fail_on_two_leaders (task 3.1)
|
||||
*
|
||||
|
|
@ -7412,6 +7482,7 @@ void test_raft(void)
|
|||
RUN_TEST(test_sim_deliver_append_entries_replicates);
|
||||
RUN_TEST(test_sim_check_log_matching_pass_on_converged);
|
||||
RUN_TEST(test_sim_check_log_matching_fail_on_divergence);
|
||||
RUN_TEST(test_sim_3_2_log_matching_shared_idx_term_requires_identical_prefix);
|
||||
RUN_TEST(test_sim_check_election_safety_fail_on_two_leaders);
|
||||
RUN_TEST(test_sim_check_election_safety_pass_on_single_leader);
|
||||
RUN_TEST(test_sim_9_1_replication_and_partition_catch_up);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue