// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#include "google/protobuf/json/json.h"

#include <algorithm>
#include <cstdint>
#include <list>
#include <memory>
#include <string>
#include <vector>

#include "google/protobuf/duration.pb.h"
#include "google/protobuf/field_mask.pb.h"
#include "google/protobuf/struct.pb.h"
#include "google/protobuf/timestamp.pb.h"
#include "google/protobuf/wrappers.pb.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/descriptor_database.h"
#include "google/protobuf/dynamic_message.h"
#include "google/protobuf/io/test_zero_copy_stream.h"
#include "google/protobuf/io/zero_copy_stream.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "google/protobuf/util/json_format.pb.h"
#include "google/protobuf/util/json_format_proto3.pb.h"
#include "google/protobuf/unittest.pb.h"
#include "google/protobuf/util/type_resolver.h"
#include "google/protobuf/util/type_resolver_util.h"
#include "google/protobuf/stubs/status_macros.h"

// Must be included last.
#include "google/protobuf/port_def.inc"

namespace google {
namespace protobuf {
namespace json {
namespace {
using ::google::protobuf::util::TypeResolver;
using ::proto3::MapIn;
using ::proto3::TestAny;
using ::proto3::TestEnumValue;
using ::proto3::TestMap;
using ::proto3::TestMessage;
using ::proto3::TestOneof;
using ::proto3::TestWrapper;
using ::testing::ContainsRegex;
using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::Not;
using ::testing::SizeIs;

// TODO: Use the gtest versions once that's available in OSS.
MATCHER_P(IsOkAndHolds, inner,
          absl::StrCat("is OK and holds ", testing::PrintToString(inner))) {
  if (!arg.ok()) {
    *result_listener << arg.status();
    return false;
  }
  return testing::ExplainMatchResult(inner, *arg, result_listener);
}

absl::Status GetStatus(const absl::Status& s) { return s; }
template <typename T>
absl::Status GetStatus(const absl::StatusOr<T>& s) {
  return s.status();
}

MATCHER_P(StatusIs, status,
          absl::StrCat(".status() is ", testing::PrintToString(status))) {
  return GetStatus(arg).code() == status;
}

#define EXPECT_OK(x) EXPECT_THAT(x, StatusIs(absl::StatusCode::kOk))
#define ASSERT_OK(x) ASSERT_THAT(x, StatusIs(absl::StatusCode::kOk))

enum class Codec {
  kReflective,
  kResolver,
};

class JsonTest : public testing::TestWithParam<Codec> {
 protected:
  absl::StatusOr<std::string> ToJson(const Message& proto,
                                     PrintOptions options = {}) {
    if (GetParam() == Codec::kReflective) {
      std::string result;
      RETURN_IF_ERROR(MessageToJsonString(proto, &result, options));
      return result;
    }
    std::string proto_data = proto.SerializeAsString();
    io::ArrayInputStream in(proto_data.data(), proto_data.size());

    std::string result;
    io::StringOutputStream out(&result);

    RETURN_IF_ERROR(BinaryToJsonStream(
        resolver_.get(),
        absl::StrCat("type.googleapis.com/", proto.GetTypeName()), &in, &out,
        options));
    return result;
  }

  // The out parameter comes first since `json` tends to be a very long string,
  // and clang-format does a poor job if it is not the last parameter.
  absl::Status ToProto(Message& proto, absl::string_view json,
                       ParseOptions options = {}) {
    if (GetParam() == Codec::kReflective) {
      return JsonStringToMessage(json, &proto, options);
    }
    io::ArrayInputStream in(json.data(), json.size());

    std::string result;
    io::StringOutputStream out(&result);

    RETURN_IF_ERROR(JsonToBinaryStream(
        resolver_.get(),
        absl::StrCat("type.googleapis.com/", proto.GetTypeName()), &in, &out,
        options));

    if (!proto.ParseFromString(result)) {
      return absl::InternalError("wire format parse failed");
    }
    return absl::OkStatus();
  }

  template <typename Proto>
  absl::StatusOr<Proto> ToProto(absl::string_view json,
                                ParseOptions options = {}) {
    Proto proto;
    RETURN_IF_ERROR(ToProto(proto, json, options));
    return proto;
  }

  std::unique_ptr<TypeResolver> resolver_{
      google::protobuf::util::NewTypeResolverForDescriptorPool(
          "type.googleapis.com", DescriptorPool::generated_pool())};
};

INSTANTIATE_TEST_SUITE_P(JsonTestSuite, JsonTest,
                         testing::Values(Codec::kReflective, Codec::kResolver));

TEST_P(JsonTest, TestWhitespaces) {
  TestMessage m;
  m.mutable_message_value();
  m.set_string_value("foo");
  m.add_repeated_bool_value(true);
  m.add_repeated_bool_value(false);

  EXPECT_THAT(
      ToJson(m),
      IsOkAndHolds(
          R"({"stringValue":"foo","messageValue":{},"repeatedBoolValue":[true,false]})"));

  PrintOptions options;
  options.add_whitespace = true;
  // Note: whitespace here is significant.
  EXPECT_THAT(ToJson(m, options), IsOkAndHolds(R"({
 "stringValue": "foo",
 "messageValue": {},
 "repeatedBoolValue": [
  true,
  false
 ]
}
)"));
}

TEST_P(JsonTest, TestDefaultValues) {
  TestMessage m;
  EXPECT_THAT(ToJson(m), IsOkAndHolds("{}"));

  PrintOptions options;
  options.always_print_primitive_fields = true;
  EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\"boolValue\":false,"
                                               "\"int32Value\":0,"
                                               "\"int64Value\":\"0\","
                                               "\"uint32Value\":0,"
                                               "\"uint64Value\":\"0\","
                                               "\"floatValue\":0,"
                                               "\"doubleValue\":0,"
                                               "\"stringValue\":\"\","
                                               "\"bytesValue\":\"\","
                                               "\"enumValue\":\"FOO\","
                                               "\"repeatedBoolValue\":[],"
                                               "\"repeatedInt32Value\":[],"
                                               "\"repeatedInt64Value\":[],"
                                               "\"repeatedUint32Value\":[],"
                                               "\"repeatedUint64Value\":[],"
                                               "\"repeatedFloatValue\":[],"
                                               "\"repeatedDoubleValue\":[],"
                                               "\"repeatedStringValue\":[],"
                                               "\"repeatedBytesValue\":[],"
                                               "\"repeatedEnumValue\":[],"
                                               "\"repeatedMessageValue\":[]"
                                               "}"));

