#include "catch.hpp" #include <google/protobuf/util/json_util.h> #include <testpb/messages.pb.h> TEST_CASE("simple struct", "[protobuf]") { auto json = R"( { "int_value": 42, "string_value": "foo bar", "int_arr": [4, 2], "string_arr": ["foo", "bar", "baz"], "int_map": { "foo": 4, "bar": 2, }, "string_map": { "foo": "foo_value", "bar": "bar_value" } } )"; testproto::SimpleStruct msg; google::protobuf::util::JsonParseOptions options; auto status = google::protobuf::util::JsonStringToMessage(json, &msg, options); CHECK(status.error_message() == ""); REQUIRE(status.ok()); CHECK(msg.string_value() == "foo bar"); CHECK(msg.int_value() == 42); CHECK(msg.int_arr().size() == 2); CHECK(msg.int_arr().Get(0) == 4); CHECK(msg.int_arr().Get(1) == 2); CHECK(msg.string_arr().size() == 3); std::vector<std::string> expected{"foo", "bar", "baz"}; CHECK( std::equal( msg.string_arr().begin(), msg.string_arr().end(), expected.begin(), expected.end() ) ); CHECK(msg.int_map_size() == 2); CHECK(msg.int_map().at("foo") == 4); CHECK(msg.int_map().at("bar") == 2); CHECK(msg.string_map_size() == 2); CHECK(msg.string_map().at("foo") == "foo_value"); CHECK(msg.string_map().at("bar") == "bar_value"); } TEST_CASE("invalid json", "[protobuf]") { auto json = R"( { "string_value": invalid, } )"; testproto::SimpleStruct msg; google::protobuf::util::JsonParseOptions options; auto status = JsonStringToMessage(json, &msg, options); REQUIRE_FALSE(status.ok()); } TEST_CASE("null/missing integer value", "[protobuf]") { auto json = R"( { "int_value": null, "string_value": "foo bar", } )"; google::protobuf::util::JsonParseOptions options; testproto::SimpleStruct msg; auto status = google::protobuf::util::JsonStringToMessage(json, &msg, options); REQUIRE(status.ok()); REQUIRE(msg.string_value() == "foo bar"); REQUIRE(msg.int_value() == 0); json = R"( { "string_value": "foo bar", } )"; msg = testproto::SimpleStruct{}; status = google::protobuf::util::JsonStringToMessage(json, &msg, options); REQUIRE(status.ok()); REQUIRE(msg.string_value() == "foo bar"); REQUIRE(msg.int_value() == 0); } TEST_CASE("json marshal naming", "[protobuf]") { testproto::JsonNaming msg; msg.set_foo_value(1); msg.set_barvalue(2); msg.set_bazvalue(3); msg.set_qux_value(4); std::string json; google::protobuf::util::JsonOptions options; options.add_whitespace = true; options.preserve_proto_field_names = true; options.always_print_primitive_fields = true; options.always_print_enums_as_ints = true; auto status = google::protobuf::util::MessageToJsonString(msg, &json, options); auto expected = R"({ "foo_value": 1, "barValue": 2, "BazValue": 3, "quX_Value": 4 } )"; REQUIRE(json == expected); } TEST_CASE("json unmarshal naming", "[protobuf]") { SECTION("accept snake case") { auto json = R"( { "quX_Value": 42, } )"; testproto::JsonNaming msg; google::protobuf::util::JsonParseOptions options; auto status = google::protobuf::util::JsonStringToMessage(json, &msg, options); CHECK(status.error_message() == ""); REQUIRE(status.ok()); CHECK(msg.qux_value() == 42); } SECTION("accept camelCase case") { auto json = R"( { "barValue": 42, } )"; testproto::JsonNaming msg; google::protobuf::util::JsonParseOptions options; auto status = google::protobuf::util::JsonStringToMessage(json, &msg, options); CHECK(status.error_message() == ""); REQUIRE(status.ok()); CHECK(msg.barvalue() == 42); } SECTION("accept CamelCase case") { auto json = R"( { "BazValue": 42, } )"; testproto::JsonNaming msg; google::protobuf::util::JsonParseOptions options; auto status = google::protobuf::util::JsonStringToMessage(json, &msg, options); CHECK(status.error_message() == ""); REQUIRE(status.ok()); CHECK(msg.bazvalue() == 42); } } TEST_CASE("oneof unmarshal", "[protobuf]") { SECTION("accept string value") { auto json = R"( { "string_value": "foo bar", } )"; testproto::OneofValue msg; google::protobuf::util::JsonParseOptions options; auto status = google::protobuf::util::JsonStringToMessage(json, &msg, options); CHECK(status.error_message() == ""); REQUIRE(status.ok()); CHECK(msg.data_case() == testproto::OneofValue::kStringValue); CHECK_FALSE(msg.has_struct_value()); CHECK(msg.string_value() == "foo bar"); } SECTION("accept struct value") { auto json = R"( { "struct_value": { "int_value": 42 } } )"; testproto::OneofValue msg; google::protobuf::util::JsonParseOptions options; auto status = google::protobuf::util::JsonStringToMessage(json, &msg, options); CHECK(status.error_message() == ""); REQUIRE(status.ok()); CHECK(msg.data_case() == testproto::OneofValue::kStructValue); CHECK(msg.has_struct_value()); CHECK(msg.struct_value().int_value() == 42); } } TEST_CASE("oneof marshal", "[protobuf]") { SECTION("marshal string value") { testproto::OneofValue msg; msg.set_string_value("foo bar"); google::protobuf::util::JsonPrintOptions options; options.preserve_proto_field_names = true; std::string json; auto status = google::protobuf::util::MessageToJsonString(msg, &json, options); auto expected = R"({"string_value":"foo bar"})"; CHECK(status.error_message() == ""); REQUIRE(status.ok()); CHECK(json == expected); } SECTION("marshal struct value") { testproto::OneofValue msg; msg.mutable_struct_value()->set_int_value(42); google::protobuf::util::JsonPrintOptions options; options.preserve_proto_field_names = true; std::string json; auto status = google::protobuf::util::MessageToJsonString(msg, &json, options); auto expected = R"({"struct_value":{"int_value":"42"}})"; CHECK(status.error_message() == ""); REQUIRE(status.ok()); CHECK(json == expected); } SECTION("marshal test") { testproto::OneofValue msg; msg.mutable_struct_value()->set_int_value(42); google::protobuf::util::JsonPrintOptions options; options.preserve_proto_field_names = true; std::string json; auto status = google::protobuf::util::MessageToJsonString(msg, &json, options); auto expected = R"({"struct_value":{"int_value":"42"}})"; CHECK(status.error_message() == ""); REQUIRE(status.ok()); CHECK(json == expected); } }