#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "commit_request.hpp" #include TEST_CASE("CommitRequest basic parsing") { CommitRequest request; SUBCASE("Simple commit request") { std::string json = R"({ "request_id": "test123", "leader_id": "leader456", "read_version": 12345 })"; REQUIRE(request.parse_json(json)); REQUIRE(request.request_id().has_value()); REQUIRE(request.request_id().value() == "test123"); REQUIRE(request.leader_id() == "leader456"); REQUIRE(request.read_version() == 12345); } SUBCASE("With preconditions") { std::string json = R"({ "leader_id": "leader456", "read_version": 12345, "preconditions": [ { "type": "point_read", "version": 12340, "key": "dGVzdA==" } ] })"; REQUIRE(request.parse_json(json)); REQUIRE(request.preconditions().size() == 1); REQUIRE(request.preconditions()[0].type == Precondition::Type::PointRead); REQUIRE(request.preconditions()[0].version.has_value()); REQUIRE(request.preconditions()[0].version.value() == 12340); REQUIRE(request.preconditions()[0].key == "test"); // "dGVzdA==" decoded } SUBCASE("With operations") { std::string json = R"({ "leader_id": "leader456", "read_version": 12345, "operations": [ { "type": "write", "key": "dGVzdA==", "value": "dmFsdWU=" }, { "type": "delete", "key": "dGVzdDI=" } ] })"; REQUIRE(request.parse_json(json)); REQUIRE(request.operations().size() == 2); REQUIRE(request.operations()[0].type == Operation::Type::Write); REQUIRE(request.operations()[0].key == "test"); REQUIRE(request.operations()[0].value.has_value()); REQUIRE(request.operations()[0].value.value() == "value"); REQUIRE(request.operations()[1].type == Operation::Type::Delete); REQUIRE(request.operations()[1].key == "test2"); } SUBCASE("Invalid JSON") { std::string json = R"({ "leader_id": "leader456", "read_version": "not_a_number" })"; REQUIRE_FALSE(request.parse_json(json)); } } TEST_CASE("CommitRequest memory management") { CommitRequest request; std::string json = R"({ "request_id": "test123", "leader_id": "leader456", "read_version": 12345, "operations": [ { "type": "write", "key": "dGVzdA==", "value": "dmFsdWU=" } ] })"; REQUIRE(request.parse_json(json)); // Check that arena allocation worked REQUIRE(request.total_allocated() > 0); REQUIRE(request.used_bytes() > 0); // Test reset request.reset(); REQUIRE(request.request_id().has_value() == false); REQUIRE(request.leader_id().empty()); REQUIRE(request.read_version() == 0); REQUIRE(request.operations().empty()); } TEST_CASE("CommitRequest streaming parsing") { CommitRequest request; SUBCASE("Simple streaming parse") { std::string json = R"({ "request_id": "test123", "leader_id": "leader456", "read_version": 12345 })"; REQUIRE(request.begin_streaming_parse()); // Parse in small chunks to simulate network reception std::string mutable_json = json; size_t chunk_size = 10; size_t offset = 0; CommitRequest::ParseStatus status = CommitRequest::ParseStatus::Incomplete; while (offset < mutable_json.size() && status == CommitRequest::ParseStatus::Incomplete) { size_t len = std::min(chunk_size, mutable_json.size() - offset); status = request.parse_chunk(mutable_json.data() + offset, len); offset += len; } if (status == CommitRequest::ParseStatus::Incomplete) { status = request.finish_streaming_parse(); } REQUIRE(status == CommitRequest::ParseStatus::Complete); REQUIRE(request.is_parse_complete()); REQUIRE_FALSE(request.has_parse_error()); REQUIRE(request.request_id().has_value()); REQUIRE(request.request_id().value() == "test123"); REQUIRE(request.leader_id() == "leader456"); REQUIRE(request.read_version() == 12345); } SUBCASE("Streaming parse with complex data") { std::string json = R"({ "request_id": "streaming-test", "leader_id": "leader789", "read_version": 98765, "preconditions": [ { "type": "point_read", "version": 98764, "key": "dGVzdEtleQ==" } ], "operations": [ { "type": "write", "key": "dGVzdEtleQ==", "value": "dGVzdFZhbHVl" }, { "type": "delete", "key": "ZGVsZXRlS2V5" } ] })"; REQUIRE(request.begin_streaming_parse()); // Parse one character at a time to really stress test streaming std::string mutable_json = json; CommitRequest::ParseStatus status = CommitRequest::ParseStatus::Incomplete; for (size_t i = 0; i < mutable_json.size() && status == CommitRequest::ParseStatus::Incomplete; ++i) { status = request.parse_chunk(mutable_json.data() + i, 1); } if (status == CommitRequest::ParseStatus::Incomplete) { status = request.finish_streaming_parse(); } REQUIRE(status == CommitRequest::ParseStatus::Complete); REQUIRE(request.is_parse_complete()); REQUIRE(request.request_id().value() == "streaming-test"); REQUIRE(request.leader_id() == "leader789"); REQUIRE(request.read_version() == 98765); REQUIRE(request.preconditions().size() == 1); REQUIRE(request.operations().size() == 2); // Verify precondition was parsed correctly REQUIRE(request.preconditions()[0].type == Precondition::Type::PointRead); REQUIRE(request.preconditions()[0].version.value() == 98764); REQUIRE(request.preconditions()[0].key == "testKey"); // Verify operations were parsed correctly REQUIRE(request.operations()[0].type == Operation::Type::Write); REQUIRE(request.operations()[0].key == "testKey"); REQUIRE(request.operations()[0].value.value() == "testValue"); REQUIRE(request.operations()[1].type == Operation::Type::Delete); REQUIRE(request.operations()[1].key == "deleteKey"); } SUBCASE("Streaming parse error handling") { std::string invalid_json = R"({ "leader_id": "leader456", "read_version": "invalid_number" })"; REQUIRE(request.begin_streaming_parse()); std::string mutable_json = invalid_json; CommitRequest::ParseStatus status = request.parse_chunk(mutable_json.data(), mutable_json.size()); if (status == CommitRequest::ParseStatus::Incomplete) { status = request.finish_streaming_parse(); } REQUIRE(status == CommitRequest::ParseStatus::Error); REQUIRE(request.has_parse_error()); REQUIRE_FALSE(request.is_parse_complete()); } SUBCASE("Complete document in single chunk") { std::string json = R"({"leader_id": "test", "read_version": 123})"; REQUIRE(request.begin_streaming_parse()); std::string mutable_json = json; CommitRequest::ParseStatus status = request.parse_chunk(mutable_json.data(), mutable_json.size()); // Should still be incomplete (streaming parser doesn't know if more data is // coming) REQUIRE(status == CommitRequest::ParseStatus::Incomplete); // Signal end of input to complete parsing status = request.finish_streaming_parse(); REQUIRE(status == CommitRequest::ParseStatus::Complete); REQUIRE(request.is_parse_complete()); REQUIRE(request.leader_id() == "test"); REQUIRE(request.read_version() == 123); } }