/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include namespace facebook { TEST(JsonTest, structKey) { Small s; SubStruct stru; std::map m; m.insert({s, 1}); stru.key_map() = m; SubStruct outStruct; auto t = apache::thrift::SimpleJSONSerializer::serialize(stru); ASSERT_EQ( t, "{\"req_def\":\"IAMREQ\",\"key_map\":{{\"num\":0,\"two\":0}:1},\"bin\":\"\"}"); apache::thrift::SimpleJSONSerializer::deserialize(t, outStruct); ASSERT_EQ(stru, outStruct); } TEST(JsonTest, weirdText) { SubStruct stru; stru.optDef() = "stuff\twith\nescape\\characters'...\"lots{of}fun"; stru.bin() = "1234"; SubStruct outStruct; auto t = apache::thrift::SimpleJSONSerializer::serialize(stru); ASSERT_EQ( t, "{\"optDef\":\"stuff\\twith\\nescape\\\\characters'...\\\"lots{of}fun\"," "\"req_def\":\"IAMREQ\",\"bin\":\"MTIzNA\"}"); apache::thrift::SimpleJSONSerializer::deserialize(t, outStruct); ASSERT_EQ(stru, outStruct); stru.optDef() = "UNICODE\U0001F60AUH OH"; t = apache::thrift::SimpleJSONSerializer::serialize(stru); ASSERT_EQ( t, "{\"optDef\":\"UNICODE\xf0\x9f\x98\x8aUH OH\"," "\"req_def\":\"IAMREQ\",\"bin\":\"MTIzNA\"}"); apache::thrift::SimpleJSONSerializer::deserialize(t, outStruct); ASSERT_EQ(stru, outStruct); } TEST(JsonTest, skipComplex) { SubStruct stru; stru.optDef() = "thing"; stru.bin() = "1234"; SubStruct outStruct; std::string input( "{\"optDef\":\"thing\"," "\"req_def\":\"IAMREQ\",\"bin\":\"MTIzNA\"," "\"extra\":[1, {\"thing\":\"thing2\"}]," "\"extra_map\":{\"thing\":null,\"thing2\":2}," "\"extra_bool\":true}"); apache::thrift::SimpleJSONSerializer::deserialize(input, outStruct); ASSERT_EQ(stru, outStruct); } TEST(JsonTest, needCommas) { Small outStruct; // Note the missing commas std::string input( "{\"num\":1" "\"two\":2}"); EXPECT_THROW( apache::thrift::SimpleJSONSerializer::deserialize(input, outStruct), std::exception); // even when skipping std::string input2( "{\"num\":1," "\"two\":2," "\"extra_map\":{\"thing\":null,\"thing2\":2}" "\"extra_bool\":true}"); EXPECT_THROW( apache::thrift::SimpleJSONSerializer::deserialize(input2, outStruct), std::exception); } TEST(JsonTest, needCommasContainers) { Containers outStruct; std::string goodinput("{\"m\":{\"m1\":\"m1\",\"m2\":\"m2\"}}"); // Note the missing comma std::string badinput("{\"m\":{\"m1\":\"m1\"\"m2\":\"m2\"}}"); // we can serialize the good input apache::thrift::SimpleJSONSerializer::deserialize(goodinput, outStruct); EXPECT_THROW( apache::thrift::SimpleJSONSerializer::deserialize(badinput, outStruct), std::exception); std::string goodinput2("{\"l\":[\"l1\",\"l2\"]}"); // Note the missing comma std::string badinput2("{\"l\":[\"l1\"\"l2\"]}"); // we can serialize the good input apache::thrift::SimpleJSONSerializer::deserialize(goodinput2, outStruct); EXPECT_THROW( apache::thrift::SimpleJSONSerializer::deserialize(badinput2, outStruct), std::exception); } TEST(JsonTest, nullStuffDeser) { // NOTE: Keep in sync with `test_null_stuff_deser` test in simplejson.rs SubStruct stru; stru.bin() = "1234"; const std::string_view inputs[] = { R"({ "req_def": "IAMREQ", "bin": "MTIzNA" })", R"({"optDef": null, "req_def": "IAMREQ", "bin": "MTIzNA" })", }; for (const auto& input : inputs) { SubStruct outStruct; apache::thrift::SimpleJSONSerializer::deserialize(input, outStruct); EXPECT_EQ(stru, outStruct) << input; } } TEST(JsonTest, unknownUnion) { Un u; auto t = apache::thrift::SimpleJSONSerializer::serialize(u); ASSERT_EQ(t, "{}"); } TEST(DefaultsTest, defaults) { // NOTE: Keep in sync with `test_defaults` test in defaults.rs SubStruct stru; ASSERT_FALSE(stru.optDef().has_value()); ASSERT_EQ("IAMREQ", stru.req_def()); ASSERT_FALSE(stru.key_map().has_value()); ASSERT_TRUE(stru.bin()->empty()); } TEST(ConstsTest, consts) { // NOTE: Keep in sync with `test_consts` test in consts.rs const auto def = test_thrift_constants::DEFAULT_SUBSTRUCT(); ASSERT_EQ(SubStruct(), def); const auto custom = test_thrift_constants::CUSTOM_SUBSTRUCT(); SubStruct expected; expected.optDef() = "CUSTOM_OPT_DEF"; expected.req_def() = "CUSTOM_REQ_DEF"; Small key; key.num() = 4; key.two() = 2; expected.key_map() = {{key, 42}}; expected.bin() = "0123456789"; ASSERT_EQ(expected, custom); } } // namespace facebook