482 lines
14 KiB
C++
482 lines
14 KiB
C++
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
|
#include "../benchmarks/test_data.hpp"
|
|
#include "parser_comparison.hpp"
|
|
#include <doctest/doctest.h>
|
|
|
|
/**
|
|
* @brief Test helper that uses parser comparison to validate both parsers.
|
|
*
|
|
* This function tests that either both parsers reject the input, or both
|
|
* accept it and produce equivalent CommitRequest objects.
|
|
*/
|
|
void test_parser_comparison(const std::string &json_str,
|
|
const std::string &test_name) {
|
|
INFO("Testing: " << test_name);
|
|
INFO("JSON: " << json_str);
|
|
|
|
auto result = ParserComparison::compare_parsers(json_str);
|
|
|
|
INFO("Comparison result: " << ParserComparison::result_to_string(result));
|
|
if (!ParserComparison::get_last_error().empty()) {
|
|
INFO("Error details: " << ParserComparison::get_last_error());
|
|
}
|
|
|
|
// Accept either both success or both failure - reject inconsistencies
|
|
REQUIRE((result == ParserComparison::ComparisonResult::BothSuccess ||
|
|
result == ParserComparison::ComparisonResult::BothFailure));
|
|
}
|
|
|
|
TEST_CASE("Parser Comparison - Basic Cases") {
|
|
SUBCASE("Simple valid request") {
|
|
test_parser_comparison(R"({
|
|
"request_id": "test123",
|
|
"leader_id": "leader456",
|
|
"read_version": 12345
|
|
})",
|
|
"Simple valid request");
|
|
}
|
|
|
|
SUBCASE("Minimal valid request (no request_id)") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345
|
|
})",
|
|
"Minimal valid request");
|
|
}
|
|
|
|
SUBCASE("Request with preconditions") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"preconditions": [
|
|
{
|
|
"type": "point_read",
|
|
"version": 12340,
|
|
"key": "dGVzdA=="
|
|
}
|
|
]
|
|
})",
|
|
"Request with preconditions");
|
|
}
|
|
|
|
SUBCASE("Request with operations") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"operations": [
|
|
{
|
|
"type": "write",
|
|
"key": "dGVzdA==",
|
|
"value": "dmFsdWU="
|
|
},
|
|
{
|
|
"type": "delete",
|
|
"key": "dGVzdDI="
|
|
}
|
|
]
|
|
})",
|
|
"Request with operations");
|
|
}
|
|
|
|
SUBCASE("Complex request with both preconditions and operations") {
|
|
test_parser_comparison(R"({
|
|
"request_id": "complex-test",
|
|
"leader_id": "leader789",
|
|
"read_version": 98765,
|
|
"preconditions": [
|
|
{
|
|
"type": "point_read",
|
|
"version": 98764,
|
|
"key": "dGVzdEtleQ=="
|
|
},
|
|
{
|
|
"type": "range_read",
|
|
"begin": "c3RhcnQ=",
|
|
"end": "ZW5k"
|
|
}
|
|
],
|
|
"operations": [
|
|
{
|
|
"type": "write",
|
|
"key": "dGVzdEtleQ==",
|
|
"value": "dGVzdFZhbHVl"
|
|
},
|
|
{
|
|
"type": "delete",
|
|
"key": "ZGVsZXRlS2V5"
|
|
},
|
|
{
|
|
"type": "range_delete",
|
|
"begin": "cmFuZ2VTdGFydA==",
|
|
"end": "cmFuZ2VFbmQ="
|
|
}
|
|
]
|
|
})",
|
|
"Complex request");
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Parser Comparison - Invalid Cases") {
|
|
SUBCASE("Invalid JSON syntax") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": "not_a_number"
|
|
})",
|
|
"Invalid JSON - string for number");
|
|
}
|
|
|
|
SUBCASE("Missing required leader_id") {
|
|
test_parser_comparison(R"({
|
|
"request_id": "test123",
|
|
"read_version": 12345
|
|
})",
|
|
"Missing leader_id");
|
|
}
|
|
|
|
SUBCASE("Missing required read_version") {
|
|
test_parser_comparison(R"({
|
|
"request_id": "test123",
|
|
"leader_id": "leader456"
|
|
})",
|
|
"Missing read_version");
|
|
}
|
|
|
|
SUBCASE("Empty leader_id") {
|
|
test_parser_comparison(R"({
|
|
"request_id": "test123",
|
|
"leader_id": "",
|
|
"read_version": 12345
|
|
})",
|
|
"Empty leader_id");
|
|
}
|
|
|
|
SUBCASE("Invalid precondition - missing key") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"preconditions": [
|
|
{
|
|
"type": "point_read"
|
|
}
|
|
]
|
|
})",
|
|
"Invalid precondition - missing key");
|
|
}
|
|
|
|
SUBCASE("Invalid precondition - missing begin for range_read") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"preconditions": [
|
|
{
|
|
"type": "range_read",
|
|
"end": "dGVzdFo="
|
|
}
|
|
]
|
|
})",
|
|
"Invalid precondition - missing begin");
|
|
}
|
|
|
|
SUBCASE("Invalid precondition - missing end for range_read") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"preconditions": [
|
|
{
|
|
"type": "range_read",
|
|
"begin": "dGVzdA=="
|
|
}
|
|
]
|
|
})",
|
|
"Invalid precondition - missing end");
|
|
}
|
|
|
|
SUBCASE("Invalid operation - missing key for write") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"operations": [
|
|
{
|
|
"type": "write",
|
|
"value": "dmFsdWU="
|
|
}
|
|
]
|
|
})",
|
|
"Invalid operation - missing key");
|
|
}
|
|
|
|
SUBCASE("Invalid operation - missing value for write") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"operations": [
|
|
{
|
|
"type": "write",
|
|
"key": "dGVzdA=="
|
|
}
|
|
]
|
|
})",
|
|
"Invalid operation - missing value");
|
|
}
|
|
|
|
SUBCASE("Invalid operation - missing key for delete") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"operations": [
|
|
{
|
|
"type": "delete"
|
|
}
|
|
]
|
|
})",
|
|
"Invalid operation - missing key for delete");
|
|
}
|
|
|
|
SUBCASE("Invalid operation - missing begin for range_delete") {
|
|
test_parser_comparison(
|
|
R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"operations": [
|
|
{
|
|
"type": "range_delete",
|
|
"end": "dGVzdFo="
|
|
}
|
|
]
|
|
})",
|
|
"Invalid operation - missing begin for range_delete");
|
|
}
|
|
|
|
SUBCASE("Invalid operation - missing end for range_delete") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"operations": [
|
|
{
|
|
"type": "range_delete",
|
|
"begin": "dGVzdA=="
|
|
}
|
|
]
|
|
})",
|
|
"Invalid operation - missing end for range_delete");
|
|
}
|
|
|
|
SUBCASE("Malformed JSON") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456"
|
|
"read_version": 12345
|
|
})",
|
|
"Malformed JSON - missing comma");
|
|
}
|
|
|
|
SUBCASE("Invalid precondition type") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"preconditions": [
|
|
{
|
|
"type": "invalid_type",
|
|
"key": "dGVzdA=="
|
|
}
|
|
]
|
|
})",
|
|
"Invalid precondition type");
|
|
}
|
|
|
|
SUBCASE("Invalid operation type") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"operations": [
|
|
{
|
|
"type": "invalid_operation",
|
|
"key": "dGVzdA=="
|
|
}
|
|
]
|
|
})",
|
|
"Invalid operation type");
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Parser Comparison - Edge Cases") {
|
|
SUBCASE("Empty arrays") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"preconditions": [],
|
|
"operations": []
|
|
})",
|
|
"Empty arrays");
|
|
}
|
|
|
|
SUBCASE("Empty base64 strings") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"operations": [
|
|
{
|
|
"type": "write",
|
|
"key": "",
|
|
"value": ""
|
|
}
|
|
]
|
|
})",
|
|
"Empty base64 strings");
|
|
}
|
|
|
|
SUBCASE("Zero read_version") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 0
|
|
})",
|
|
"Zero read_version");
|
|
}
|
|
|
|
SUBCASE("Large read_version") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 18446744073709551615
|
|
})",
|
|
"Large read_version");
|
|
}
|
|
|
|
SUBCASE("Precondition with zero version") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"preconditions": [
|
|
{
|
|
"type": "point_read",
|
|
"version": 0,
|
|
"key": "dGVzdA=="
|
|
}
|
|
]
|
|
})",
|
|
"Precondition with zero version");
|
|
}
|
|
|
|
SUBCASE("Range operations with empty keys") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "leader456",
|
|
"read_version": 12345,
|
|
"preconditions": [
|
|
{
|
|
"type": "range_read",
|
|
"begin": "",
|
|
"end": ""
|
|
}
|
|
],
|
|
"operations": [
|
|
{
|
|
"type": "range_delete",
|
|
"begin": "",
|
|
"end": ""
|
|
}
|
|
]
|
|
})",
|
|
"Range operations with empty keys");
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Parser Comparison - Test Data") {
|
|
SUBCASE("SIMPLE_JSON") {
|
|
test_parser_comparison(weaseldb::test_data::SIMPLE_JSON,
|
|
"SIMPLE_JSON test data");
|
|
}
|
|
|
|
SUBCASE("MEDIUM_JSON") {
|
|
test_parser_comparison(weaseldb::test_data::MEDIUM_JSON,
|
|
"MEDIUM_JSON test data");
|
|
}
|
|
|
|
SUBCASE("COMPLEX_JSON") {
|
|
test_parser_comparison(weaseldb::test_data::COMPLEX_JSON,
|
|
"COMPLEX_JSON test data");
|
|
}
|
|
|
|
SUBCASE("Generated large JSON") {
|
|
// Test with a reasonably sized generated JSON to avoid extremely long test
|
|
// times
|
|
std::string large_json = weaseldb::test_data::generate_large_json(100);
|
|
test_parser_comparison(large_json, "Generated large JSON (100 operations)");
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Parser Comparison - Stress Tests") {
|
|
SUBCASE("Multiple operations of each type") {
|
|
test_parser_comparison(R"({
|
|
"request_id": "stress-test-operations",
|
|
"leader_id": "stress-leader",
|
|
"read_version": 1000000,
|
|
"operations": [
|
|
{
|
|
"type": "write",
|
|
"key": "a2V5MQ==",
|
|
"value": "dmFsdWUx"
|
|
},
|
|
{
|
|
"type": "write",
|
|
"key": "a2V5Mg==",
|
|
"value": "dmFsdWUy"
|
|
},
|
|
{
|
|
"type": "delete",
|
|
"key": "a2V5Mw=="
|
|
},
|
|
{
|
|
"type": "delete",
|
|
"key": "a2V5NA=="
|
|
},
|
|
{
|
|
"type": "range_delete",
|
|
"begin": "cmFuZ2UxX3N0YXJ0",
|
|
"end": "cmFuZ2UxX2VuZA=="
|
|
},
|
|
{
|
|
"type": "range_delete",
|
|
"begin": "cmFuZ2UyX3N0YXJ0",
|
|
"end": "cmFuZ2UyX2VuZA=="
|
|
}
|
|
]
|
|
})",
|
|
"Multiple operations of each type");
|
|
}
|
|
|
|
SUBCASE("Multiple preconditions of each type") {
|
|
test_parser_comparison(R"({
|
|
"leader_id": "stress-leader",
|
|
"read_version": 2000000,
|
|
"preconditions": [
|
|
{
|
|
"type": "point_read",
|
|
"version": 1999999,
|
|
"key": "cG9pbnQx"
|
|
},
|
|
{
|
|
"type": "point_read",
|
|
"version": 1999998,
|
|
"key": "cG9pbnQy"
|
|
},
|
|
{
|
|
"type": "range_read",
|
|
"version": 1999997,
|
|
"begin": "cmFuZ2UxX3N0YXJ0",
|
|
"end": "cmFuZ2UxX2VuZA=="
|
|
},
|
|
{
|
|
"type": "range_read",
|
|
"begin": "cmFuZ2UyX3N0YXJ0",
|
|
"end": "cmFuZ2UyX2VuZA=="
|
|
}
|
|
]
|
|
})",
|
|
"Multiple preconditions of each type");
|
|
}
|
|
|
|
SUBCASE("Deep nesting with many operations") {
|
|
// Use the generate_large_json function which now properly encodes base64
|
|
std::string stress_json = weaseldb::test_data::generate_large_json(50);
|
|
test_parser_comparison(stress_json, "Deep nesting with many operations");
|
|
}
|
|
}
|