#include "json.h" #include "proto.h" #include "proto2json.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace NProtobufJson; using namespace NProtobufJsonTest; namespace google { namespace protobuf { namespace internal { void MapTestForceDeterministic() { google::protobuf::io::CodedOutputStream::SetDefaultSerializationDeterministic(); } } } // namespace protobuf } namespace { class TInit { public: TInit() { ::google::protobuf::internal::MapTestForceDeterministic(); } } Init; template TString ConvertToString(T value) { return ToString(value); } // default ToString() implementation loses precision TString ConvertToString(double value) { return FloatToString(value); } TString JsonValueToString(const NJson::TJsonValue& json) { NJsonWriter::TBuf buf(NJsonWriter::HEM_UNSAFE); return buf.WriteJsonValue(&json).Str(); } void TestComplexMapAsObject(std::function&& init, const TString& json, const TJson2ProtoConfig& config = TJson2ProtoConfig().SetMapAsObject(true)) { TComplexMapType modelProto; init(modelProto); TString modelStr(json); TComplexMapType proto; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } } Y_UNIT_TEST_SUITE(TJson2ProtoTest) { Y_UNIT_TEST(TestFlatOptional){ {const NJson::TJsonValue& json = CreateFlatJson(); TFlatOptional proto; Json2Proto(json, proto); TFlatOptional modelProto; FillFlatProto(&modelProto); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // Try to skip each field #define DEFINE_FIELD(name, value) \ { \ THashSet skippedField; \ skippedField.insert(#name); \ const NJson::TJsonValue& json = CreateFlatJson(skippedField); \ TFlatOptional proto; \ Json2Proto(json, proto); \ TFlatOptional modelProto; \ FillFlatProto(&modelProto, skippedField); \ UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); \ } #include #undef DEFINE_FIELD } // TestFlatOptional Y_UNIT_TEST(TestFlatRequired){ {const NJson::TJsonValue& json = CreateFlatJson(); TFlatRequired proto; Json2Proto(json, proto); TFlatRequired modelProto; FillFlatProto(&modelProto); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // Try to skip each field #define DEFINE_FIELD(name, value) \ { \ THashSet skippedField; \ skippedField.insert(#name); \ const NJson::TJsonValue& json = CreateFlatJson(skippedField); \ TFlatRequired proto; \ UNIT_ASSERT_EXCEPTION(Json2Proto(json, proto), yexception); \ } #include #undef DEFINE_FIELD } // TestFlatRequired Y_UNIT_TEST(TestNameGenerator) { TJson2ProtoConfig cfg; cfg.SetNameGenerator([](const NProtoBuf::FieldDescriptor&) { return "42"; }); TNameGeneratorType proto; Json2Proto(TStringBuf(R"({"42":42})"), proto, cfg); TNameGeneratorType expected; expected.SetField(42); UNIT_ASSERT_PROTOS_EQUAL(expected, proto); } Y_UNIT_TEST(TestFlatNoCheckRequired) { { const NJson::TJsonValue& json = CreateFlatJson(); TFlatRequired proto; Json2Proto(json, proto); TFlatRequired modelProto; FillFlatProto(&modelProto); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } TJson2ProtoConfig cfg; cfg.CheckRequiredFields = false; // Try to skip each field #define DEFINE_FIELD(name, value) \ { \ THashSet skippedField; \ skippedField.insert(#name); \ const NJson::TJsonValue& json = CreateFlatJson(skippedField); \ TFlatRequired proto; \ UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto, cfg)); \ } #include #undef DEFINE_FIELD } // TestFlatNoCheckRequired Y_UNIT_TEST(TestFlatRepeated){ {const NJson::TJsonValue& json = CreateRepeatedFlatJson(); TFlatRepeated proto; Json2Proto(json, proto); TFlatRepeated modelProto; FillRepeatedProto(&modelProto); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // Try to skip each field #define DEFINE_REPEATED_FIELD(name, ...) \ { \ THashSet skippedField; \ skippedField.insert(#name); \ const NJson::TJsonValue& json = CreateRepeatedFlatJson(skippedField); \ TFlatRepeated proto; \ Json2Proto(json, proto); \ TFlatRepeated modelProto; \ FillRepeatedProto(&modelProto, skippedField); \ UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); \ } #include #undef DEFINE_REPEATED_FIELD } // TestFlatRepeated Y_UNIT_TEST(TestCompositeOptional){ {const NJson::TJsonValue& json = CreateCompositeJson(); TCompositeOptional proto; Json2Proto(json, proto); TCompositeOptional modelProto; FillCompositeProto(&modelProto); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // Try to skip each field #define DEFINE_FIELD(name, value) \ { \ THashSet skippedField; \ skippedField.insert(#name); \ const NJson::TJsonValue& json = CreateCompositeJson(skippedField); \ TCompositeOptional proto; \ Json2Proto(json, proto); \ TCompositeOptional modelProto; \ FillCompositeProto(&modelProto, skippedField); \ UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); \ } #include #undef DEFINE_FIELD } // TestCompositeOptional Y_UNIT_TEST(TestCompositeOptionalStringBuf){ {NJson::TJsonValue json = CreateCompositeJson(); json["Part"]["Double"] = 42.5; TCompositeOptional proto; Json2Proto(JsonValueToString(json), proto); TCompositeOptional modelProto; FillCompositeProto(&modelProto); modelProto.MutablePart()->SetDouble(42.5); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // Try to skip each field #define DEFINE_FIELD(name, value) \ { \ THashSet skippedField; \ skippedField.insert(#name); \ NJson::TJsonValue json = CreateCompositeJson(skippedField); \ if (json["Part"].Has("Double")) { \ json["Part"]["Double"] = 42.5; \ } \ TCompositeOptional proto; \ Json2Proto(JsonValueToString(json), proto); \ TCompositeOptional modelProto; \ FillCompositeProto(&modelProto, skippedField); \ if (modelProto.GetPart().HasDouble()) { \ modelProto.MutablePart()->SetDouble(42.5); \ } \ UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); \ } #include #undef DEFINE_FIELD } // TestCompositeOptionalStringBuf Y_UNIT_TEST(TestCompositeRequired) { { const NJson::TJsonValue& json = CreateCompositeJson(); TCompositeRequired proto; Json2Proto(json, proto); TCompositeRequired modelProto; FillCompositeProto(&modelProto); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } { NJson::TJsonValue json; TCompositeRequired proto; UNIT_ASSERT_EXCEPTION(Json2Proto(json, proto), yexception); } } // TestCompositeRequired Y_UNIT_TEST(TestCompositeRepeated) { { NJson::TJsonValue json; NJson::TJsonValue array; array.AppendValue(CreateFlatJson()); json.InsertValue("Part", array); TCompositeRepeated proto; Json2Proto(json, proto); TFlatOptional partModelProto; FillFlatProto(&partModelProto); TCompositeRepeated modelProto; modelProto.AddPart()->CopyFrom(partModelProto); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } { // Array of messages with each field skipped TCompositeRepeated modelProto; NJson::TJsonValue array; #define DEFINE_REPEATED_FIELD(name, ...) \ { \ THashSet skippedField; \ skippedField.insert(#name); \ TFlatOptional partModelProto; \ FillFlatProto(&partModelProto, skippedField); \ modelProto.AddPart()->CopyFrom(partModelProto); \ array.AppendValue(CreateFlatJson(skippedField)); \ } #include #undef DEFINE_REPEATED_FIELD NJson::TJsonValue json; json.InsertValue("Part", array); TCompositeRepeated proto; Json2Proto(json, proto); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } } // TestCompositeRepeated Y_UNIT_TEST(TestInvalidEnum) { { NJson::TJsonValue json; json.InsertValue("Enum", "E_100"); TFlatOptional proto; UNIT_ASSERT_EXCEPTION(Json2Proto(json, proto), yexception); } { NJson::TJsonValue json; json.InsertValue("Enum", 100); TFlatOptional proto; UNIT_ASSERT_EXCEPTION(Json2Proto(json, proto), yexception); } } Y_UNIT_TEST(TestFieldNameMode) { // Original case 1 { TString modelStr(R"_({"String":"value"})_"); TFlatOptional proto; TJson2ProtoConfig config; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetString() == "value"); } // Original case 2 { TString modelStr(R"_({"String":"value"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameOriginalCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetString() == "value"); } // Lowercase { TString modelStr(R"_({"string":"value"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameLowerCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetString() == "value"); } // Uppercase { TString modelStr(R"_({"STRING":"value"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameUpperCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetString() == "value"); } // Camelcase { TString modelStr(R"_({"string":"value"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameCamelCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetString() == "value"); } { TString modelStr(R"_({"oneString":"value"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameCamelCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetOneString() == "value"); } { TString modelStr(R"_({"oneTwoString":"value"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameCamelCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetOneTwoString() == "value"); } // snake_case { TString modelStr(R"_({"string":"value"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameSnakeCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetString() == "value"); } { TString modelStr(R"_({"one_string":"value"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameSnakeCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetOneString() == "value"); } { TString modelStr(R"_({"one_two_string":"value"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameSnakeCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetOneTwoString() == "value"); } // Original case, repeated { TString modelStr(R"_({"I32":[1,2]})_"); TFlatRepeated proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameOriginalCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.I32Size() == 2); UNIT_ASSERT(proto.GetI32(0) == 1); UNIT_ASSERT(proto.GetI32(1) == 2); } // Lower case, repeated { TString modelStr(R"_({"i32":[1,2]})_"); TFlatRepeated proto; TJson2ProtoConfig config; config.FieldNameMode = TJson2ProtoConfig::FieldNameLowerCase; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.I32Size() == 2); UNIT_ASSERT(proto.GetI32(0) == 1); UNIT_ASSERT(proto.GetI32(1) == 2); } // UseJsonName { // FIXME(CONTRIB-139): since protobuf 3.1, Def_upper json name is // "DefUpper", but until kernel/ugc/schema and yweb/yasap/pdb are // updated, library/cpp/protobuf/json preserves compatibility with // protobuf 3.0 by lowercasing default names, making it "defUpper". TString modelStr(R"_({"My-Upper":1,"my-lower":2,"defUpper":3,"defLower":4})_"); TWithJsonName proto; TJson2ProtoConfig config; config.SetUseJsonName(true); UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT_EQUAL(proto.Getmy_upper(), 1); UNIT_ASSERT_EQUAL(proto.GetMy_lower(), 2); UNIT_ASSERT_EQUAL(proto.GetDef_upper(), 3); UNIT_ASSERT_EQUAL(proto.Getdef_lower(), 4); } // UseJsonName with UseJsonEnumValue { TString modelStr(R"_({"json_enum" : "enum_1"})_"); TCustomJsonEnumValue proto; TJson2ProtoConfig config; config.SetUseJsonName(true); config.SetUseJsonEnumValue(true); UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT_EQUAL(proto.GetJsonEnum(), EJsonEnum::J_1); } // FieldNameMode with UseJsonName { TJson2ProtoConfig config; config.SetFieldNameMode(TJson2ProtoConfig::FieldNameLowerCase); UNIT_ASSERT_EXCEPTION_CONTAINS( config.SetUseJsonName(true), yexception, "mutually exclusive"); } { TJson2ProtoConfig config; config.SetUseJsonName(true); UNIT_ASSERT_EXCEPTION_CONTAINS( config.SetFieldNameMode(TJson2ProtoConfig::FieldNameLowerCase), yexception, "mutually exclusive"); } } // TestFieldNameMode class TStringTransform: public IStringTransform { public: int GetType() const override { return 0; } void Transform(TString& str) const override { str = "transformed_any"; } }; class TBytesTransform: public IStringTransform { public: int GetType() const override { return 0; } void Transform(TString&) const override { } void TransformBytes(TString& str) const override { str = "transformed_bytes"; } }; Y_UNIT_TEST(TestInvalidJson) { NJson::TJsonValue val{"bad value"}; TFlatOptional proto; UNIT_ASSERT_EXCEPTION(Json2Proto(val, proto), yexception); } Y_UNIT_TEST(TestInvalidRepeatedFieldWithMapAsObject) { TCompositeRepeated proto; TJson2ProtoConfig config; config.MapAsObject = true; UNIT_ASSERT_EXCEPTION(Json2Proto(TStringBuf(R"({"Part":{"Boo":{}}})"), proto, config), yexception); } Y_UNIT_TEST(TestStringTransforms) { // Check that strings and bytes are transformed { TString modelStr(R"_({"String":"value_str", "Bytes": "value_bytes"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.AddStringTransform(new TStringTransform); UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetString() == "transformed_any"); UNIT_ASSERT(proto.GetBytes() == "transformed_any"); } // Check that bytes are transformed, strings are left intact { TString modelStr(R"_({"String":"value_str", "Bytes": "value_bytes"})_"); TFlatOptional proto; TJson2ProtoConfig config; config.AddStringTransform(new TBytesTransform); UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetString() == "value_str"); UNIT_ASSERT(proto.GetBytes() == "transformed_bytes"); } // Check that repeated bytes are transformed, repeated strings are left intact { TString modelStr(R"_({"String":["value_str", "str2"], "Bytes": ["value_bytes", "bytes2"]})_"); TFlatRepeated proto; TJson2ProtoConfig config; config.AddStringTransform(new TBytesTransform); UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.StringSize() == 2); UNIT_ASSERT(proto.GetString(0) == "value_str"); UNIT_ASSERT(proto.GetString(1) == "str2"); UNIT_ASSERT(proto.BytesSize() == 2); UNIT_ASSERT(proto.GetBytes(0) == "transformed_bytes"); UNIT_ASSERT(proto.GetBytes(1) == "transformed_bytes"); } // Check that bytes are transformed, strings are left intact in composed messages { TString modelStr(R"_({"Part": {"String":"value_str", "Bytes": "value_bytes"}})_"); TCompositeOptional proto; TJson2ProtoConfig config; config.AddStringTransform(new TBytesTransform); UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT(proto.GetPart().GetString() == "value_str"); UNIT_ASSERT(proto.GetPart().GetBytes() == "transformed_bytes"); } } // TestStringTransforms Y_UNIT_TEST(TestCastFromString) { // single fields { NJson::TJsonValue json; #define DEFINE_FIELD(name, value) \ json.InsertValue(#name, ConvertToString(value)); #include #undef DEFINE_FIELD TFlatOptional proto; UNIT_ASSERT_EXCEPTION_CONTAINS(Json2Proto(json, proto), yexception, "Invalid type"); TJson2ProtoConfig config; config.SetCastFromString(true); Json2Proto(json, proto, config); TFlatOptional modelProto; FillFlatProto(&modelProto); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // repeated fields { NJson::TJsonValue json; #define DEFINE_REPEATED_FIELD(name, type, ...) \ { \ type values[] = {__VA_ARGS__}; \ NJson::TJsonValue array(NJson::JSON_ARRAY); \ for (size_t i = 0, end = Y_ARRAY_SIZE(values); i < end; ++i) { \ array.AppendValue(ConvertToString(values[i])); \ } \ json.InsertValue(#name, array); \ } #include #undef DEFINE_REPEATED_FIELD TFlatRepeated proto; UNIT_ASSERT_EXCEPTION_CONTAINS(Json2Proto(json, proto), yexception, "Invalid type"); TJson2ProtoConfig config; config.SetCastFromString(true); Json2Proto(json, proto, config); TFlatRepeated modelProto; FillRepeatedProto(&modelProto); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } } // TestCastFromString Y_UNIT_TEST(TestMap) { TMapType modelProto; auto& items = *modelProto.MutableItems(); items["key1"] = "value1"; items["key2"] = "value2"; items["key3"] = "value3"; TString modelStr(R"_({"Items":[{"key":"key3","value":"value3"},{"key":"key2","value":"value2"},{"key":"key1","value":"value1"}]})_"); TJson2ProtoConfig config; TMapType proto; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // TestMap Y_UNIT_TEST(TestCastRobust) { NJson::TJsonValue json; json["I32"] = "5"; json["Bool"] = 1; json["String"] = 6; json["Double"] = 8; TFlatOptional proto; UNIT_ASSERT_EXCEPTION_CONTAINS(Json2Proto(json, proto), yexception, "Invalid type"); TJson2ProtoConfig config; config.SetCastRobust(true); Json2Proto(json, proto, config); TFlatOptional expected; expected.SetI32(5); expected.SetBool(true); expected.SetString("6"); expected.SetDouble(8); UNIT_ASSERT_PROTOS_EQUAL(proto, expected); } Y_UNIT_TEST(TestVectorizeScalars) { NJson::TJsonValue json; #define DEFINE_FIELD(name, value) \ json.InsertValue(#name, value); #include #undef DEFINE_FIELD TFlatRepeated proto; TJson2ProtoConfig config; config.SetVectorizeScalars(true); Json2Proto(json, proto, config); #define DEFINE_FIELD(name, value) \ UNIT_ASSERT_VALUES_EQUAL(proto.Get ## name(0), value); #include #undef DEFINE_FIELD } Y_UNIT_TEST(TestValueVectorizer) { { // No ValueVectorizer NJson::TJsonValue json; json["RepeatedString"] = "123"; TJson2ProtoConfig config; TSingleRepeatedString expected; UNIT_ASSERT_EXCEPTION(Json2Proto(json, expected, config), yexception); } { // ValueVectorizer replace original value by array NJson::TJsonValue json; json["RepeatedString"] = "123"; TJson2ProtoConfig config; TSingleRepeatedString expected; expected.AddRepeatedString("4"); expected.AddRepeatedString("5"); expected.AddRepeatedString("6"); config.ValueVectorizer = [](const NJson::TJsonValue& val) -> NJson::TJsonValue::TArray { Y_UNUSED(val); return {NJson::TJsonValue("4"), NJson::TJsonValue("5"), NJson::TJsonValue("6")}; }; TSingleRepeatedString actual; Json2Proto(json, actual, config); UNIT_ASSERT_PROTOS_EQUAL(expected, actual); } { // ValueVectorizer replace original value by array and cast NJson::TJsonValue json; json["RepeatedInt"] = 123; TJson2ProtoConfig config; TSingleRepeatedInt expected; expected.AddRepeatedInt(4); expected.AddRepeatedInt(5); expected.AddRepeatedInt(6); config.ValueVectorizer = [](const NJson::TJsonValue& val) -> NJson::TJsonValue::TArray { Y_UNUSED(val); return {NJson::TJsonValue("4"), NJson::TJsonValue(5), NJson::TJsonValue("6")}; }; config.CastFromString = true; TSingleRepeatedInt actual; Json2Proto(json, actual, config); UNIT_ASSERT_PROTOS_EQUAL(expected, actual); } } Y_UNIT_TEST(TestMapAsObject) { TMapType modelProto; auto& items = *modelProto.MutableItems(); items["key1"] = "value1"; items["key2"] = "value2"; items["key3"] = "value3"; TString modelStr(R"_({"Items":{"key1":"value1","key2":"value2","key3":"value3"}})_"); TJson2ProtoConfig config; config.MapAsObject = true; TMapType proto; UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(modelStr, config)); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // TestMapAsObject Y_UNIT_TEST(TestComplexMapAsObject_I32) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableI32(); items[1] = 1; items[-2] = -2; items[3] = 3; }, R"_({"I32":{"1":1,"-2":-2,"3":3}})_"); } // TestComplexMapAsObject_I32 Y_UNIT_TEST(TestComplexMapAsObject_I64) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableI64(); items[2147483649L] = 2147483649L; items[-2147483650L] = -2147483650L; items[2147483651L] = 2147483651L; }, R"_({"I64":{"2147483649":2147483649,"-2147483650":-2147483650,"2147483651":2147483651}})_"); } // TestComplexMapAsObject_I64 Y_UNIT_TEST(TestComplexMapAsObject_UI32) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableUI32(); items[1073741825U] = 1073741825U; items[1073741826U] = 1073741826U; items[1073741827U] = 1073741827U; }, R"_({"UI32":{"1073741825":1073741825,"1073741826":1073741826,"1073741827":1073741827}})_"); } // TestComplexMapAsObject_UI32 Y_UNIT_TEST(TestComplexMapAsObject_UI64) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableUI64(); items[9223372036854775809UL] = 9223372036854775809UL; items[9223372036854775810UL] = 9223372036854775810UL; items[9223372036854775811UL] = 9223372036854775811UL; }, R"_({"UI64":{"9223372036854775809":9223372036854775809,"9223372036854775810":9223372036854775810,"9223372036854775811":9223372036854775811}})_"); } // TestComplexMapAsObject_UI64 Y_UNIT_TEST(TestComplexMapAsObject_SI32) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableSI32(); items[1] = 1; items[-2] = -2; items[3] = 3; }, R"_({"SI32":{"1":1,"-2":-2,"3":3}})_"); } // TestComplexMapAsObject_SI32 Y_UNIT_TEST(TestComplexMapAsObject_SI64) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableSI64(); items[2147483649L] = 2147483649L; items[-2147483650L] = -2147483650L; items[2147483651L] = 2147483651L; }, R"_({"SI64":{"2147483649":2147483649,"-2147483650":-2147483650,"2147483651":2147483651}})_"); } // TestComplexMapAsObject_SI64 Y_UNIT_TEST(TestComplexMapAsObject_FI32) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableFI32(); items[1073741825U] = 1073741825U; items[1073741826U] = 1073741826U; items[1073741827U] = 1073741827U; }, R"_({"FI32":{"1073741825":1073741825,"1073741826":1073741826,"1073741827":1073741827}})_"); } // TestComplexMapAsObject_FI32 Y_UNIT_TEST(TestComplexMapAsObject_FI64) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableFI64(); items[9223372036854775809UL] = 9223372036854775809UL; items[9223372036854775810UL] = 9223372036854775810UL; items[9223372036854775811UL] = 9223372036854775811UL; }, R"_({"FI64":{"9223372036854775809":9223372036854775809,"9223372036854775810":9223372036854775810,"9223372036854775811":9223372036854775811}})_"); } // TestComplexMapAsObject_FI64 Y_UNIT_TEST(TestComplexMapAsObject_SFI32) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableSFI32(); items[1] = 1; items[-2] = -2; items[3] = 3; }, R"_({"SFI32":{"1":1,"-2":-2,"3":3}})_"); } // TestComplexMapAsObject_SFI32 Y_UNIT_TEST(TestComplexMapAsObject_SFI64) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableSFI64(); items[2147483649L] = 2147483649L; items[-2147483650L] = -2147483650L; items[2147483651L] = 2147483651L; }, R"_({"SFI64":{"2147483649":2147483649,"-2147483650":-2147483650,"2147483651":2147483651}})_"); } // TestComplexMapAsObject_SFI64 Y_UNIT_TEST(TestComplexMapAsObject_Bool) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableBool(); items[true] = true; items[false] = false; }, R"_({"Bool":{"true":true,"false":false}})_"); } // TestComplexMapAsObject_Bool Y_UNIT_TEST(TestComplexMapAsObject_String) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableString(); items["key1"] = "value1"; items["key2"] = "value2"; items["key3"] = "value3"; items[""] = "value4"; }, R"_({"String":{"key1":"value1","key2":"value2","key3":"value3","":"value4"}})_"); } // TestComplexMapAsObject_String Y_UNIT_TEST(TestComplexMapAsObject_Enum) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableEnum(); items["key1"] = EEnum::E_1; items["key2"] = EEnum::E_2; items["key3"] = EEnum::E_3; }, R"_({"Enum":{"key1":1,"key2":2,"key3":3}})_"); } // TestComplexMapAsObject_Enum Y_UNIT_TEST(TestComplexMapAsObject_EnumString) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableEnum(); items["key1"] = EEnum::E_1; items["key2"] = EEnum::E_2; items["key3"] = EEnum::E_3; }, R"_({"Enum":{"key1":"E_1","key2":"E_2","key3":"E_3"}})_"); } // TestComplexMapAsObject_EnumString Y_UNIT_TEST(TestComplexMapAsObject_EnumStringCaseInsensetive) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableEnum(); items["key1"] = EEnum::E_1; items["key2"] = EEnum::E_2; items["key3"] = EEnum::E_3; }, R"_({"Enum":{"key1":"e_1","key2":"E_2","key3":"e_3"}})_", TJson2ProtoConfig() .SetMapAsObject(true) .SetEnumValueMode(NProtobufJson::TJson2ProtoConfig::EnumCaseInsensetive) ); } // TestComplexMapAsObject_EnumStringCaseInsensetive Y_UNIT_TEST(TestComplexMapAsObject_EnumStringSnakeCaseInsensitive) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableEnum(); items["key1"] = EEnum::E_1; items["key2"] = EEnum::E_2; items["key3"] = EEnum::E_3; }, R"_({"Enum":{"key1":"e1","key2":"_E_2_","key3":"e_3"}})_", TJson2ProtoConfig() .SetMapAsObject(true) .SetEnumValueMode(NProtobufJson::TJson2ProtoConfig::EnumSnakeCaseInsensitive) ); } // TestComplexMapAsObject_EnumStringCaseInsensetive Y_UNIT_TEST(TestComplexMapAsObject_Float) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableFloat(); items["key1"] = 0.1f; items["key2"] = 0.2f; items["key3"] = 0.3f; }, R"_({"Float":{"key1":0.1,"key2":0.2,"key3":0.3}})_"); } // TestComplexMapAsObject_Float Y_UNIT_TEST(TestComplexMapAsObject_Double) { TestComplexMapAsObject( [](TComplexMapType& proto) { auto& items = *proto.MutableDouble(); items["key1"] = 0.1L; items["key2"] = 0.2L; items["key3"] = 0.3L; }, R"_({"Double":{"key1":0.1,"key2":0.2,"key3":0.3}})_"); } // TestComplexMapAsObject_Double Y_UNIT_TEST(TestComplexMapAsObject_Nested) { TestComplexMapAsObject( [](TComplexMapType& proto) { TComplexMapType inner; auto& innerItems = *inner.MutableString(); innerItems["key"] = "value"; auto& items = *proto.MutableNested(); items["key1"] = inner; items["key2"] = inner; items["key3"] = inner; }, R"_({"Nested":{"key1":{"String":{"key":"value"}},"key2":{"String":{"key":"value"}},"key3":{"String":{"key":"value"}}}})_"); } // TestComplexMapAsObject_Nested Y_UNIT_TEST(TestMapAsObjectConfigNotSet) { TString modelStr(R"_({"Items":{"key":"value"}})_"); TJson2ProtoConfig config; UNIT_ASSERT_EXCEPTION_CONTAINS( Json2Proto(modelStr, config), yexception, "Map as object representation is not allowed"); } // TestMapAsObjectNotSet Y_UNIT_TEST(TestMergeFlatOptional) { const NJson::TJsonValue& json = CreateFlatJson(); NJson::TJsonValue patch; patch["I32"] = 5; patch["Bool"] = false; patch["String"] = "abacaba"; patch["Double"] = 0.123; TFlatOptional proto; UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto)); UNIT_ASSERT_NO_EXCEPTION(MergeJson2Proto(patch, proto)); TFlatRequired modelProto; FillFlatProto(&modelProto); modelProto.SetI32(5); modelProto.SetBool(false); modelProto.SetString("abacaba"); modelProto.SetDouble(0.123); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // TestMergeFlatOptional Y_UNIT_TEST(TestMergeFlatRequired) { const NJson::TJsonValue& json = CreateFlatJson(); NJson::TJsonValue patch; patch["I32"] = 5; patch["Bool"] = false; patch["String"] = "abacaba"; patch["Double"] = 0.123; TFlatRequired proto; UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto)); UNIT_ASSERT_NO_EXCEPTION(MergeJson2Proto(patch, proto)); TFlatRequired modelProto; FillFlatProto(&modelProto); modelProto.SetI32(5); modelProto.SetBool(false); modelProto.SetString("abacaba"); modelProto.SetDouble(0.123); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // TestMergeFlatRequired Y_UNIT_TEST(TestMergeComposite) { const NJson::TJsonValue& json = CreateCompositeJson(); NJson::TJsonValue patch; patch["Part"]["I32"] = 5; patch["Part"]["Bool"] = false; patch["Part"]["String"] = "abacaba"; patch["Part"]["Double"] = 0.123; TCompositeOptional proto; UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto)); UNIT_ASSERT_NO_EXCEPTION(MergeJson2Proto(patch, proto)); TCompositeOptional modelProto; FillCompositeProto(&modelProto); modelProto.MutablePart()->SetI32(5); modelProto.MutablePart()->SetBool(false); modelProto.MutablePart()->SetString("abacaba"); modelProto.MutablePart()->SetDouble(0.123); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // TestMergeComposite Y_UNIT_TEST(TestMergeRepeatedReplace) { const NJson::TJsonValue& json = CreateRepeatedFlatJson(); NJson::TJsonValue patch; patch["I32"].AppendValue(5); patch["I32"].AppendValue(6); patch["String"].AppendValue("abacaba"); TFlatRepeated proto; TJson2ProtoConfig config; config.ReplaceRepeatedFields = true; UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto)); UNIT_ASSERT_NO_EXCEPTION(MergeJson2Proto(patch, proto, config)); TFlatRepeated modelProto; FillRepeatedProto(&modelProto); modelProto.ClearI32(); modelProto.AddI32(5); modelProto.AddI32(6); modelProto.ClearString(); modelProto.AddString("abacaba"); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // TestMergeRepeatedReplace Y_UNIT_TEST(TestMergeRepeatedAppend) { const NJson::TJsonValue& json = CreateRepeatedFlatJson(); NJson::TJsonValue patch; patch["I32"].AppendValue(5); patch["I32"].AppendValue(6); patch["String"].AppendValue("abacaba"); TFlatRepeated proto; UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto)); UNIT_ASSERT_NO_EXCEPTION(MergeJson2Proto(patch, proto)); TFlatRepeated modelProto; FillRepeatedProto(&modelProto); modelProto.AddI32(5); modelProto.AddI32(6); modelProto.AddString("abacaba"); UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); } // TestMergeRepeatedAppend Y_UNIT_TEST(TestEmptyStringForCastFromString) { NJson::TJsonValue json; json["I32"] = ""; json["Bool"] = ""; json["OneString"] = ""; TJson2ProtoConfig config; config.SetCastFromString(true); config.SetDoNotCastEmptyStrings(true); TFlatOptional proto; UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto, config)); UNIT_ASSERT(!proto.HasBool()); UNIT_ASSERT(!proto.HasI32()); UNIT_ASSERT(proto.HasOneString()); UNIT_ASSERT_EQUAL("", proto.GetOneString()); } // TestEmptyStringForCastFromString Y_UNIT_TEST(TestAllowComments) { constexpr TStringBuf json = R"( { "I32": 4, // comment1 /* comment2 {} qwer */ "I64": 3423 } )"; TJson2ProtoConfig config; TFlatOptional proto; UNIT_ASSERT_EXCEPTION_CONTAINS(Json2Proto(json, proto, config), yexception, "Error: Missing a name for object member"); config.SetAllowComments(true); UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto, config)); UNIT_ASSERT_VALUES_EQUAL(proto.GetI32(), 4); UNIT_ASSERT_VALUES_EQUAL(proto.GetI64(), 3423); proto = TFlatOptional(); UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto(json, config)); UNIT_ASSERT_VALUES_EQUAL(proto.GetI32(), 4); UNIT_ASSERT_VALUES_EQUAL(proto.GetI64(), 3423); } // TestAllowComments Y_UNIT_TEST(TestSimplifiedDuration) { NJson::TJsonValue json; TSingleDuration simpleDuration; json["Duration"] = "10.1s"; NProtobufJson::Json2Proto(json, simpleDuration, NProtobufJson::TJson2ProtoConfig().SetAllowString2TimeConversion(true)); UNIT_ASSERT_EQUAL(NProtoInterop::CastFromProto(simpleDuration.GetDuration()), TDuration::MilliSeconds(10100)); } // TestSimplifiedDuration Y_UNIT_TEST(TestUnwrappedDuration) { NJson::TJsonValue json; TSingleDuration duration; json["Duration"]["seconds"] = 2; NProtobufJson::Json2Proto(json, duration, NProtobufJson::TJson2ProtoConfig()); UNIT_ASSERT_EQUAL(NProtoInterop::CastFromProto(duration.GetDuration()), TDuration::MilliSeconds(2000)); } // TestUnwrappedDuration Y_UNIT_TEST(TestSimplifiedTimestamp) { NJson::TJsonValue json; TSingleTimestamp simpleTimestamp; json["Timestamp"] = "2014-08-26T15:52:15Z"; NProtobufJson::Json2Proto(json, simpleTimestamp, NProtobufJson::TJson2ProtoConfig().SetAllowString2TimeConversion(true)); UNIT_ASSERT_EQUAL(NProtoInterop::CastFromProto(simpleTimestamp.GetTimestamp()), TInstant::ParseIso8601("2014-08-26T15:52:15Z")); } // TestSimplifiedTimestamp } // TJson2ProtoTest