  m.set_string_value("i am a test string value");
  m.set_bytes_value("i am a test bytes value");
  EXPECT_THAT(
      ToJson(m, options),
      IsOkAndHolds("{\"boolValue\":false,"
                   "\"int32Value\":0,"
                   "\"int64Value\":\"0\","
                   "\"uint32Value\":0,"
                   "\"uint64Value\":\"0\","
                   "\"floatValue\":0,"
                   "\"doubleValue\":0,"
                   "\"stringValue\":\"i am a test string value\","
                   "\"bytesValue\":\"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=\","
                   "\"enumValue\":\"FOO\","
                   "\"repeatedBoolValue\":[],"
                   "\"repeatedInt32Value\":[],"
                   "\"repeatedInt64Value\":[],"
                   "\"repeatedUint32Value\":[],"
                   "\"repeatedUint64Value\":[],"
                   "\"repeatedFloatValue\":[],"
                   "\"repeatedDoubleValue\":[],"
                   "\"repeatedStringValue\":[],"
                   "\"repeatedBytesValue\":[],"
                   "\"repeatedEnumValue\":[],"
                   "\"repeatedMessageValue\":[]"
                   "}"));

  EXPECT_THAT(
      ToJson(protobuf_unittest::TestAllTypes(), options),
      IsOkAndHolds(
          R"({"optionalInt32":0,"optionalInt64":"0","optionalUint32":0,)"
          R"("optionalUint64":"0","optionalSint32":0,"optionalSint64":"0","optionalFixed32":0,)"
          R"("optionalFixed64":"0","optionalSfixed32":0,"optionalSfixed64":"0",)"
          R"("optionalFloat":0,"optionalDouble":0,"optionalBool":false,"optionalString":"",)"
          R"("optionalBytes":"","optionalgroup":null,"optionalNestedEnum":"FOO","optionalForeignEnum":"FOREIGN_FOO",)"
          R"("optionalImportEnum":"IMPORT_FOO","optionalStringPiece":"","optionalCord":"",)"
          R"("repeatedInt32":[],"repeatedInt64":[],"repeatedUint32":[],"repeatedUint64":[],)"
          R"("repeatedSint32":[],"repeatedSint64":[],"repeatedFixed32":[],"repeatedFixed64":[],)"
          R"("repeatedSfixed32":[],"repeatedSfixed64":[],"repeatedFloat":[],"repeatedDouble":[],)"
          R"("repeatedBool":[],"repeatedString":[],"repeatedBytes":[],"repeatedgroup":[],)"
          R"("repeatedNestedMessage":[],"repeatedForeignMessage":[],"repeatedImportMessage":[],)"
          R"("repeatedNestedEnum":[],"repeatedForeignEnum":[],"repeatedImportEnum":[],)"
          R"("repeatedStringPiece":[],"repeatedCord":[],"repeatedLazyMessage":[],"defaultInt32":41,)"
          R"("defaultInt64":"42","defaultUint32":43,"defaultUint64":"44","defaultSint32":-45,)"
          R"("defaultSint64":"46","defaultFixed32":47,"defaultFixed64":"48","defaultSfixed32":49,)"
          R"("defaultSfixed64":"-50","defaultFloat":51.5,"defaultDouble":52000,"defaultBool":true,)"
          R"("defaultString":"hello","defaultBytes":"d29ybGQ=","defaultNestedEnum":"BAR",)"
          R"("defaultForeignEnum":"FOREIGN_BAR","defaultImportEnum":"IMPORT_BAR",)"
          R"("defaultStringPiece":"abc","defaultCord":"123"})"));

  EXPECT_THAT(
      ToJson(protobuf_unittest::TestExtremeDefaultValues(), options),
      IsOkAndHolds(
          R"({"escapedBytes":"XDAwMFwwMDFcMDA3XDAxMFwwMTRcblxyXHRcMDEzXFxcJ1wiXDM3Ng==")"
          R"(,"largeUint32":4294967295,"largeUint64":"18446744073709551615",)"
          R"("smallInt32":-2147483647,"smallInt64":"-9223372036854775807",)"
          R"("utf8String":"ሴ","zeroFloat":0,"oneFloat":1,"smallFloat":1.5,)"
          R"("negativeOneFloat":-1,"negativeFloat":-1.5,"largeFloat":2e+08,)"
          R"("smallNegativeFloat":-8e-28,"infDouble":0,"negInfDouble":0,)"
          R"("nanDouble":0,"infFloat":0,"negInfFloat":0,"nanFloat":0,)"
          R"("cppTrigraph":"? ? ?? ?? ??? ??/ ??-","reallySmallInt32":-2147483648)"
          R"(,"reallySmallInt64":"-9223372036854775808","stringWithZero":"hel\u0000lo")"
          R"(,"bytesWithZero":"d29yXDAwMGxk","stringPieceWithZero":"ab\u0000c")"
          R"(,"cordWithZero":"12\u00003","replacementString":"${unknown}"})"));
}

TEST_P(JsonTest, TestPreserveProtoFieldNames) {
  TestMessage m;
  m.mutable_message_value();

  PrintOptions options;
  options.preserve_proto_field_names = true;
  EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\"message_value\":{}}"));

}

TEST_P(JsonTest, Camels) {
  protobuf_unittest::TestCamelCaseFieldNames m;
  m.set_stringfield("sTRINGfIELD");

  EXPECT_THAT(ToJson(m), IsOkAndHolds(R"({"StringField":"sTRINGfIELD"})"));
}

