commit missed
This commit is contained in:
parent
a8840dbba5
commit
c45f80ab2b
15 changed files with 14109 additions and 1268 deletions
222
src/lib/testsuite/test_wal_wire.c
Normal file
222
src/lib/testsuite/test_wal_wire.c
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/* test_wal_wire.c — Unity tests for the raw payload wire format
|
||||
* (ha-repl-hardening task 7.1).
|
||||
*
|
||||
* Verifies true encode→decode round-trips for:
|
||||
* - OPCode_walCatchupEntry batches (xce_wal_batch_encode_raw):
|
||||
* per-sub-entry lsn/entry_type and raw_payload bytes survive the wire
|
||||
* - OPCode_snapshotTransfer chunks (xce_snapshot_chunk_encode_raw):
|
||||
* chunk envelope fields and raw_payload bytes survive the wire
|
||||
*
|
||||
* These messages bypass the generated encode path because their
|
||||
* payload :Data fields cannot be carried through xce_data_t (same
|
||||
* limitation as replConfigPush); xce_decode() pre-extracts the bytes
|
||||
* into raw_payload/raw_payload_len before capn_free().
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "request.h"
|
||||
#include "xce/types.h"
|
||||
|
||||
/* ── Helpers ────────────────────────────────────────────────────────────── */
|
||||
|
||||
static xce_peerinfo_t test_peer = { 0 };
|
||||
|
||||
/* encode a batch into a heap io_packet_t (xce_decode frees it) */
|
||||
static io_packet_t *encode_batch(wal_catchup_entry_t **entries, int n)
|
||||
{
|
||||
io_packet_t *pkt = (io_packet_t *)calloc(1, sizeof(*pkt));
|
||||
TEST_ASSERT_NOT_NULL(pkt);
|
||||
int rc = xce_wal_batch_encode_raw(entries, n, &test_peer, 42, pkt);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
return pkt;
|
||||
}
|
||||
|
||||
/* ── WAL batch round-trip ───────────────────────────────────────────────── */
|
||||
|
||||
static void test_wal_batch_roundtrip_payload_bytes(void)
|
||||
{
|
||||
/* Arrange: 2 entries with distinct payloads */
|
||||
uint8_t p0[] = { 0xDE, 0xAD, 0xBE, 0xEF };
|
||||
uint8_t p1[] = "capnp job blob \x00 with embedded NUL";
|
||||
|
||||
wal_catchup_entry_t e0, e1;
|
||||
memset(&e0, 0, sizeof(e0));
|
||||
memset(&e1, 0, sizeof(e1));
|
||||
e0.lsn = 101;
|
||||
e0.entry_type = 1; /* WAL_JOB_SUBMIT */
|
||||
e0.raw_payload = p0;
|
||||
e0.raw_payload_len = sizeof(p0);
|
||||
e1.lsn = 102;
|
||||
e1.entry_type = 2;
|
||||
e1.raw_payload = p1;
|
||||
e1.raw_payload_len = sizeof(p1);
|
||||
|
||||
wal_catchup_entry_t *entries[2] = { &e0, &e1 };
|
||||
|
||||
/* Act: encode → decode */
|
||||
io_packet_t *pkt = encode_batch(entries, 2);
|
||||
xce_request_t req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_decode(pkt, &req));
|
||||
|
||||
/* Assert: opcode, body arm, batch shape */
|
||||
TEST_ASSERT_EQUAL_INT(OPCode_walCatchupEntry, req.opcode);
|
||||
TEST_ASSERT_EQUAL_INT(Request_body_walcatchupentrymsg, req.body_which);
|
||||
TEST_ASSERT_NOT_NULL(req.body.walcatchupentrymsg);
|
||||
|
||||
wal_catchup_entry_t *batch = req.body.walcatchupentrymsg;
|
||||
TEST_ASSERT_EQUAL_INT(2, batch->n_entries);
|
||||
TEST_ASSERT_NOT_NULL(batch->entries);
|
||||
|
||||
/* Entry 0: header + payload bytes intact */
|
||||
TEST_ASSERT_NOT_NULL(batch->entries[0]);
|
||||
TEST_ASSERT_EQUAL_UINT64(101, batch->entries[0]->lsn);
|
||||
TEST_ASSERT_EQUAL_UINT8(1, batch->entries[0]->entry_type);
|
||||
TEST_ASSERT_EQUAL_size_t(sizeof(p0), batch->entries[0]->raw_payload_len);
|
||||
TEST_ASSERT_NOT_NULL(batch->entries[0]->raw_payload);
|
||||
TEST_ASSERT_EQUAL_MEMORY(p0, batch->entries[0]->raw_payload, sizeof(p0));
|
||||
|
||||
/* Entry 1: embedded NUL survives (binary-safe) */
|
||||
TEST_ASSERT_NOT_NULL(batch->entries[1]);
|
||||
TEST_ASSERT_EQUAL_UINT64(102, batch->entries[1]->lsn);
|
||||
TEST_ASSERT_EQUAL_UINT8(2, batch->entries[1]->entry_type);
|
||||
TEST_ASSERT_EQUAL_size_t(sizeof(p1), batch->entries[1]->raw_payload_len);
|
||||
TEST_ASSERT_EQUAL_MEMORY(p1, batch->entries[1]->raw_payload, sizeof(p1));
|
||||
|
||||
xce_free_request(&req);
|
||||
printf("test_wal_batch_roundtrip_payload_bytes: PASS\n");
|
||||
}
|
||||
|
||||
static void test_wal_batch_entry_without_payload(void)
|
||||
{
|
||||
/* An entry with no payload (e.g. header-only types) must decode with
|
||||
* raw_payload == NULL and raw_payload_len == 0 — not garbage. */
|
||||
wal_catchup_entry_t e;
|
||||
memset(&e, 0, sizeof(e));
|
||||
e.lsn = 7;
|
||||
e.entry_type = 3;
|
||||
|
||||
wal_catchup_entry_t *entries[1] = { &e };
|
||||
io_packet_t *pkt = encode_batch(entries, 1);
|
||||
|
||||
xce_request_t req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_decode(pkt, &req));
|
||||
|
||||
wal_catchup_entry_t *batch = req.body.walcatchupentrymsg;
|
||||
TEST_ASSERT_NOT_NULL(batch);
|
||||
TEST_ASSERT_EQUAL_INT(1, batch->n_entries);
|
||||
TEST_ASSERT_EQUAL_UINT64(7, batch->entries[0]->lsn);
|
||||
TEST_ASSERT_NULL(batch->entries[0]->raw_payload);
|
||||
TEST_ASSERT_EQUAL_size_t(0, batch->entries[0]->raw_payload_len);
|
||||
|
||||
xce_free_request(&req);
|
||||
printf("test_wal_batch_entry_without_payload: PASS\n");
|
||||
}
|
||||
|
||||
static void test_wal_batch_encode_rejects_invalid_args(void)
|
||||
{
|
||||
io_packet_t pkt;
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
wal_catchup_entry_t e;
|
||||
memset(&e, 0, sizeof(e));
|
||||
wal_catchup_entry_t *entries[1] = { &e };
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL,
|
||||
xce_wal_batch_encode_raw(NULL, 1, &test_peer, 0, &pkt));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL,
|
||||
xce_wal_batch_encode_raw(entries, 0, &test_peer, 0, &pkt));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL,
|
||||
xce_wal_batch_encode_raw(entries, 1, &test_peer, 0, NULL));
|
||||
|
||||
printf("test_wal_batch_encode_rejects_invalid_args: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Snapshot chunk round-trip ──────────────────────────────────────────── */
|
||||
|
||||
static void test_snapshot_chunk_roundtrip(void)
|
||||
{
|
||||
/* Arrange: a mid-transfer chunk with binary payload */
|
||||
uint8_t payload[300];
|
||||
for (size_t i = 0; i < sizeof(payload); i++)
|
||||
payload[i] = (uint8_t)(i * 7);
|
||||
|
||||
snapshot_transfer_t chunk;
|
||||
memset(&chunk, 0, sizeof(chunk));
|
||||
chunk.snapshot_lsn = 5000;
|
||||
chunk.chunk_index = 3;
|
||||
chunk.chunk_offset = 3 * 65536;
|
||||
chunk.total_size = 4 * 65536 + 300;
|
||||
chunk.is_last = 1;
|
||||
|
||||
io_packet_t *pkt = (io_packet_t *)calloc(1, sizeof(*pkt));
|
||||
TEST_ASSERT_NOT_NULL(pkt);
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
xce_snapshot_chunk_encode_raw(&chunk, payload, sizeof(payload),
|
||||
&test_peer, 9, pkt));
|
||||
|
||||
/* Act */
|
||||
xce_request_t req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_decode(pkt, &req));
|
||||
|
||||
/* Assert: envelope + payload bytes */
|
||||
TEST_ASSERT_EQUAL_INT(OPCode_snapshotTransfer, req.opcode);
|
||||
TEST_ASSERT_EQUAL_INT(Request_body_snapshottransfermsg, req.body_which);
|
||||
snapshot_transfer_t *rx = req.body.snapshottransfermsg;
|
||||
TEST_ASSERT_NOT_NULL(rx);
|
||||
TEST_ASSERT_EQUAL_UINT64(5000, rx->snapshot_lsn);
|
||||
TEST_ASSERT_EQUAL_UINT32(3, rx->chunk_index);
|
||||
TEST_ASSERT_EQUAL_UINT64(3 * 65536, rx->chunk_offset);
|
||||
TEST_ASSERT_EQUAL_UINT64(4 * 65536 + 300, rx->total_size);
|
||||
TEST_ASSERT_TRUE(rx->is_last);
|
||||
TEST_ASSERT_EQUAL_size_t(sizeof(payload), rx->raw_payload_len);
|
||||
TEST_ASSERT_NOT_NULL(rx->raw_payload);
|
||||
TEST_ASSERT_EQUAL_MEMORY(payload, rx->raw_payload, sizeof(payload));
|
||||
|
||||
xce_free_request(&req);
|
||||
printf("test_snapshot_chunk_roundtrip: PASS\n");
|
||||
}
|
||||
|
||||
static void test_snapshot_chunk_empty_payload(void)
|
||||
{
|
||||
/* Zero-length payload (defensive): envelope round-trips, raw NULL/0 */
|
||||
snapshot_transfer_t chunk;
|
||||
memset(&chunk, 0, sizeof(chunk));
|
||||
chunk.snapshot_lsn = 1;
|
||||
chunk.total_size = 0;
|
||||
chunk.is_last = 1;
|
||||
|
||||
io_packet_t *pkt = (io_packet_t *)calloc(1, sizeof(*pkt));
|
||||
TEST_ASSERT_NOT_NULL(pkt);
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
xce_snapshot_chunk_encode_raw(&chunk, NULL, 0, &test_peer, 0, pkt));
|
||||
|
||||
xce_request_t req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_decode(pkt, &req));
|
||||
|
||||
snapshot_transfer_t *rx = req.body.snapshottransfermsg;
|
||||
TEST_ASSERT_NOT_NULL(rx);
|
||||
TEST_ASSERT_EQUAL_UINT64(1, rx->snapshot_lsn);
|
||||
TEST_ASSERT_TRUE(rx->is_last);
|
||||
TEST_ASSERT_NULL(rx->raw_payload);
|
||||
TEST_ASSERT_EQUAL_size_t(0, rx->raw_payload_len);
|
||||
|
||||
xce_free_request(&req);
|
||||
printf("test_snapshot_chunk_empty_payload: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Suite entry point ──────────────────────────────────────────────────── */
|
||||
|
||||
void test_wal_wire(void)
|
||||
{
|
||||
RUN_TEST(test_wal_batch_roundtrip_payload_bytes);
|
||||
RUN_TEST(test_wal_batch_entry_without_payload);
|
||||
RUN_TEST(test_wal_batch_encode_rejects_invalid_args);
|
||||
RUN_TEST(test_snapshot_chunk_roundtrip);
|
||||
RUN_TEST(test_snapshot_chunk_empty_payload);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue