#define _GNU_SOURCE #include #include #include #include "config.h" #include "persist.h" #include "xce/cluster_def.h" /* ------------------------------------------------------------------ */ /* Compile-time structural checks */ /* ------------------------------------------------------------------ */ static void test_cluster_t_has_persist_field(void) { /* Verify cluster_t has a persist field — compile-time structural check. * If persist_t persist is not in cluster_t, this will fail to compile. */ cluster_t c; memset(&c, 0, sizeof(c)); /* Access the persist field to confirm it exists at compile time */ (void)c.persist.current_lsn; TEST_PASS_MESSAGE("cluster_t has persist field"); } /* ------------------------------------------------------------------ */ /* persist_config_defaults() correctness */ /* ------------------------------------------------------------------ */ static void test_persist_config_defaults_wal_sync_ms(void) { persist_config_t cfg = persist_config_defaults(); TEST_ASSERT_EQUAL_UINT32(DEFAULT_WAL_SYNC_MS, cfg.wal_sync_ms); } static void test_persist_config_defaults_snap_interval_s(void) { persist_config_t cfg = persist_config_defaults(); TEST_ASSERT_EQUAL_UINT32(DEFAULT_SNAP_INTERVAL_S, cfg.snap_interval_s); } static void test_persist_config_defaults_snap_wal_size_mb(void) { persist_config_t cfg = persist_config_defaults(); TEST_ASSERT_EQUAL_UINT32(DEFAULT_SNAP_WAL_SIZE_MB, cfg.snap_wal_size_mb); } static void test_persist_config_defaults_snap_keep_n(void) { persist_config_t cfg = persist_config_defaults(); TEST_ASSERT_EQUAL_UINT32(DEFAULT_SNAP_KEEP_N, cfg.snap_keep_n); } static void test_persist_config_defaults_finished_retain_s(void) { persist_config_t cfg = persist_config_defaults(); TEST_ASSERT_EQUAL_UINT32(DEFAULT_FINISHED_RETAIN_S, cfg.finished_retain_s); } static void test_persist_config_defaults_sync_timeout_ms(void) { persist_config_t cfg = persist_config_defaults(); /* Default raised to 500 ms: realistic for durable sync majority * replication (each standby fdatasyncs before ACK), still bounded for * failure detection. Literal-pinned so a regression to the old 50 ms * tight window fails here, not just on a live cluster. */ TEST_ASSERT_EQUAL_UINT32(500, cfg.sync_timeout_ms); TEST_ASSERT_EQUAL_UINT32(DEFAULT_SYNC_TIMEOUT_MS, cfg.sync_timeout_ms); } /* ------------------------------------------------------------------ */ /* --wal-sync-ms flush-cadence override (ha-commit-durability task 1.1) */ /* */ /* persist_config_set_wal_sync_ms() is the single override point shared */ /* by both persist_config_defaults() callers (cluster.c phase2 and */ /* myapp.c step 6). It mirrors the --sync-timeout-ms convention: a */ /* value of 0 means "flag not passed" and preserves the default; a */ /* positive value sets the flush cadence to exactly that. */ /* ------------------------------------------------------------------ */ static void test_wal_sync_ms_override_applies_positive_value(void) { persist_config_t cfg = persist_config_defaults(); persist_config_set_wal_sync_ms(&cfg, 250); TEST_ASSERT_EQUAL_UINT32(250, cfg.wal_sync_ms); } static void test_wal_sync_ms_override_zero_preserves_default(void) { persist_config_t cfg = persist_config_defaults(); /* 0 = flag omitted: cadence must stay byte-for-byte the default * (Req 3.3 — the seam changes timing controllability only). */ persist_config_set_wal_sync_ms(&cfg, 0); TEST_ASSERT_EQUAL_UINT32(DEFAULT_WAL_SYNC_MS, cfg.wal_sync_ms); } static void test_wal_sync_ms_override_leaves_other_fields_intact(void) { persist_config_t cfg = persist_config_defaults(); persist_config_set_wal_sync_ms(&cfg, 7); /* Only the cadence changes; every other persistence default is * preserved (Req 3.3 — no behavioral change beyond timing). */ TEST_ASSERT_EQUAL_UINT32(7, cfg.wal_sync_ms); TEST_ASSERT_EQUAL_UINT32(DEFAULT_SNAP_INTERVAL_S, cfg.snap_interval_s); TEST_ASSERT_EQUAL_UINT32(DEFAULT_SYNC_TIMEOUT_MS, cfg.sync_timeout_ms); TEST_ASSERT_EQUAL_UINT32(DEFAULT_MAX_WAL_RECORDS, cfg.max_wal_records); } /* ------------------------------------------------------------------ */ /* persist_set_workdir derives correct paths */ /* ------------------------------------------------------------------ */ static void test_persist_set_workdir_derives_wal_path(void) { persist_t p; memset(&p, 0, sizeof(p)); persist_set_workdir(&p, "/tmp/xce", "ctrl1"); TEST_ASSERT_EQUAL_STRING("/tmp/xce/ctrl1/data/wal", p.wal_path); } static void test_persist_set_workdir_derives_snap_path(void) { persist_t p; memset(&p, 0, sizeof(p)); persist_set_workdir(&p, "/tmp/xce", "ctrl1"); TEST_ASSERT_EQUAL_STRING("/tmp/xce/ctrl1/data/snap", p.snap_path); } /* ------------------------------------------------------------------ */ /* Suite entry point */ /* ------------------------------------------------------------------ */ void test_cluster_persist(void) { UnitySetTestFile(__FILE__); printf("\n>> cluster persist wiring tests (P0-F)\n\n"); RUN_TEST(test_cluster_t_has_persist_field); RUN_TEST(test_persist_config_defaults_wal_sync_ms); RUN_TEST(test_persist_config_defaults_snap_interval_s); RUN_TEST(test_persist_config_defaults_snap_wal_size_mb); RUN_TEST(test_persist_config_defaults_snap_keep_n); RUN_TEST(test_persist_config_defaults_finished_retain_s); RUN_TEST(test_persist_config_defaults_sync_timeout_ms); RUN_TEST(test_wal_sync_ms_override_applies_positive_value); RUN_TEST(test_wal_sync_ms_override_zero_preserves_default); RUN_TEST(test_wal_sync_ms_override_leaves_other_fields_intact); RUN_TEST(test_persist_set_workdir_derives_wal_path); RUN_TEST(test_persist_set_workdir_derives_snap_path); }