TEST_P(JsonTest, EvilString) {
  auto m = ToProto<TestMessage>(R"json(
    {"string_value": ")json"
                                "\n\r\b\f\1\2\3"
                                "\"}");
  ASSERT_OK(m);
  EXPECT_EQ(m->string_value(), "\n\r\b\f\1\2\3");
}

TEST_P(JsonTest, Unquoted64) {
  TestMessage m;
  m.add_repeated_int64_value(0);
  m.add_repeated_int64_value(42);
  m.add_repeated_int64_value(-((int64_t{1} << 60) + 1));
  m.add_repeated_int64_value(INT64_MAX);
  // This is a power of two and is therefore representable.
  m.add_repeated_int64_value(INT64_MIN);
  m.add_repeated_uint64_value(0);
  m.add_repeated_uint64_value(42);
  m.add_repeated_uint64_value((uint64_t{1} << 60) + 1);
  // This will be UB without the min/max check in RoundTripsThroughDouble().
  m.add_repeated_uint64_value(UINT64_MAX);

  PrintOptions opts;
  opts.unquote_int64_if_possible = true;
  EXPECT_THAT(
      ToJson(m, opts),
      R"({"repeatedInt64Value":[0,42,"-1152921504606846977","9223372036854775807",-9223372036854775808],)"
      R"("repeatedUint64Value":[0,42,"1152921504606846977","18446744073709551615"]})");
}

TEST_P(JsonTest, TestAlwaysPrintEnumsAsInts) {
  TestMessage orig;
  orig.set_enum_value(proto3::BAR);
  orig.add_repeated_enum_value(proto3::FOO);
  orig.add_repeated_enum_value(proto3::BAR);

  PrintOptions print_options;
  print_options.always_print_enums_as_ints = true;

  auto printed = ToJson(orig, print_options);
  ASSERT_THAT(printed,
              IsOkAndHolds("{\"enumValue\":1,\"repeatedEnumValue\":[0,1]}"));

  auto parsed = ToProto<TestMessage>(*printed);
  ASSERT_OK(parsed);

  EXPECT_EQ(parsed->enum_value(), proto3::BAR);
  EXPECT_THAT(parsed->repeated_enum_value(),
              ElementsAre(proto3::FOO, proto3::BAR));
}

TEST_P(JsonTest, TestPrintEnumsAsIntsWithDefaultValue) {
  TestEnumValue orig;
  // orig.set_enum_value1(proto3::FOO)
  orig.set_enum_value2(proto3::FOO);
  orig.set_enum_value3(proto3::BAR);

  PrintOptions print_options;
  print_options.always_print_enums_as_ints = true;
  print_options.always_print_primitive_fields = true;

  auto printed = ToJson(orig, print_options);
  ASSERT_THAT(
      printed,
      IsOkAndHolds("{\"enumValue1\":0,\"enumValue2\":0,\"enumValue3\":1}"));

  auto parsed = ToProto<TestEnumValue>(*printed);

  EXPECT_EQ(parsed->enum_value1(), proto3::FOO);
  EXPECT_EQ(parsed->enum_value2(), proto3::FOO);
  EXPECT_EQ(parsed->enum_value3(), proto3::BAR);
}

TEST_P(JsonTest, TestPrintProto2EnumAsIntWithDefaultValue) {
  protobuf_unittest::TestDefaultEnumValue orig;

  PrintOptions print_options;
  print_options.always_print_enums_as_ints = true;
  print_options.always_print_primitive_fields = true;

  auto printed = ToJson(orig, print_options);
  ASSERT_THAT(printed, IsOkAndHolds("{\"enumValue\":2}"));

  auto parsed = ToProto<protobuf_unittest::TestDefaultEnumValue>(*printed);
  ASSERT_OK(parsed);

  EXPECT_EQ(parsed->enum_value(), protobuf_unittest::DEFAULT);
}

TEST_P(JsonTest, QuotedEnumValue) {
  auto m = ToProto<TestEnumValue>(R"json(
    {"enumValue1": "1"}
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->enum_value1(), proto3::BAR);
}

TEST_P(JsonTest, WebSafeBytes) {
  auto m = ToProto<TestMessage>(R"json({
      "bytesValue": "-_"
  })json");
  ASSERT_OK(m);

  EXPECT_EQ(m->bytes_value(), "\xfb");
}

TEST_P(JsonTest, ParseMessage) {
  auto m = ToProto<TestMessage>(R"json(
    {
      "boolValue": true,
      "int32Value": 1234567891,
      "int64Value": -5302428716536692736,
      "uint32Value": 42,
      "uint64Value": 530242871653669,
      "floatValue": 3.4e+38,
      "doubleValue": -55.3,
      "stringValue": "foo bar baz",
      "enumValue": "BAR",
      "messageValue": {
        "value": 2048
      },

      "repeatedBoolValue": [true],
      "repeatedInt32Value": [0, -42],
      "repeatedUint64Value": [1, 2],
      "repeatedDoubleValue": [1.5, -2],
      "repeatedStringValue": ["foo", "bar ", ""],
      "repeatedEnumValue": [1, "FOO"],
      "repeatedMessageValue": [
        {"value": 40},
        {},
        {"value": 96}
      ]
    }
  )json");
  ASSERT_OK(m);

  EXPECT_TRUE(m->bool_value());
  EXPECT_EQ(m->int32_value(), 1234567891);
  EXPECT_EQ(m->int64_value(), -5302428716536692736);
  EXPECT_EQ(m->uint32_value(), 42);
  EXPECT_EQ(m->uint64_value(), 530242871653669);
  EXPECT_EQ(m->float_value(), 3.4e+38f);
  EXPECT_EQ(m->double_value(),
            -55.3);  // This value is intentionally not a nice
                     // round number in base 2, so its floating point
                     // representation has many digits at the end, which
                     // printing back to JSON must handle well.
  EXPECT_EQ(m->string_value(), "foo bar baz");
  EXPECT_EQ(m->enum_value(), proto3::EnumType::BAR);
  EXPECT_EQ(m->message_value().value(), 2048);

  EXPECT_THAT(m->repeated_bool_value(), ElementsAre(true));
  EXPECT_THAT(m->repeated_int32_value(), ElementsAre(0, -42));
  EXPECT_THAT(m->repeated_uint64_value(), ElementsAre(1, 2));
  EXPECT_THAT(m->repeated_double_value(), ElementsAre(1.5, -2));
  EXPECT_THAT(m->repeated_string_value(), ElementsAre("foo", "bar ", ""));
  EXPECT_THAT(m->repeated_enum_value(), ElementsAre(proto3::BAR, proto3::FOO));

  ASSERT_THAT(m->repeated_message_value(), SizeIs(3));
  EXPECT_EQ(m->repeated_message_value(0).value(), 40);
  EXPECT_EQ(m->repeated_message_value(1).value(), 0);
  EXPECT_EQ(m->repeated_message_value(2).value(), 96);

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"boolValue":true,"int32Value":1234567891,"int64Value":"-5302428716536692736",)"
          R"("uint32Value":42,"uint64Value":"530242871653669","floatValue":3.4e+38,)"
          R"("doubleValue":-55.3,"stringValue":"foo bar baz","enumValue":"BAR",)"
          R"("messageValue":{"value":2048},"repeatedBoolValue":[true],"repeatedInt32Value":[0,-42])"
          R"(,"repeatedUint64Value":["1","2"],"repeatedDoubleValue":[1.5,-2],)"
          R"("repeatedStringValue":["foo","bar ",""],"repeatedEnumValue":["BAR","FOO"],)"
          R"("repeatedMessageValue":[{"value":40},{},{"value":96}]})"));
}

