4#include <google/protobuf/any.pb.h>
5#include <gtest/gtest.h>
7#include <protobuf/json/impl/proto_message_visitor.hpp>
8#include <protobuf/json/impl/string_writer.hpp>
10#include <userver/formats/json/serialize.hpp>
11#include <userver/protobuf/json/convert.hpp>
12#include <userver/utest/assert_macros.hpp>
14#include "proto_json/messages.pb.h"
17USERVER_NAMESPACE_BEGIN
19namespace protobuf::json::tests {
23constexpr std::string_view kSecret =
"sensitive-payload";
24constexpr std::string_view kSecretBase64 =
"c2Vuc2l0aXZlLXBheWxvYWQ=";
25constexpr std::size_t kLimit = 1 << 20;
27void ExpectNoSecret(std::string_view text) {
28 EXPECT_EQ(text.find(kSecret), std::string_view::npos);
29 EXPECT_EQ(text.find(kSecretBase64), std::string_view::npos);
32::google::protobuf::Any MakeUnresolvedAny(
33 std::string_view type_url =
"type.googleapis.com/testing.unregistered.Message"
35 ::google::protobuf::Any any;
36 any.set_type_url(std::string{type_url});
37 any.set_value(std::string{kSecret});
43TEST(DebugStringAny, RawAnyOmitsValueWhenRedacted) {
44 protobuf::json::impl::StringWriter string_writer{kLimit};
45 protobuf::json::impl::ProtoMessageVisitor visitor{string_writer};
46 visitor.SetPreserveProtoFieldNames(
true);
47 visitor.SetExpandAny(
false);
48 visitor.SetRedactDebugString(
true);
49 visitor(MakeUnresolvedAny());
51 const auto json = string_writer.GetString();
52 EXPECT_EQ(json, R"({"type_url":"type.googleapis.com/testing.unregistered.Message"})");
56TEST(DebugStringAny, UnknownTypeOmitsValue) {
57 const auto any = MakeUnresolvedAny();
58 const auto json = MessageToDebugString(any, kLimit);
60 EXPECT_EQ(json, R"({"@type":"type.googleapis.com/testing.unregistered.Message","@error":"unresolved_any_type"})");
64TEST(DebugStringAny, KnownTypeIsExpanded) {
65 proto_json::messages::Int32Message payload;
66 payload.set_field1(1);
67 payload.set_field2(2);
68 payload.set_field3(3);
70 ::google::protobuf::Any any;
71 any.PackFrom(payload);
73 const auto json = MessageToDebugString(any, kLimit);
76 R"({"@type":"type.googleapis.com/proto_json.messages.Int32Message","field1":1,"field2":2,"field3":3})"
80TEST(DebugStringAny, EmptyAny) {
81 const ::google::protobuf::Any any;
82 EXPECT_EQ(MessageToDebugString(any, kLimit),
"{}");
85TEST(DebugStringAny, NestedUnresolvedAny) {
86 ::google::protobuf::Any outer;
87 outer.PackFrom(MakeUnresolvedAny());
89 const auto json = MessageToDebugString(outer, kLimit);
92 R"({"@type":"type.googleapis.com/google.protobuf.Any","value":{"@type":"type.googleapis.com/testing.unregistered.Message","@error":"unresolved_any_type"}})"
97TEST(DebugStringAny, NeighborsRepeatedAndMap) {
98 proto_json::messages::AnyContainer message;
99 message.set_keep(
"keep");
100 *message.mutable_any() = MakeUnresolvedAny();
102 *message.add_items() = MakeUnresolvedAny(
"example.com/Unknown");
104 proto_json::messages::Int32Message known;
108 message.add_items()->PackFrom(known);
110 (*message.mutable_entries())[
"unknown"] = MakeUnresolvedAny(
"example.com/Unknown");
112 const auto json = MessageToDebugString(message, kLimit);
115 R"({"keep":"keep","any":{"@type":"type.googleapis.com/testing.unregistered.Message","@error":"unresolved_any_type"},"items":[{"@type":"example.com/Unknown","@error":"unresolved_any_type"},{"@type":"type.googleapis.com/proto_json.messages.Int32Message","field1":1,"field2":2,"field3":3}],"entries":{"unknown":{"@type":"example.com/Unknown","@error":"unresolved_any_type"}}})"
117 ExpectNoSecret(json);
120TEST(DebugStringAny, EscapedTypeUrl) {
121 const std::string type_url =
"example.com/\"quoted\"\nMessage";
122 const auto json = formats
::json::FromString(MessageToDebugString(MakeUnresolvedAny(type_url), kLimit)
);
124 EXPECT_EQ(json
["@type"].As<std::string>(), type_url);
125 EXPECT_EQ(json
["@error"].As<std::string>(),
"unresolved_any_type");
129TEST(DebugStringAny, MissingTypeUrlDoesNotLeakValue) {
130 proto_json::messages::AnyMessage message;
131 message.mutable_field1()->set_value(std::string{kSecret});
134 const auto json = MessageToDebugString(message, kLimit);
135 ADD_FAILURE() <<
"Should throw 'PrintError', got: " << json;
138 EXPECT_EQ(error.GetErrorInfo().GetPath(),
"field1");
139 ExpectNoSecret(error.what());
143TEST(DebugStringAny, CorruptKnownPayloadDoesNotLeakValue) {
144 proto_json::messages::AnyMessage message;
145 message.mutable_field1()->set_type_url(
"type.googleapis.com/proto_json.messages.Int32Message");
146 message.mutable_field1()->set_value(std::string{
"\x80"} + std::string{kSecret});
148 const auto json = MessageToDebugString(message, kLimit);
151 R"({"field1":{"@type":"type.googleapis.com/proto_json.messages.Int32Message","@error":"invalid_payload"}})"
153 ExpectNoSecret(json);
156TEST(DebugStringAny, StrictJsonStillFailsOnCorruptPayload) {
157 proto_json::messages::AnyMessage message;
158 message.mutable_field1()->set_type_url(
"type.googleapis.com/proto_json.messages.Int32Message");
159 message.mutable_field1()->set_value(std::string{
"\x80"} + std::string{kSecret});
164TEST(DebugStringAny, TruncationDoesNotEmitValue) {
165 const auto json = MessageToDebugString(MakeUnresolvedAny(), 16);
166 ExpectNoSecret(json);
169TEST(DebugStringAny, StrictJsonStillFails) {
170 proto_json::messages::AnyMessage message;
171 *message.mutable_field1() = MakeUnresolvedAny();