test(raft-ha-core-safety): machine-checkable walcompare report (task 1.1)
Per-member (lsn,term,pcrc,present) report + committed-vs-uncommitted divergence classification via walcompare_report()/--report. Soak signal (OK/exit-code) preserved. 4 new unit tests, all GREEN.
This commit is contained in:
parent
227f592250
commit
b028d25dbd
5 changed files with 453 additions and 120 deletions
|
|
@ -354,6 +354,160 @@ static void test_too_few_members_errors(void)
|
|||
TEST_ASSERT_LESS_THAN_INT(0, rc);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Machine-checkable report: a committed-prefix divergence must emit a */
|
||||
/* parseable per-member line set and a committed-vs-uncommitted */
|
||||
/* classification (task 1.1, R7.1/R7.5). Captured via open_memstream so */
|
||||
/* the exact greppable tokens are asserted, not just the verdict. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void test_machine_report_committed_divergence(void)
|
||||
{
|
||||
wal_spec_t a_specs[] = {
|
||||
{ 1, WAL_NOOP, NULL },
|
||||
{ 1, WAL_JOB_SUBMIT, "jobA" },
|
||||
{ 2, WAL_JOB_SUBMIT, "jobB" }, /* LSN 3, term 2 */
|
||||
};
|
||||
wal_spec_t b_specs[] = {
|
||||
{ 1, WAL_NOOP, NULL },
|
||||
{ 1, WAL_JOB_SUBMIT, "jobA" },
|
||||
{ 3, WAL_JOB_SUBMIT, "jobB" }, /* LSN 3, term 3 — divergent */
|
||||
};
|
||||
char a[256], b[256];
|
||||
build_member(a, sizeof(a), a_specs, 3);
|
||||
build_member(b, sizeof(b), b_specs, 3);
|
||||
|
||||
char* buf = NULL;
|
||||
size_t len = 0;
|
||||
FILE* out = open_memstream(&buf, &len);
|
||||
TEST_ASSERT_NOT_NULL(out);
|
||||
|
||||
walcompare_member_t m[2] = { { a, 0 }, { b, 0 } };
|
||||
walcompare_divergence_t div;
|
||||
int rc = walcompare(m, 2, out, &div);
|
||||
fclose(out);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(WALCMP_DIVERGENT, rc);
|
||||
/* committed-vs-uncommitted classification line. */
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "WALCMP_DIVERGENCE"));
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "class=committed"));
|
||||
/* parseable per-member lines with fixed field order. */
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "WALCMP member=0 lsn=3"));
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "WALCMP member=1 lsn=3"));
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "status=present"));
|
||||
/* divergence struct carries the committed classification flag. */
|
||||
TEST_ASSERT_EQUAL_INT(1, div.committed);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* walcompare_report emits a full per-member, per-committed-LSN table */
|
||||
/* (lsn, term, pcrc, status) for identical members and a clean verdict. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void test_report_full_member_table(void)
|
||||
{
|
||||
wal_spec_t specs[] = {
|
||||
{ 1, WAL_NOOP, NULL },
|
||||
{ 1, WAL_JOB_SUBMIT, "jobA" },
|
||||
{ 2, WAL_JOB_SUBMIT, "jobB" },
|
||||
};
|
||||
char a[256], b[256], c[256];
|
||||
build_member(a, sizeof(a), specs, 3);
|
||||
build_member(b, sizeof(b), specs, 3);
|
||||
build_member(c, sizeof(c), specs, 3);
|
||||
|
||||
char* buf = NULL;
|
||||
size_t len = 0;
|
||||
FILE* out = open_memstream(&buf, &len);
|
||||
TEST_ASSERT_NOT_NULL(out);
|
||||
|
||||
walcompare_member_t m[3] = { { a, 0 }, { b, 0 }, { c, 0 } };
|
||||
walcompare_divergence_t div;
|
||||
int rc = walcompare_report(m, 3, out, &div);
|
||||
fclose(out);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(WALCMP_OK, rc);
|
||||
/* every member appears at every committed LSN with committed=1. */
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "WALCMP member=0 lsn=1"));
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "WALCMP member=2 lsn=3"));
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "committed=1"));
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "status=present"));
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* A MISSING committed entry is tagged status=MISSING in the report, */
|
||||
/* and classified class=committed (a committed-prefix hole, R4.2/R6.4). */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void test_report_missing_tagged(void)
|
||||
{
|
||||
wal_spec_t a_specs[] = {
|
||||
{ 1, WAL_NOOP, NULL },
|
||||
{ 1, WAL_JOB_SUBMIT, "jobA" },
|
||||
{ 2, WAL_JOB_SUBMIT, "jobB" },
|
||||
};
|
||||
wal_spec_t b_specs[] = {
|
||||
{ 1, WAL_NOOP, NULL },
|
||||
{ 1, WAL_JOB_SUBMIT, "jobA" },
|
||||
};
|
||||
char a[256], b[256];
|
||||
build_member(a, sizeof(a), a_specs, 3);
|
||||
build_member(b, sizeof(b), b_specs, 2);
|
||||
|
||||
char* buf = NULL;
|
||||
size_t len = 0;
|
||||
FILE* out = open_memstream(&buf, &len);
|
||||
TEST_ASSERT_NOT_NULL(out);
|
||||
|
||||
walcompare_member_t m[2] = { { a, 3 }, { b, 3 } };
|
||||
walcompare_divergence_t div;
|
||||
int rc = walcompare_report(m, 2, out, &div);
|
||||
fclose(out);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(WALCMP_DIVERGENT, rc);
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "status=MISSING"));
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "class=committed"));
|
||||
TEST_ASSERT_EQUAL_INT(1, div.committed);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* A divergence in the UNCOMMITTED tail (above the committed boundary) is */
|
||||
/* reported class=uncommitted and is NON-fatal: the committed verdict is */
|
||||
/* still WALCMP_OK (task 1.1 classification; §6.1 committed-prefix only). */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void test_report_uncommitted_tail_classified(void)
|
||||
{
|
||||
wal_spec_t a_specs[] = {
|
||||
{ 1, WAL_NOOP, NULL },
|
||||
{ 1, WAL_JOB_SUBMIT, "jobA" }, /* committed LSN 2 */
|
||||
{ 2, WAL_JOB_SUBMIT, "uncA" }, /* LSN 3 uncommitted, divergent */
|
||||
};
|
||||
wal_spec_t b_specs[] = {
|
||||
{ 1, WAL_NOOP, NULL },
|
||||
{ 1, WAL_JOB_SUBMIT, "jobA" },
|
||||
{ 9, WAL_JOB_SUBMIT, "uncB" }, /* LSN 3, different term+bytes */
|
||||
};
|
||||
char a[256], b[256];
|
||||
build_member(a, sizeof(a), a_specs, 3);
|
||||
build_member(b, sizeof(b), b_specs, 3);
|
||||
|
||||
char* buf = NULL;
|
||||
size_t len = 0;
|
||||
FILE* out = open_memstream(&buf, &len);
|
||||
TEST_ASSERT_NOT_NULL(out);
|
||||
|
||||
/* committed boundary pinned at 2 → LSN-3 divergence is the tail. */
|
||||
walcompare_member_t m[2] = { { a, 2 }, { b, 2 } };
|
||||
walcompare_divergence_t div;
|
||||
int rc = walcompare_report(m, 2, out, &div);
|
||||
fclose(out);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(WALCMP_OK, rc); /* tail divergence is non-fatal */
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "class=uncommitted"));
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "committed=0"));
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void test_walcompare(void)
|
||||
{
|
||||
RUN_TEST(test_identical_members_ok);
|
||||
|
|
@ -367,4 +521,8 @@ void test_walcompare(void)
|
|||
RUN_TEST(test_three_members_divergent_config_marker);
|
||||
RUN_TEST(test_missing_member_errors);
|
||||
RUN_TEST(test_too_few_members_errors);
|
||||
RUN_TEST(test_machine_report_committed_divergence);
|
||||
RUN_TEST(test_report_full_member_table);
|
||||
RUN_TEST(test_report_missing_tagged);
|
||||
RUN_TEST(test_report_uncommitted_tail_classified);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue