From 221d86788dac13627c7924aad7fb3f9706d7c52b Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Wed, 1 Jul 2026 07:34:37 +0000 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01JDYFbrgyDHbvNYcxApKhrX --- .kiro/specs/ha-entity-snapshots/tasks.md | 2 +- .../test_persist_snapshot_entities.c | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/.kiro/specs/ha-entity-snapshots/tasks.md b/.kiro/specs/ha-entity-snapshots/tasks.md index 496f1e30..b1f35bee 100644 --- a/.kiro/specs/ha-entity-snapshots/tasks.md +++ b/.kiro/specs/ha-entity-snapshots/tasks.md @@ -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 diff --git a/src/controller/testsuite/test_persist_snapshot_entities.c b/src/controller/testsuite/test_persist_snapshot_entities.c index fb4887fe..d8c6597d 100644 --- a/src/controller/testsuite/test_persist_snapshot_entities.c +++ b/src/controller/testsuite/test_persist_snapshot_entities.c @@ -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); }