test(raft-commit-safety): simulator invariant tests (tasks 7.1-7.4)

7.1 Figure-8 commit refusal (G4): prior-term entry at a genuine quorum HELD (only the
   current-term gate keeps commit_index at 0), then a current-term entry advances over
   both (indirect commit) with one fire. Closes the Req 6.3 two-phase coverage.
7.2 Leader Completeness: committed prefix on {0,1}; node 0 fails; the stale node 2 is
   DENIED by the freshness gate so only the up-to-date node 1 can win — and it holds the
   full committed prefix (term+bytes).
7.3 freshness lexicographic (vote + prevote): stale-term-higher-index DENIED,
   fresher-term-lower-index GRANTED, tie decided by index; prevote side-effect-free.
7.4 on_commit_advanced trigger: fire-count tracks STRICT advances — held(0)/advance(1)/
   no-op-redrive(1)/advance(2)/non-leader(2).

Test-only (sim_set_log_entry local helper); reviewed in-parent (transient API 529 on the
review subagent). ASAN green (243 tests).
This commit is contained in:
Rongsong Shen 2026-06-22 00:41:11 +00:00
parent 56485e2fa3
commit 62a927b419
2 changed files with 366 additions and 4 deletions

View file

@ -127,28 +127,28 @@
## 7. Simulator invariant tests (Figure-8, Leader Completeness, freshness, trigger semantics)
- [ ] 7.1 Figure-8 commit-refusal test (G4)
- [x] 7.1 Figure-8 commit-refusal test (G4)
- Test: construct a previous-term entry replicated to a quorum but not covered by a committed current-term entry; drive `raft_advance_commit` and assert `commit_index` does NOT move over it; then replicate a current-term entry above it to a quorum, drive `raft_advance_commit`, and assert `commit_index` advances over BOTH (indirect commit)
- Observable completion: test passes end-to-end via the sim; the held phase and the released phase are both asserted
- _Requirements: 6.2, 6.3_
- _Boundary: test_raft.c, raft_sim.c_
- _Depends: 6.1_
- [ ] 7.2 Leader Completeness across forced leader changes
- [x] 7.2 Leader Completeness across forced leader changes
- Test: drive replication to a commit, then force leader changes (timeout / partition); assert any entry committed (≤ `commit_index` on the committing leader) is present in the log of every node that subsequently becomes leader
- Observable completion: test passes; no future leader is missing a committed entry across the churn
- _Requirements: 6.4_
- _Boundary: test_raft.c, raft_sim.c_
- _Depends: 6.2, 7.1_
- [ ] 7.3 Freshness-gate lexicographic exercise
- [x] 7.3 Freshness-gate lexicographic exercise
- Test: drive votes with mismatched `(last_log_term, last_log_index)` tuples and assert the lexicographic outcome — stale-term-but-higher-index denied; fresher-term-but-lower-index granted; tie-term decided by index
- Observable completion: test passes for both vote and PreVote paths
- _Requirements: 6.5_
- _Boundary: test_raft.c, raft_sim.c_
- _Depends: 6.2_
- [ ] 7.4 Trigger-semantics test for `on_commit_advanced`
- [x] 7.4 Trigger-semantics test for `on_commit_advanced`
- Test: `on_commit_advanced` fires exactly once on a strict advance and does NOT fire while the current-term rule holds the commit index (Figure-8 held phase) or when not leader
- Observable completion: test passes; the callback fire-count matches the number of strict advances
- _Requirements: 6.6, 2.2, 2.3_