TEST_P(JsonTest, CurseOfAtob) {
  auto m = ToProto<TestMessage>(R"json(
    {
      repeatedBoolValue: ["0", "1", "false", "true", "f", "t", "no", "yes", "n", "y"]
    }
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->repeated_bool_value(),
              ElementsAre(false, true, false, true, false, true, false, true,
                          false, true));
}

TEST_P(JsonTest, FloatPrecision) {
  google::protobuf::Value v;
  v.mutable_list_value()->add_values()->set_number_value(0.9900000095367432);
  v.mutable_list_value()->add_values()->set_number_value(0.8799999952316284);

  EXPECT_THAT(ToJson(v),
              IsOkAndHolds("[0.99000000953674316,0.87999999523162842]"));
}

TEST_P(JsonTest, FloatMinMaxValue) {
  // 3.4028235e38 is FLT_MAX to 8-significant-digits. The final digit (5)
  // is rounded up; that means that when parsing this as a 64-bit FP number,
  // the value ends up higher than FLT_MAX. We still want to accept it though,
  // as a reasonable representation of FLT_MAX.
  auto m = ToProto<TestMessage>(R"json(
    {
      "repeatedFloatValue": [3.4028235e38, -3.4028235e38],
    }
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->repeated_float_value(), ElementsAre(FLT_MAX, -FLT_MAX));
}

TEST_P(JsonTest, FloatOutOfRange) {
  // Check that the slightly-lenient parsing demonstrated in FloatMinMaxValue
  // doesn't mean we allow all values. The value being parsed differs only
  // in the least significant (represented) digit.
  auto m = ToProto<TestMessage>(R"json(
    {
      "floatValue": 3.4028236e38
    }
  )json");
  EXPECT_THAT(m, StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, ParseLegacySingleRepeatedField) {
  auto m = ToProto<TestMessage>(R"json({
    "repeatedInt32Value": 1997,
    "repeatedStringValue": "oh no",
    "repeatedEnumValue": "BAR",
    "repeatedMessageValue": {"value": -1}
  })json");
  ASSERT_OK(m);

  EXPECT_THAT(m->repeated_int32_value(), ElementsAre(1997));
  EXPECT_THAT(m->repeated_string_value(), ElementsAre("oh no"));
  EXPECT_THAT(m->repeated_enum_value(), ElementsAre(proto3::EnumType::BAR));

  ASSERT_THAT(m->repeated_message_value(), SizeIs(1));
  EXPECT_EQ(m->repeated_message_value(0).value(), -1);

  EXPECT_THAT(ToJson(*m),
              IsOkAndHolds(R"({"repeatedInt32Value":[1997],)"
                           R"("repeatedStringValue":["oh no"],)"
                           R"("repeatedEnumValue":["BAR"],)"
                           R"("repeatedMessageValue":[{"value":-1}]})"));
}

TEST_P(JsonTest, ParseMap) {
  TestMap message;
  (*message.mutable_string_map())["hello"] = 1234;
  auto printed = ToJson(message);
  ASSERT_THAT(printed, IsOkAndHolds(R"({"stringMap":{"hello":1234}})"));

  auto other = ToProto<TestMap>(*printed);
  ASSERT_OK(other);
  EXPECT_EQ(other->DebugString(), message.DebugString());
}

TEST_P(JsonTest, RepeatedMapKey) {
  EXPECT_THAT(ToProto<TestMap>(R"json({
    "string_map": {
      "twiceKey": 0,
      "twiceKey": 1
    }
  })json"),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, ParsePrimitiveMapIn) {
  MapIn message;
  PrintOptions print_options;
  print_options.always_print_primitive_fields = true;
  auto printed = ToJson(message, print_options);
  ASSERT_THAT(
      ToJson(message, print_options),
      IsOkAndHolds(R"({"other":"","things":[],"mapInput":{},"mapAny":{}})"));

  auto other = ToProto<MapIn>(*printed);
  ASSERT_OK(other);
  EXPECT_EQ(other->DebugString(), message.DebugString());
}

TEST_P(JsonTest, PrintPrimitiveOneof) {
  TestOneof message;
  PrintOptions options;
  options.always_print_primitive_fields = true;
  message.mutable_oneof_message_value();
  EXPECT_THAT(ToJson(message, options),
              IsOkAndHolds(R"({"oneofMessageValue":{"value":0}})"));

  message.set_oneof_int32_value(1);
  EXPECT_THAT(ToJson(message, options),
              IsOkAndHolds(R"({"oneofInt32Value":1})"));
}

TEST_P(JsonTest, ParseOverOneof) {
  TestOneof m;
  m.set_oneof_string_value("foo");
  ASSERT_OK(ToProto(m, R"json({
    "oneofInt32Value": 5,
  })json"));
  EXPECT_EQ(m.oneof_int32_value(), 5);
}

TEST_P(JsonTest, RepeatedSingularKeys) {
  auto m = ToProto<TestMessage>(R"json({
    "int32Value": 1,
    "int32Value": 2
  })json");
  EXPECT_OK(m);
  EXPECT_EQ(m->int32_value(), 2);
}

TEST_P(JsonTest, RepeatedRepeatedKeys) {
  auto m = ToProto<TestMessage>(R"json({
    "repeatedInt32Value": [1],
    "repeatedInt32Value": [2, 3]
  })json");
  EXPECT_OK(m);
  EXPECT_THAT(m->repeated_int32_value(), ElementsAre(1, 2, 3));
}

TEST_P(JsonTest, RepeatedOneofKeys) {
  EXPECT_THAT(ToProto<TestOneof>(R"json({
    "oneofInt32Value": 1,
    "oneofStringValue": "foo"
  })json"),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, TestParseIgnoreUnknownFields) {
  ParseOptions options;
  options.ignore_unknown_fields = true;
  EXPECT_OK(ToProto<TestMessage>(R"({"unknownName":0})", options));

  TestMessage m;
  m.GetReflection()->MutableUnknownFields(&m)->AddFixed32(9001, 9001);
  m.GetReflection()->MutableUnknownFields(&m)->AddFixed64(9001, 9001);
  m.GetReflection()->MutableUnknownFields(&m)->AddVarint(9001, 9001);
  m.GetReflection()->MutableUnknownFields(&m)->AddLengthDelimited(9001, "9001");
  EXPECT_THAT(ToJson(m), IsOkAndHolds("{}"));
}

TEST_P(JsonTest, TestParseErrors) {
  // Parsing should fail if the field name can not be recognized.
  EXPECT_THAT(ToProto<TestMessage>(R"({"unknownName": 0})"),
              StatusIs(absl::StatusCode::kInvalidArgument));
  // Parsing should fail if the value is invalid.
  EXPECT_THAT(ToProto<TestMessage>(R"("{"int32Value": 2147483648})"),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, TestDynamicMessage) {
  // Create a new DescriptorPool with the same protos as the generated one.
  DescriptorPoolDatabase database(*DescriptorPool::generated_pool());
  DescriptorPool pool(&database);
  // A dynamic version of the test proto.
  DynamicMessageFactory factory;
  std::unique_ptr<Message> message(
      factory.GetPrototype(pool.FindMessageTypeByName("proto3.TestMessage"))
          ->New());
  ASSERT_OK(ToProto(*message, R"json(
    {
      "int32Value": 1024,
      "repeatedInt32Value": [1, 2],
      "messageValue": {
        "value": 2048
      },
      "repeatedMessageValue": [
        {"value": 40},
        {"value": 96}
      ]
    }
  )json"));

  // Convert to generated message for easy inspection.
  TestMessage generated;
  EXPECT_TRUE(generated.ParseFromString(message->SerializeAsString()));

  EXPECT_EQ(generated.int32_value(), 1024);
  EXPECT_THAT(generated.repeated_int32_value(), ElementsAre(1, 2));

  EXPECT_EQ(generated.message_value().value(), 2048);
  ASSERT_EQ(generated.repeated_message_value_size(), 2);
  EXPECT_EQ(generated.repeated_message_value(0).value(), 40);
  EXPECT_EQ(generated.repeated_message_value(1).value(), 96);

  auto message_json = ToJson(*message);
  ASSERT_OK(message_json);
  auto generated_json = ToJson(generated);
  ASSERT_OK(generated_json);
  EXPECT_EQ(*message_json, *generated_json);
}

TEST_P(JsonTest, TestParsingAny) {
  auto m = ToProto<TestAny>(R"json(
    {
      "value": {
        "@type": "type.googleapis.com/proto3.TestMessage",
        "int32_value": 5,
        "string_value": "expected_value",
        "message_value": {"value": 1}
      }
    }
  )json");
  ASSERT_OK(m);

  TestMessage t;
  ASSERT_TRUE(m->value().UnpackTo(&t));
  EXPECT_EQ(t.int32_value(), 5);
  EXPECT_EQ(t.string_value(), "expected_value");
  EXPECT_EQ(t.message_value().value(), 1);

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"value":{"@type":"type.googleapis.com/proto3.TestMessage",)"
          R"("int32Value":5,"stringValue":"expected_value","messageValue":{"value":1}}})"));
}

TEST_P(JsonTest, TestParsingAnyWithRequiredFields) {
  auto m = ToProto<TestAny>(R"json(
    {
      "value": {
        "@type": "type.googleapis.com/protobuf_unittest.TestRequired",
        "a": 5,
        "dummy2": 6,
        "b": 7
      }
    }
  )json");
  ASSERT_OK(m);

  protobuf_unittest::TestRequired t;
  // Can't use UnpackTo directly, since that checks IsInitialized.
  ASSERT_FALSE(m->value().UnpackTo(&t));

  t.Clear();
  EXPECT_TRUE(t.ParsePartialFromString(m->value().value()));
  EXPECT_EQ(t.a(), 5);
  EXPECT_EQ(t.dummy2(), 6);
  EXPECT_EQ(t.b(), 7);
  EXPECT_FALSE(t.has_c());

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"value":{"@type":"type.googleapis.com/protobuf_unittest.TestRequired",)"
          R"("a":5,"dummy2":6,"b":7}})"));
}

TEST_P(JsonTest, TestParsingAnyEndAtType) {
  auto m = ToProto<TestAny>(R"json(
    {
      "value": {
        "int32_value": 5,
        "string_value": "expected_value",
        "message_value": {"value": 1},
        "@type": "type.googleapis.com/proto3.TestMessage"
      }
    }
  )json");
  ASSERT_OK(m);

  TestMessage t;
  ASSERT_TRUE(m->value().UnpackTo(&t));
  EXPECT_EQ(t.int32_value(), 5);
  EXPECT_EQ(t.string_value(), "expected_value");
  EXPECT_EQ(t.message_value().value(), 1);
}

TEST_P(JsonTest, TestParsingNestedAnys) {
  auto m = ToProto<TestAny>(R"json(
    {
      "value": {
        "value": {
          "int32_value": 5,
          "string_value": "expected_value",
          "message_value": {"value": 1},
          "@type": "type.googleapis.com/proto3.TestMessage"
        },
        "@type": "type.googleapis.com/google.protobuf.Any"
      }
    }
  )json");
  ASSERT_OK(m);

  google::protobuf::Any inner;
  ASSERT_TRUE(m->value().UnpackTo(&inner));

  TestMessage t;
  ASSERT_TRUE(inner.UnpackTo(&t));
  EXPECT_EQ(t.int32_value(), 5);
  EXPECT_EQ(t.string_value(), "expected_value");
  EXPECT_EQ(t.message_value().value(), 1);

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"value":{"@type":"type.googleapis.com/google.protobuf.Any",)"
          R"("value":{"@type":"type.googleapis.com/proto3.TestMessage",)"
          R"("int32Value":5,"stringValue":"expected_value","messageValue":{"value":1}}}})"));
}

TEST_P(JsonTest, TestParsingBrokenAny) {
  auto m = ToProto<TestAny>(R"json(
    {
      "value": {}
    }
  )json");
  ASSERT_OK(m);
  EXPECT_EQ(m->value().type_url(), "");
  EXPECT_EQ(m->value().value(), "");

  EXPECT_THAT(ToProto<TestAny>(R"json(
    {
      "value": {
        "type_url": "garbage",
        "value": "bW9yZSBnYXJiYWdl"
      }
    }
  )json"),
              StatusIs(absl::StatusCode::kInvalidArgument));

  TestAny m2;
  m2.mutable_value();
  EXPECT_THAT(ToJson(m2), IsOkAndHolds(R"({"value":{}})"));
  m2.mutable_value()->set_value("garbage");
  // The ESF parser does not return InvalidArgument for this error.
  EXPECT_THAT(ToJson(m2), Not(StatusIs(absl::StatusCode::kOk)));

  m2.Clear();
  m2.mutable_value()->set_type_url("type.googleapis.com/proto3.TestMessage");
  EXPECT_THAT(
      ToJson(m2),
      IsOkAndHolds(
          R"({"value":{"@type":"type.googleapis.com/proto3.TestMessage"}})"));
}

TEST_P(JsonTest, TestFlatList) {
  auto m = ToProto<TestMessage>(R"json(
    {
      "repeatedInt32Value": [[[5]], [6]]
    }
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->repeated_int32_value(), ElementsAre(5, 6));

  // The above flatteing behavior is suppressed for google::protobuf::ListValue.
  auto m2 = ToProto<google::protobuf::Value>(R"json(
    {
      "repeatedInt32Value": [[[5]], [6]]
    }
  )json");
  ASSERT_OK(m2);
  auto fields = m2->struct_value().fields();
  auto list = fields["repeatedInt32Value"].list_value();
  EXPECT_EQ(list.values(0)
                .list_value()
                .values(0)
                .list_value()
                .values(0)
                .number_value(),
            5);
  EXPECT_EQ(list.values(1).list_value().values(0).number_value(), 6);
}

TEST_P(JsonTest, ParseWrappers) {
  auto m = ToProto<TestWrapper>(R"json(
    {
      "boolValue": true,
      "int32Value": 42,
      "stringValue": "ieieo",
    }
  )json");
  ASSERT_OK(m);

  EXPECT_TRUE(m->bool_value().value());
  EXPECT_EQ(m->int32_value().value(), 42);
  EXPECT_EQ(m->string_value().value(), "ieieo");

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"boolValue":true,"int32Value":42,"stringValue":"ieieo"})"));

  auto m2 = ToProto<TestWrapper>(R"json(
    {
      "boolValue": { "value": true },
      "int32Value": { "value": 42 },
      "stringValue": { "value": "ieieo" },
    }
  )json");
  ASSERT_OK(m2);

  EXPECT_TRUE(m2->bool_value().value());
  EXPECT_EQ(m2->int32_value().value(), 42);
  EXPECT_EQ(m2->string_value().value(), "ieieo");
}

TEST_P(JsonTest, TestParsingUnknownAnyFields) {
  absl::string_view input = R"json(
    {
      "value": {
        "@type": "type.googleapis.com/proto3.TestMessage",
        "unknown_field": "UNKNOWN_VALUE",
        "string_value": "expected_value"
      }
    }
  )json";

  EXPECT_THAT(ToProto<TestAny>(input),
              StatusIs(absl::StatusCode::kInvalidArgument));

  ParseOptions options;
  options.ignore_unknown_fields = true;
  auto m = ToProto<TestAny>(input, options);
  ASSERT_OK(m);

  TestMessage t;
  ASSERT_TRUE(m->value().UnpackTo(&t));
  EXPECT_EQ(t.string_value(), "expected_value");
}

TEST_P(JsonTest, TestHugeBareString) {
  auto m = ToProto<TestMessage>(R"json({
    "int64Value": 6009652459062546621
  })json");
  ASSERT_OK(m);
  EXPECT_EQ(m->int64_value(), 6009652459062546621);
}

TEST_P(JsonTest, TestParsingUnknownEnumsProto2) {
  absl::string_view input = R"json({"ayuLmao": "UNKNOWN_VALUE"})json";

  EXPECT_THAT(ToProto<protobuf_unittest::TestNumbers>(input),
              StatusIs(absl::StatusCode::kInvalidArgument));

  ParseOptions options;
  options.ignore_unknown_fields = true;
  auto m = ToProto<protobuf_unittest::TestNumbers>(input, options);
  ASSERT_OK(m);
  EXPECT_FALSE(m->has_a());
}

TEST_P(JsonTest, TestParsingUnknownEnumsProto3) {
  TestMessage m;
  absl::string_view input = R"json({"enum_value":"UNKNOWN_VALUE"})json";

  m.set_enum_value(proto3::BAR);
  ASSERT_THAT(ToProto(m, input), StatusIs(absl::StatusCode::kInvalidArgument));
  EXPECT_EQ(m.enum_value(), proto3::BAR);  // Keep previous value

  ParseOptions options;
  options.ignore_unknown_fields = true;
  ASSERT_OK(ToProto(m, input, options));
  EXPECT_EQ(m.enum_value(), 0);  // Unknown enum value must be decoded as 0
}

TEST_P(JsonTest, TestParsingUnknownEnumsProto3FromInt) {
  TestMessage m;
  absl::string_view input = R"json({"enum_value":12345})json";

  m.set_enum_value(proto3::BAR);
  ASSERT_OK(ToProto(m, input));
  EXPECT_EQ(m.enum_value(), 12345);

  ParseOptions options;
  options.ignore_unknown_fields = true;
  ASSERT_OK(ToProto(m, input, options));
  EXPECT_EQ(m.enum_value(), 12345);
}

// Trying to pass an object as an enum field value is always treated as an
// error
TEST_P(JsonTest, TestParsingUnknownEnumsProto3FromObject) {
  absl::string_view input = R"json({"enum_value": {}})json";

  EXPECT_THAT(ToProto<TestMessage>(input),
              StatusIs(absl::StatusCode::kInvalidArgument));

  ParseOptions options;
  options.ignore_unknown_fields = true;
  EXPECT_THAT(ToProto<TestMessage>(input, options),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, TestParsingUnknownEnumsProto3FromArray) {
  absl::string_view input = R"json({"enum_value": []})json";

  EXPECT_THAT(ToProto<TestMessage>(input),
              StatusIs(absl::StatusCode::kInvalidArgument));

  ParseOptions options;
  options.ignore_unknown_fields = true;
  EXPECT_THAT(ToProto<TestMessage>(input, options),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, TestParsingRepeatedUnknownEnums) {
  absl::string_view input = R"json({
    "repeated_enum_value": ["FOO", "BAZ", "BAR"]
  })json";

  EXPECT_THAT(ToProto<TestMessage>(input),
              StatusIs(absl::StatusCode::kInvalidArgument));

  ParseOptions options;
  options.ignore_unknown_fields = true;
  auto m = ToProto<TestMessage>(input, options);
  ASSERT_OK(m);
  EXPECT_THAT(m->repeated_enum_value(), ElementsAre(proto3::FOO, proto3::BAR));
}

TEST_P(JsonTest, TestParsingEnumCaseSensitive) {
  TestMessage m;
  m.set_enum_value(proto3::FOO);
  EXPECT_THAT(ToProto(m, R"json({"enum_value": "bar"})json"),
              StatusIs(absl::StatusCode::kInvalidArgument));
  // Default behavior is case-sensitive, so keep previous value.
  EXPECT_EQ(m.enum_value(), proto3::FOO);
}

TEST_P(JsonTest, TestParsingEnumLowercase) {
  ParseOptions options;
  options.case_insensitive_enum_parsing = true;
  auto m =
      ToProto<TestMessage>(R"json({"enum_value": "TLSv1_2"})json", options);
  ASSERT_OK(m);
  EXPECT_THAT(m->enum_value(), proto3::TLSv1_2);
}

TEST_P(JsonTest, TestParsingEnumIgnoreCase) {
  TestMessage m;
  m.set_enum_value(proto3::FOO);

  ParseOptions options;
  options.case_insensitive_enum_parsing = true;
  ASSERT_OK(ToProto(m, R"json({"enum_value":"bar"})json", options));
  EXPECT_EQ(m.enum_value(), proto3::BAR);
}

TEST_P(JsonTest, Extensions) {
  if (GetParam() == Codec::kResolver) {
    GTEST_SKIP();
  }

  auto m = ToProto<protobuf_unittest::TestMixedFieldsAndExtensions>(R"json({
    "[protobuf_unittest.TestMixedFieldsAndExtensions.c]": 42,
    "a": 5,
    "b": [1, 2, 3],
    "[protobuf_unittest.TestMixedFieldsAndExtensions.d]": [1, 1, 2, 3, 5, 8, 13]
  })json");
  ASSERT_OK(m);
  EXPECT_EQ(m->a(), 5);
  EXPECT_THAT(m->b(), ElementsAre(1, 2, 3));
  EXPECT_EQ(m->GetExtension(protobuf_unittest::TestMixedFieldsAndExtensions::c),
            42);
  EXPECT_THAT(
      m->GetRepeatedExtension(protobuf_unittest::TestMixedFieldsAndExtensions::d),
      ElementsAre(1, 1, 2, 3, 5, 8, 13));

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"a":5,)"
          R"("[protobuf_unittest.TestMixedFieldsAndExtensions.c]":42,)"
          R"("b":[1,2,3],)"
          R"("[protobuf_unittest.TestMixedFieldsAndExtensions.d]":[1,1,2,3,5,8,13]})"));

  auto m2 = ToProto<protobuf_unittest::TestAllTypes>(R"json({
    "[this.extension.does.not.exist]": 42
  })json");
  EXPECT_THAT(m2, StatusIs(absl::StatusCode::kInvalidArgument));

  auto m3 = ToProto<protobuf_unittest::TestAllTypes>(R"json({
    "[protobuf_unittest.TestMixedFieldsAndExtensions.c]": 42
  })json");
  EXPECT_THAT(m3, StatusIs(absl::StatusCode::kInvalidArgument));
}

// Parsing does NOT work like MergeFrom: existing repeated field values are
// clobbered, not appended to.
TEST_P(JsonTest, TestOverwriteRepeated) {
  TestMessage m;
  m.add_repeated_int32_value(5);

  ASSERT_OK(ToProto(m, R"json({"repeated_int32_value": [1, 2, 3]})json"));
  EXPECT_THAT(m.repeated_int32_value(), ElementsAre(1, 2, 3));
}


TEST_P(JsonTest, TestDuration) {
  auto m = ToProto<proto3::TestDuration>(R"json(
    {
      "value": "123456.789s",
      "repeated_value": ["0.1s", "999s"]
    }
  )json");
  ASSERT_OK(m);

  EXPECT_EQ(m->value().seconds(), 123456);
  EXPECT_EQ(m->value().nanos(), 789000000);

  EXPECT_THAT(m->repeated_value(), SizeIs(2));
  EXPECT_EQ(m->repeated_value(0).seconds(), 0);
  EXPECT_EQ(m->repeated_value(0).nanos(), 100000000);
  EXPECT_EQ(m->repeated_value(1).seconds(), 999);
  EXPECT_EQ(m->repeated_value(1).nanos(), 0);

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"value":"123456.789s","repeatedValue":["0.100s","999s"]})"));

  auto m2 = ToProto<proto3::TestDuration>(R"json(
    {
      "value": {"seconds": 4, "nanos": 5},
    }
  )json");
  ASSERT_OK(m2);

  EXPECT_EQ(m2->value().seconds(), 4);
  EXPECT_EQ(m2->value().nanos(), 5);

  // Negative duration with zero seconds.
  auto m3 = ToProto<proto3::TestDuration>(R"json(
    {
      "value": {"nanos": -5},
    }
  )json");
  ASSERT_OK(m3);
  EXPECT_EQ(m3->value().seconds(), 0);
  EXPECT_EQ(m3->value().nanos(), -5);
  EXPECT_THAT(ToJson(m3->value()), IsOkAndHolds("\"-0.000000005s\""));

  // Negative duration with zero nanos.
  auto m4 = ToProto<proto3::TestDuration>(R"json(
    {
      "value": {"seconds": -5},
    }
  )json");
  ASSERT_OK(m4);
  EXPECT_EQ(m4->value().seconds(), -5);
  EXPECT_EQ(m4->value().nanos(), 0);
  EXPECT_THAT(ToJson(m4->value()), IsOkAndHolds("\"-5s\""));

  // Parse "0.5s" as a JSON string.
  auto m5 = ToProto<proto3::TestDuration>(R"json(
    {
      "value": "0.5s",
    }
  )json");
  ASSERT_OK(m5);
  EXPECT_EQ(m5->value().seconds(), 0);
  EXPECT_EQ(m5->value().nanos(), 500000000);
  EXPECT_THAT(ToJson(m5->value()), IsOkAndHolds("\"0.500s\""));
}

// These tests are not exhaustive; tests in //google/protobuf/conformance
// are more comprehensive.
TEST_P(JsonTest, TestTimestamp) {
  auto m = ToProto<proto3::TestTimestamp>(R"json(
    {
      "value": "1996-02-27T12:00:00Z",
      "repeated_value": ["9999-12-31T23:59:59Z"]
    }
  )json");
  ASSERT_OK(m);

  EXPECT_EQ(m->value().seconds(), 825422400);
  EXPECT_EQ(m->value().nanos(), 0);
  EXPECT_THAT(m->repeated_value(), SizeIs(1));
  EXPECT_EQ(m->repeated_value(0).seconds(), 253402300799);
  EXPECT_EQ(m->repeated_value(0).nanos(), 0);

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"value":"1996-02-27T12:00:00Z","repeatedValue":["9999-12-31T23:59:59Z"]})"));

  auto m2 = ToProto<proto3::TestTimestamp>(R"json(
    {
      "value": {"seconds": 4, "nanos": 5},
    }
  )json");
  ASSERT_OK(m2);

  EXPECT_EQ(m2->value().seconds(), 4);
  EXPECT_EQ(m2->value().nanos(), 5);
}

