test(ha-entity-snapshots): RED test for the rotation guard (task 4.2)

A checkpoint file whose position matches an in-flight peer transfer
(task 4.1's snapshot_inflight_lsn[]) must survive rotate_snapshots even
when snap_keep_n would otherwise delete it. Genuinely fails today (file
removed, access() returns -1) since rotate_snapshots has no awareness of
in-flight transfers yet.

Executed directly (no subagent dispatch) per the hybrid execution-mode
decision -- small, single-file, isolated test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDYFbrgyDHbvNYcxApKhrX
This commit is contained in:
Rongsong Shen 2026-07-01 07:34:37 +00:00
parent 164c9a9d2e
commit 221d86788d
2 changed files with 57 additions and 1 deletions

View file

@ -57,7 +57,7 @@
goes out and cleared after completion
- _Requirements: 3.1, 3.2, 3.3_
- _Boundary: HA Replication Layer_
- [ ] 4.2 RED: rotation-guard test
- [x] 4.2 RED: rotation-guard test
- Assert a checkpoint file whose position matches an in-flight transfer survives file rotation
even when it would otherwise be rotated out
- Observable: new test compiles and fails (RED) against the current rotation logic

View file

@ -941,6 +941,61 @@ static void test_snapent_checkpoint_queue_status_roundtrip(void)
printf("test_snapent_checkpoint_queue_status_roundtrip: PASS\n");
}
/* ───────────────────────────────────────────────────────────────────────
* ha-entity-snapshots Task 4.2 (Req 3.1-3.3): rotation-guard RED test.
*
* A checkpoint file whose position matches an in-flight peer transfer
* (task 4.1's snapshot_inflight_lsn[]) must survive rotate_snapshots even
* when it would otherwise fall outside snap_keep_n. RED against today's
* rotate_snapshots, which has no awareness of in-flight transfers at all.
* ─────────────────────────────────────────────────────────────────────── */
static void test_snapent_rotation_preserves_in_flight_checkpoint(void)
{
snapent_fixture_t* f = snapent_make(0, 0);
TEST_ASSERT_NOT_NULL(f);
/* Force aggressive rotation so the protected file would otherwise be
* deleted after just one more checkpoint cycle. */
f->c->persist.config.snap_keep_n = 1;
/* Round 1: produce the checkpoint file we'll mark as in-flight. */
TEST_ASSERT_EQUAL_INT(0,
persist_wal_async(&f->c->persist, WAL_JOB_UPDATE, "a", 1));
TEST_ASSERT_EQUAL_INT(0, persist_checkpoint(&f->c->persist));
uint64_t protected_lsn = f->c->persist.checkpoint_lsn;
char protected_path[4096];
snprintf(protected_path, sizeof(protected_path),
"%s/snap-%016" PRIx64 ".capnp",
f->c->persist.snap_path, protected_lsn);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, access(protected_path, F_OK),
"the checkpoint file must exist right after it is written");
/* Mark this checkpoint's position as referenced by an in-flight peer
* transfer -- the same field/lifecycle task 4.1 wired in ha.c. */
atomic_store_explicit(&f->c->ha.snapshot_inflight_lsn[0], protected_lsn,
memory_order_release);
/* Rounds 2-3: with snap_keep_n=1, each further checkpoint would normally
* rotate out every older file, including the one marked in-flight. */
for (int i = 0; i < 2; i++) {
TEST_ASSERT_EQUAL_INT(0,
persist_wal_async(&f->c->persist, WAL_JOB_UPDATE, "b", 1));
TEST_ASSERT_EQUAL_INT(0, persist_checkpoint(&f->c->persist));
}
TEST_ASSERT_EQUAL_INT_MESSAGE(0, access(protected_path, F_OK),
"Req 3.1-3.3: a checkpoint file referenced by an in-flight peer "
"transfer must survive rotate_snapshots even when snap_keep_n "
"would otherwise have deleted it -- rotate_snapshots does not yet "
"consult snapshot_inflight_lsn[] (task 4.3 not yet implemented)");
atomic_store_explicit(&f->c->ha.snapshot_inflight_lsn[0], 0,
memory_order_release);
snapent_teardown(f);
printf("test_snapent_rotation_preserves_in_flight_checkpoint: PASS\n");
}
/* ------------------------------------------------------------------ */
/* Suite entry point */
/* ------------------------------------------------------------------ */
@ -966,4 +1021,5 @@ void test_persist_snapshot_entities(void)
RUN_TEST(test_snapent_checkpoint_write_failure_leaves_no_final_file);
RUN_TEST(test_snapent_checkpoint_mid_encode_failure_discards_whole_file);
RUN_TEST(test_snapent_checkpoint_queue_status_roundtrip);
RUN_TEST(test_snapent_rotation_preserves_in_flight_checkpoint);
}