// This test case comes from Envoy's tests. They like to parse a Value out of
// YAML, turn it into JSON, and then parse it as a different proto. This means
// we must be extremely careful with integer fields, because they need to
// round-trip through doubles. This happens all over Envoy. :(
TEST_P(JsonTest, TestEnvoyRoundTrip) {
  auto m = ToProto<google::protobuf::Value>(R"json(
    {
      "value": {"seconds": 1234567891, "nanos": 234000000},
    }
  )json");
  ASSERT_OK(m);

  auto j = ToJson(*m);
  ASSERT_OK(j);

  auto m2 = ToProto<proto3::TestTimestamp>(*j);
  ASSERT_OK(m2);

  EXPECT_EQ(m2->value().seconds(), 1234567891);
  EXPECT_EQ(m2->value().nanos(), 234000000);
}

TEST_P(JsonTest, TestFieldMask) {
  auto m = ToProto<proto3::TestFieldMask>(R"json(
    {
      "value": "foo,bar.bazBaz"
    }
  )json");
  ASSERT_OK(m);

  EXPECT_THAT(m->value().paths(), ElementsAre("foo", "bar.baz_baz"));
  EXPECT_THAT(ToJson(*m), IsOkAndHolds(R"({"value":"foo,bar.bazBaz"})"));

  auto m2 = ToProto<proto3::TestFieldMask>(R"json(
    {
      "value": {
        "paths": ["yep.really"]
      },
    }
  )json");
  ASSERT_OK(m2);

  EXPECT_THAT(m2->value().paths(), ElementsAre("yep.really"));
}

TEST_P(JsonTest, TestFieldMaskSnakeCase) {
  auto m = ToProto<proto3::TestFieldMask>(R"json(
    {
      "value": "foo_bar"
    }
  )json");
  ASSERT_OK(m);

  EXPECT_THAT(m->value().paths(), ElementsAre("foo_bar"));
}

TEST_P(JsonTest, TestLegalNullsInArray) {
  auto m = ToProto<proto3::TestNullValue>(R"json({
    "repeatedNullValue": [null]
  })json");
  ASSERT_OK(m);

  EXPECT_THAT(m->repeated_null_value(),
              ElementsAre(google::protobuf::NULL_VALUE));

  auto m2 = ToProto<proto3::TestValue>(R"json({
    "repeatedValue": [null]
  })json");
  ASSERT_OK(m2);

  ASSERT_THAT(m2->repeated_value(), SizeIs(1));
  EXPECT_TRUE(m2->repeated_value(0).has_null_value());

  m2->Clear();
  m2->mutable_value();  // Materialize an empty singular Value.
  m2->add_repeated_value();
  m2->add_repeated_value()->set_string_value("solitude");
  m2->add_repeated_value();
  EXPECT_THAT(ToJson(*m2), IsOkAndHolds(R"({"repeatedValue":["solitude"]})"));
}

TEST_P(JsonTest, EmptyValue) {
  EXPECT_THAT(ToJson(google::protobuf::Value()), IsOkAndHolds(""));

  google::protobuf::Struct s;
  s.mutable_fields()->emplace("empty", google::protobuf::Value());
  EXPECT_THAT(ToJson(s), IsOkAndHolds("{}"));
}

TEST_P(JsonTest, TrailingGarbage) {
  EXPECT_THAT(ToProto<TestMessage>("{}garbage"),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, ListList) {
  auto m = ToProto<proto3::TestListValue>(R"json({
    "repeated_value": [["ayy", "lmao"]]
  })json");
  ASSERT_OK(m);

  EXPECT_EQ(m->repeated_value(0).values(0).string_value(), "ayy");
  EXPECT_EQ(m->repeated_value(0).values(1).string_value(), "lmao");

  m = ToProto<proto3::TestListValue>(R"json({
    "repeated_value": [{
      "values": ["ayy", "lmao"]
    }]
  })json");
  ASSERT_OK(m);

  EXPECT_EQ(m->repeated_value(0).values(0).string_value(), "ayy");
  EXPECT_EQ(m->repeated_value(0).values(1).string_value(), "lmao");
}

TEST_P(JsonTest, HtmlEscape) {
  TestMessage m;
  m.set_string_value("</script>");
  EXPECT_THAT(ToJson(m),
              IsOkAndHolds(R"({"stringValue":"\u003c/script\u003e"})"));

  proto3::TestEvilJson m2;
  PrintOptions opts;
  opts.always_print_primitive_fields = true;
  EXPECT_THAT(
      ToJson(m2, opts),
      IsOkAndHolds(
          R"({"regular_name":0,"\u003c/script\u003e":0,)"
          R"("unbalanced\"quotes":0,)"
          R"("\"\u003cscript\u003ealert('hello!);\u003c/script\u003e":0})"));
}

TEST_P(JsonTest, FieldOrder) {
  // $ protoscope -s <<< "3: 3 22: 2 1: 1 22: 2"
  std::string out;
  absl::Status s = BinaryToJsonString(
      resolver_.get(), "type.googleapis.com/proto3.TestMessage",
      "\x18\x03\xb0\x01\x02\x08\x01\xb0\x01\x02", &out);
  ASSERT_OK(s);
  EXPECT_EQ(
      out, R"({"boolValue":true,"int64Value":"3","repeatedInt32Value":[2,2]})");
}

// JSON values get special treatment when it comes to pre-existing values in
// their repeated fields, when parsing through their dedicated syntax.
TEST_P(JsonTest, ClearPreExistingRepeatedInJsonValues) {
  google::protobuf::ListValue l;
  l.add_values()->set_string_value("hello");
  ASSERT_OK(JsonStringToMessage("[]", &l));
  EXPECT_THAT(l.values(), IsEmpty());

  google::protobuf::Struct s;
  (*s.mutable_fields())["hello"].set_string_value("world");
  ASSERT_OK(JsonStringToMessage("{}", &s));
  EXPECT_THAT(s.fields(), IsEmpty());
}

TEST(JsonErrorTest, FieldNameAndSyntaxErrorInSeparateChunks) {
  std::unique_ptr<TypeResolver> resolver{
      google::protobuf::util::NewTypeResolverForDescriptorPool(
          "type.googleapis.com", DescriptorPool::generated_pool())};
  io::internal::TestZeroCopyInputStream input_stream(
      {"{\"bool_value\":", "5}"});
  std::string result;
  io::StringOutputStream output_stream(&result);
  absl::Status s = JsonToBinaryStream(
      resolver.get(), "type.googleapis.com/proto3.TestMessage", &input_stream,
      &output_stream, ParseOptions{});
  ASSERT_FALSE(s.ok());
  EXPECT_THAT(
      s.message(),
      ContainsRegex("invalid *JSON *in *type.googleapis.com/proto3.TestMessage "
                    "*@ *bool_value"));
}

}  // namespace
}  // namespace json
}  // namespace protobuf
}  // namespace google
