blob: 10b8fb58c956741f467d14f27a6165d014247c27 [file] [log] [blame]
// Copyright 2021 gRPC authors.
//
// 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 "src/core/lib/json/json_object_loader.h"
#include <cstdint>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/strings/str_join.h"
namespace grpc_core {
namespace {
template <typename T>
absl::StatusOr<T> Parse(absl::string_view json,
const JsonArgs& args = JsonArgs()) {
auto parsed = Json::Parse(json);
if (!parsed.ok()) return parsed.status();
return LoadFromJson<T>(*parsed, args);
}
//
// Signed integer tests
//
template <typename T>
class SignedIntegerTest : public ::testing::Test {};
TYPED_TEST_SUITE_P(SignedIntegerTest);
TYPED_TEST_P(SignedIntegerTest, IntegerFields) {
struct TestStruct {
TypeParam value = 0;
TypeParam optional_value = 0;
absl::optional<TypeParam> absl_optional_value;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("value", &TestStruct::value)
.OptionalField("optional_value", &TestStruct::optional_value)
.OptionalField("absl_optional_value",
&TestStruct::absl_optional_value)
.Finish();
return loader;
}
};
// Positive number.
auto test_struct = Parse<TestStruct>("{\"value\": 5}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, 5);
EXPECT_EQ(test_struct->optional_value, 0);
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Negative number.
test_struct = Parse<TestStruct>("{\"value\": -5}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, -5);
EXPECT_EQ(test_struct->optional_value, 0);
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Encoded in a JSON string.
test_struct = Parse<TestStruct>("{\"value\": \"5\"}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, 5);
EXPECT_EQ(test_struct->optional_value, 0);
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Fails if required field is not present.
test_struct = Parse<TestStruct>("{}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:value error:field not present]")
<< test_struct.status();
// Optional fields present.
test_struct = Parse<TestStruct>(
"{\"value\": 5, \"optional_value\": 7, "
"\"absl_optional_value\": 9}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, 5);
EXPECT_EQ(test_struct->optional_value, 7);
EXPECT_EQ(test_struct->absl_optional_value, 9);
// Wrong JSON type.
test_struct = Parse<TestStruct>(
"{\"value\": [], \"optional_value\": {}, "
"\"absl_optional_value\": true}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value error:is not a number; "
"field:optional_value error:is not a number; "
"field:value error:is not a number]")
<< test_struct.status();
}
REGISTER_TYPED_TEST_SUITE_P(SignedIntegerTest, IntegerFields);
using IntegerTypes = ::testing::Types<int32_t, int64_t>;
INSTANTIATE_TYPED_TEST_SUITE_P(My, SignedIntegerTest, IntegerTypes);
//
// Unsigned integer tests
//
template <typename T>
class UnsignedIntegerTest : public ::testing::Test {};
TYPED_TEST_SUITE_P(UnsignedIntegerTest);
TYPED_TEST_P(UnsignedIntegerTest, IntegerFields) {
struct TestStruct {
TypeParam value = 0;
TypeParam optional_value = 0;
absl::optional<TypeParam> absl_optional_value;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("value", &TestStruct::value)
.OptionalField("optional_value", &TestStruct::optional_value)
.OptionalField("absl_optional_value",
&TestStruct::absl_optional_value)
.Finish();
return loader;
}
};
// Positive number.
auto test_struct = Parse<TestStruct>("{\"value\": 5}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, 5);
EXPECT_EQ(test_struct->optional_value, 0);
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Negative number.
test_struct = Parse<TestStruct>("{\"value\": -5}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:value error:failed to parse non-negative number]")
<< test_struct.status();
// Encoded in a JSON string.
test_struct = Parse<TestStruct>("{\"value\": \"5\"}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, 5);
EXPECT_EQ(test_struct->optional_value, 0);
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Fails if required field is not present.
test_struct = Parse<TestStruct>("{}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:value error:field not present]")
<< test_struct.status();
// Optional fields present.
test_struct = Parse<TestStruct>(
"{\"value\": 5, \"optional_value\": 7, "
"\"absl_optional_value\": 9}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, 5);
EXPECT_EQ(test_struct->optional_value, 7);
ASSERT_TRUE(test_struct->absl_optional_value.has_value());
EXPECT_EQ(*test_struct->absl_optional_value, 9);
// Wrong JSON type.
test_struct = Parse<TestStruct>(
"{\"value\": [], \"optional_value\": {}, "
"\"absl_optional_value\": true}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value error:is not a number; "
"field:optional_value error:is not a number; "
"field:value error:is not a number]")
<< test_struct.status();
}
REGISTER_TYPED_TEST_SUITE_P(UnsignedIntegerTest, IntegerFields);
using UnsignedIntegerTypes = ::testing::Types<uint32_t, uint64_t>;
INSTANTIATE_TYPED_TEST_SUITE_P(My, UnsignedIntegerTest, UnsignedIntegerTypes);
//
// Floating-point tests
//
template <typename T>
class FloatingPointTest : public ::testing::Test {};
TYPED_TEST_SUITE_P(FloatingPointTest);
TYPED_TEST_P(FloatingPointTest, FloatFields) {
struct TestStruct {
TypeParam value = 0;
TypeParam optional_value = 0;
absl::optional<TypeParam> absl_optional_value;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("value", &TestStruct::value)
.OptionalField("optional_value", &TestStruct::optional_value)
.OptionalField("absl_optional_value",
&TestStruct::absl_optional_value)
.Finish();
return loader;
}
};
// Positive number.
auto test_struct = Parse<TestStruct>("{\"value\": 5.2}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_NEAR(test_struct->value, 5.2, 0.0001);
EXPECT_EQ(test_struct->optional_value, 0);
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Negative number.
test_struct = Parse<TestStruct>("{\"value\": -5.2}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_NEAR(test_struct->value, -5.2, 0.0001);
EXPECT_EQ(test_struct->optional_value, 0);
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Encoded in a JSON string.
test_struct = Parse<TestStruct>("{\"value\": \"5.2\"}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_NEAR(test_struct->value, 5.2, 0.0001);
EXPECT_EQ(test_struct->optional_value, 0);
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Fails if required field is not present.
test_struct = Parse<TestStruct>("{}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:value error:field not present]")
<< test_struct.status();
// Optional fields present.
test_struct = Parse<TestStruct>(
"{\"value\": 5.2, \"optional_value\": 7.5, "
"\"absl_optional_value\": 9.8}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_NEAR(test_struct->value, 5.2, 0.0001);
EXPECT_NEAR(test_struct->optional_value, 7.5, 0.0001);
ASSERT_TRUE(test_struct->absl_optional_value.has_value());
EXPECT_NEAR(*test_struct->absl_optional_value, 9.8, 0.0001);
// Wrong JSON type.
test_struct = Parse<TestStruct>(
"{\"value\": [], \"optional_value\": {}, "
"\"absl_optional_value\": true}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value error:is not a number; "
"field:optional_value error:is not a number; "
"field:value error:is not a number]")
<< test_struct.status();
}
REGISTER_TYPED_TEST_SUITE_P(FloatingPointTest, FloatFields);
using FloatingPointTypes = ::testing::Types<float, double>;
INSTANTIATE_TYPED_TEST_SUITE_P(My, FloatingPointTest, FloatingPointTypes);
//
// Boolean tests
//
TEST(JsonObjectLoader, BooleanFields) {
struct TestStruct {
bool value = false;
bool optional_value = true;
absl::optional<bool> absl_optional_value;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("value", &TestStruct::value)
.OptionalField("optional_value", &TestStruct::optional_value)
.OptionalField("absl_optional_value",
&TestStruct::absl_optional_value)
.Finish();
return loader;
}
};
// True.
auto test_struct = Parse<TestStruct>("{\"value\": true}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, true);
EXPECT_EQ(test_struct->optional_value, true); // Unmodified.
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// False.
test_struct = Parse<TestStruct>("{\"value\": false}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, false);
EXPECT_EQ(test_struct->optional_value, true); // Unmodified.
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Fails if required field is not present.
test_struct = Parse<TestStruct>("{}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:value error:field not present]")
<< test_struct.status();
// Optional fields present.
test_struct = Parse<TestStruct>(
"{\"value\": true, \"optional_value\": false,"
"\"absl_optional_value\": true}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, true);
EXPECT_EQ(test_struct->optional_value, false);
EXPECT_EQ(test_struct->absl_optional_value, true);
// Wrong JSON type.
test_struct = Parse<TestStruct>(
"{\"value\": [], \"optional_value\": {}, "
"\"absl_optional_value\": 1}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value error:is not a boolean; "
"field:optional_value error:is not a boolean; "
"field:value error:is not a boolean]")
<< test_struct.status();
}
//
// String tests
//
TEST(JsonObjectLoader, StringFields) {
struct TestStruct {
std::string value;
std::string optional_value;
absl::optional<std::string> absl_optional_value;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("value", &TestStruct::value)
.OptionalField("optional_value", &TestStruct::optional_value)
.OptionalField("absl_optional_value",
&TestStruct::absl_optional_value)
.Finish();
return loader;
}
};
// Valid string.
auto test_struct = Parse<TestStruct>("{\"value\": \"foo\"}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, "foo");
EXPECT_EQ(test_struct->optional_value, "");
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Fails if required field is not present.
test_struct = Parse<TestStruct>("{}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:value error:field not present]")
<< test_struct.status();
// Optional fields present.
test_struct = Parse<TestStruct>(
"{\"value\": \"foo\", \"optional_value\": \"bar\","
"\"absl_optional_value\": \"baz\"}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, "foo");
EXPECT_EQ(test_struct->optional_value, "bar");
EXPECT_EQ(test_struct->absl_optional_value, "baz");
// Wrong JSON type.
test_struct = Parse<TestStruct>(
"{\"value\": [], \"optional_value\": {}, "
"\"absl_optional_value\": 1}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value error:is not a string; "
"field:optional_value error:is not a string; "
"field:value error:is not a string]")
<< test_struct.status();
}
//
// Duration tests
//
TEST(JsonObjectLoader, DurationFields) {
struct TestStruct {
Duration value = Duration::Zero();
Duration optional_value = Duration::Zero();
absl::optional<Duration> absl_optional_value;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("value", &TestStruct::value)
.OptionalField("optional_value", &TestStruct::optional_value)
.OptionalField("absl_optional_value",
&TestStruct::absl_optional_value)
.Finish();
return loader;
}
};
// Valid duration string.
auto test_struct = Parse<TestStruct>("{\"value\": \"3s\"}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, Duration::Seconds(3));
EXPECT_EQ(test_struct->optional_value, Duration::Zero());
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Invalid duration strings.
test_struct = Parse<TestStruct>(
"{\"value\": \"3sec\", \"optional_value\": \"foos\","
"\"absl_optional_value\": \"1.0123456789s\"}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value error:"
"Not a duration (too many digits after decimal); "
"field:optional_value error:"
"Not a duration (not a number of seconds); "
"field:value error:Not a duration (no s suffix)]")
<< test_struct.status();
test_struct = Parse<TestStruct>("{\"value\": \"3.xs\"}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:value error:Not a duration (not a number of nanoseconds)]")
<< test_struct.status();
// Fails if required field is not present.
test_struct = Parse<TestStruct>("{}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:value error:field not present]")
<< test_struct.status();
// Optional fields present.
test_struct = Parse<TestStruct>(
"{\"value\": \"3s\", \"optional_value\": \"3.2s\", "
"\"absl_optional_value\": \"10s\"}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->value, Duration::Seconds(3));
EXPECT_EQ(test_struct->optional_value, Duration::Milliseconds(3200));
EXPECT_EQ(test_struct->absl_optional_value, Duration::Seconds(10));
// Wrong JSON type.
test_struct = Parse<TestStruct>(
"{\"value\": [], \"optional_value\": {}, "
"\"absl_optional_value\": 1}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value error:is not a string; "
"field:optional_value error:is not a string; "
"field:value error:is not a string]")
<< test_struct.status();
}
//
// Json::Object tests
//
TEST(JsonObjectLoader, JsonObjectFields) {
struct TestStruct {
Json::Object value;
Json::Object optional_value;
absl::optional<Json::Object> absl_optional_value;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("value", &TestStruct::value)
.OptionalField("optional_value", &TestStruct::optional_value)
.OptionalField("absl_optional_value",
&TestStruct::absl_optional_value)
.Finish();
return loader;
}
};
// Valid object.
auto test_struct = Parse<TestStruct>("{\"value\": {\"a\":1}}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(Json{test_struct->value}.Dump(), "{\"a\":1}");
EXPECT_EQ(Json{test_struct->optional_value}.Dump(), "{}");
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Fails if required field is not present.
test_struct = Parse<TestStruct>("{}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:value error:field not present]")
<< test_struct.status();
// Optional fields present.
test_struct = Parse<TestStruct>(
"{\"value\": {\"a\":1}, \"optional_value\": {\"b\":2}, "
"\"absl_optional_value\": {\"c\":3}}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(Json{test_struct->value}.Dump(), "{\"a\":1}");
EXPECT_EQ(Json{test_struct->optional_value}.Dump(), "{\"b\":2}");
ASSERT_TRUE(test_struct->absl_optional_value.has_value());
EXPECT_EQ(Json{*test_struct->absl_optional_value}.Dump(), "{\"c\":3}");
// Wrong JSON type.
test_struct = Parse<TestStruct>(
"{\"value\": [], \"optional_value\": true, "
"\"absl_optional_value\": 1}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value error:is not an object; "
"field:optional_value error:is not an object; "
"field:value error:is not an object]")
<< test_struct.status();
}
//
// map<> tests
//
TEST(JsonObjectLoader, MapFields) {
struct TestStruct {
std::map<std::string, int32_t> value;
std::map<std::string, std::string> optional_value;
absl::optional<std::map<std::string, bool>> absl_optional_value;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("value", &TestStruct::value)
.OptionalField("optional_value", &TestStruct::optional_value)
.OptionalField("absl_optional_value",
&TestStruct::absl_optional_value)
.Finish();
return loader;
}
};
// Valid map.
auto test_struct = Parse<TestStruct>("{\"value\": {\"a\":1}}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_THAT(test_struct->value,
::testing::ElementsAre(::testing::Pair("a", 1)));
EXPECT_THAT(test_struct->optional_value, ::testing::ElementsAre());
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Fails if required field is not present.
test_struct = Parse<TestStruct>("{}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:value error:field not present]")
<< test_struct.status();
// Optional fields present.
test_struct = Parse<TestStruct>(
"{\"value\": {\"a\":1}, \"optional_value\": {\"b\":\"foo\"}, "
"\"absl_optional_value\": {\"c\":true}}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_THAT(test_struct->value,
::testing::ElementsAre(::testing::Pair("a", 1)));
EXPECT_THAT(test_struct->optional_value,
::testing::ElementsAre(::testing::Pair("b", "foo")));
ASSERT_TRUE(test_struct->absl_optional_value.has_value());
EXPECT_THAT(*test_struct->absl_optional_value,
::testing::ElementsAre(::testing::Pair("c", true)));
// Wrong JSON type.
test_struct = Parse<TestStruct>(
"{\"value\": [], \"optional_value\": true, "
"\"absl_optional_value\": 1}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value error:is not an object; "
"field:optional_value error:is not an object; "
"field:value error:is not an object]")
<< test_struct.status();
// Wrong JSON type for map value.
test_struct = Parse<TestStruct>(
"{\"value\": {\"a\":\"foo\"}, \"optional_value\": {\"b\":true}, "
"\"absl_optional_value\": {\"c\":1}}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value[\"c\"] error:is not a boolean; "
"field:optional_value[\"b\"] error:is not a string; "
"field:value[\"a\"] error:failed to parse number]")
<< test_struct.status();
}
//
// vector<> tests
//
TEST(JsonObjectLoader, VectorFields) {
struct TestStruct {
std::vector<int32_t> value;
std::vector<std::string> optional_value;
absl::optional<std::vector<bool>> absl_optional_value;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("value", &TestStruct::value)
.OptionalField("optional_value", &TestStruct::optional_value)
.OptionalField("absl_optional_value",
&TestStruct::absl_optional_value)
.Finish();
return loader;
}
};
// Valid map.
auto test_struct = Parse<TestStruct>("{\"value\": [1, 2, 3]}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_THAT(test_struct->value, ::testing::ElementsAre(1, 2, 3));
EXPECT_THAT(test_struct->optional_value, ::testing::ElementsAre());
EXPECT_FALSE(test_struct->absl_optional_value.has_value());
// Fails if required field is not present.
test_struct = Parse<TestStruct>("{}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:value error:field not present]")
<< test_struct.status();
// Optional fields present.
test_struct = Parse<TestStruct>(
"{\"value\": [4, 5, 6], \"optional_value\": [\"foo\", \"bar\"], "
"\"absl_optional_value\": [true, false, true]}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_THAT(test_struct->value, ::testing::ElementsAre(4, 5, 6));
EXPECT_THAT(test_struct->optional_value,
::testing::ElementsAre("foo", "bar"));
ASSERT_TRUE(test_struct->absl_optional_value.has_value());
EXPECT_THAT(*test_struct->absl_optional_value,
::testing::ElementsAre(true, false, true));
// Wrong JSON type.
test_struct = Parse<TestStruct>(
"{\"value\": {}, \"optional_value\": true, "
"\"absl_optional_value\": 1}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value error:is not an array; "
"field:optional_value error:is not an array; "
"field:value error:is not an array]")
<< test_struct.status();
// Wrong JSON type for map value.
test_struct = Parse<TestStruct>(
"{\"value\": [\"foo\", \"bar\"], \"optional_value\": [true, false], "
"\"absl_optional_value\": [1, 2]}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_value[0] error:is not a boolean; "
"field:absl_optional_value[1] error:is not a boolean; "
"field:optional_value[0] error:is not a string; "
"field:optional_value[1] error:is not a string; "
"field:value[0] error:failed to parse number; "
"field:value[1] error:failed to parse number]")
<< test_struct.status();
}
//
// Nested struct tests
//
TEST(JsonObjectLoader, NestedStructFields) {
struct NestedStruct {
int32_t inner = 0;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader = JsonObjectLoader<NestedStruct>()
.Field("inner", &NestedStruct::inner)
.Finish();
return loader;
}
};
struct TestStruct {
NestedStruct outer;
NestedStruct optional_outer;
absl::optional<NestedStruct> absl_optional_outer;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("outer", &TestStruct::outer)
.OptionalField("optional_outer", &TestStruct::optional_outer)
.OptionalField("absl_optional_outer",
&TestStruct::absl_optional_outer)
.Finish();
return loader;
}
};
// Valid nested struct.
auto test_struct = Parse<TestStruct>("{\"outer\": {\"inner\": 1}}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->outer.inner, 1);
EXPECT_EQ(test_struct->optional_outer.inner, 0);
EXPECT_FALSE(test_struct->absl_optional_outer.has_value());
// Fails if required field is not present.
test_struct = Parse<TestStruct>("{}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:outer error:field not present]")
<< test_struct.status();
// Fails if inner required field is not present.
test_struct = Parse<TestStruct>("{\"outer\": {}}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(
test_struct.status().message(),
"errors validating JSON: [field:outer.inner error:field not present]")
<< test_struct.status();
// Optional fields present.
test_struct = Parse<TestStruct>(
"{\"outer\": {\"inner\":1}, \"optional_outer\": {\"inner\":2}, "
"\"absl_optional_outer\": {\"inner\":3}}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->outer.inner, 1);
EXPECT_EQ(test_struct->optional_outer.inner, 2);
ASSERT_TRUE(test_struct->absl_optional_outer.has_value());
EXPECT_EQ(test_struct->absl_optional_outer->inner, 3);
// Wrong JSON type.
test_struct = Parse<TestStruct>(
"{\"outer\": \"foo\", \"optional_outer\": true, "
"\"absl_optional_outer\": 1}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_outer error:is not an object; "
"field:optional_outer error:is not an object; "
"field:outer error:is not an object]")
<< test_struct.status();
// Wrong JSON type for inner value.
test_struct = Parse<TestStruct>(
"{\"outer\": {\"inner\":\"foo\"}, \"optional_outer\": {\"inner\":true}, "
"\"absl_optional_outer\": {\"inner\":[]}}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:absl_optional_outer.inner error:is not a number; "
"field:optional_outer.inner error:is not a number; "
"field:outer.inner error:failed to parse number]")
<< test_struct.status();
}
TEST(JsonObjectLoader, BareString) {
auto parsed = Parse<std::string>("\"foo\"");
ASSERT_TRUE(parsed.ok()) << parsed.status();
EXPECT_EQ(*parsed, "foo");
}
TEST(JsonObjectLoader, BareDuration) {
auto parsed = Parse<Duration>("\"1.5s\"");
ASSERT_TRUE(parsed.ok()) << parsed.status();
EXPECT_EQ(*parsed, Duration::Milliseconds(1500));
}
TEST(JsonObjectLoader, BareSignedInteger) {
auto parsed = Parse<int32_t>("5");
ASSERT_TRUE(parsed.ok()) << parsed.status();
EXPECT_EQ(*parsed, 5);
}
TEST(JsonObjectLoader, BareUnsignedInteger) {
auto parsed = Parse<uint32_t>("5");
ASSERT_TRUE(parsed.ok()) << parsed.status();
EXPECT_EQ(*parsed, 5);
}
TEST(JsonObjectLoader, BareFloat) {
auto parsed = Parse<float>("5.2");
ASSERT_TRUE(parsed.ok()) << parsed.status();
EXPECT_NEAR(*parsed, 5.2, 0.001);
}
TEST(JsonObjectLoader, BareBool) {
auto parsed = Parse<bool>("true");
ASSERT_TRUE(parsed.ok()) << parsed.status();
EXPECT_TRUE(*parsed);
}
TEST(JsonObjectLoader, BareVector) {
auto parsed = Parse<std::vector<int32_t>>("[1, 2, 3]");
ASSERT_TRUE(parsed.ok()) << parsed.status();
EXPECT_THAT(*parsed, ::testing::ElementsAre(1, 2, 3));
}
TEST(JsonObjectLoader, BareMap) {
auto parsed =
Parse<std::map<std::string, int32_t>>("{\"a\":1, \"b\":2, \"c\":3}");
ASSERT_TRUE(parsed.ok()) << parsed.status();
EXPECT_THAT(*parsed, ::testing::ElementsAre(::testing::Pair("a", 1),
::testing::Pair("b", 2),
::testing::Pair("c", 3)));
}
TEST(JsonObjectLoader, IgnoresUnsupportedFields) {
struct TestStruct {
int32_t a = 0;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>().Field("a", &TestStruct::a).Finish();
return loader;
}
};
auto test_struct = Parse<TestStruct>("{\"a\": 3, \"b\":false}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->a, 3);
}
TEST(JsonObjectLoader, IgnoresDisabledFields) {
class FakeJsonArgs : public JsonArgs {
public:
FakeJsonArgs() = default;
bool IsEnabled(absl::string_view key) const override {
return key != "disabled";
}
};
struct TestStruct {
int32_t a = 0;
int32_t b = 0;
int32_t c = 0;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>()
.Field("a", &TestStruct::a, "disabled")
.OptionalField("b", &TestStruct::b, "disabled")
.OptionalField("c", &TestStruct::c, "enabled")
.Finish();
return loader;
}
};
// Fields "a" and "b" have the wrong types, but we ignore them,
// because they're disabled.
auto test_struct =
Parse<TestStruct>("{\"a\":false, \"b\":false, \"c\":1}", FakeJsonArgs());
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->a, 0);
EXPECT_EQ(test_struct->b, 0);
EXPECT_EQ(test_struct->c, 1);
}
TEST(JsonObjectLoader, PostLoadHook) {
struct TestStruct {
int32_t a = 0;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader = JsonObjectLoader<TestStruct>()
.OptionalField("a", &TestStruct::a)
.Finish();
return loader;
}
void JsonPostLoad(const Json& /*source*/, const JsonArgs& /*args*/,
ErrorList* /*errors*/) {
++a;
}
};
auto test_struct = Parse<TestStruct>("{\"a\": 1}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->a, 2);
test_struct = Parse<TestStruct>("{}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->a, 1);
}
TEST(JsonObjectLoader, CustomValidationInPostLoadHook) {
struct TestStruct {
int32_t a = 0;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>().Field("a", &TestStruct::a).Finish();
return loader;
}
void JsonPostLoad(const Json& /*source*/, const JsonArgs& /*args*/,
ErrorList* errors) {
ScopedField field(errors, ".a");
if (!errors->FieldHasErrors() && a <= 0) {
errors->AddError("must be greater than 0");
}
}
};
// Value greater than 0.
auto test_struct = Parse<TestStruct>("{\"a\": 1}");
ASSERT_TRUE(test_struct.ok()) << test_struct.status();
EXPECT_EQ(test_struct->a, 1);
// Value 0, triggers custom validation.
test_struct = Parse<TestStruct>("{\"a\": 0}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:a error:must be greater than 0]")
<< test_struct.status();
// Invalid type, generates built-in parsing error, so custom
// validation will not generate a new error.
test_struct = Parse<TestStruct>("{\"a\": []}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: [field:a error:is not a number]")
<< test_struct.status();
}
TEST(JsonObjectLoader, LoadFromJsonWithErrorList) {
struct TestStruct {
int32_t a = 0;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<TestStruct>().Field("a", &TestStruct::a).Finish();
return loader;
}
};
// Valid.
{
absl::string_view json_str = "{\"a\":1}";
auto json = Json::Parse(json_str);
ASSERT_TRUE(json.ok()) << json.status();
ErrorList errors;
TestStruct test_struct =
LoadFromJson<TestStruct>(*json, JsonArgs(), &errors);
ASSERT_TRUE(errors.ok()) << errors.status();
EXPECT_EQ(test_struct.a, 1);
}
// Invalid.
{
absl::string_view json_str = "{\"a\":\"foo\"}";
auto json = Json::Parse(json_str);
ASSERT_TRUE(json.ok()) << json.status();
ErrorList errors;
LoadFromJson<TestStruct>(*json, JsonArgs(), &errors);
absl::Status status = errors.status();
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(status.message(),
"errors validating JSON: [field:a error:failed to parse number]")
<< status;
}
}
TEST(JsonObjectLoader, LoadJsonObjectField) {
absl::string_view json_str = "{\"int\":1}";
auto json = Json::Parse(json_str);
ASSERT_TRUE(json.ok()) << json.status();
// Load a valid field.
{
ErrorList errors;
auto value = LoadJsonObjectField<int32_t>(json->object_value(), JsonArgs(),
"int", &errors);
ASSERT_TRUE(value.has_value()) << errors.status();
EXPECT_EQ(*value, 1);
EXPECT_TRUE(errors.ok());
}
// An optional field that is not present.
{
ErrorList errors;
auto value = LoadJsonObjectField<int32_t>(json->object_value(), JsonArgs(),
"not_present", &errors,
/*required=*/false);
EXPECT_FALSE(value.has_value());
EXPECT_TRUE(errors.ok());
}
// A required field that is not present.
{
ErrorList errors;
auto value = LoadJsonObjectField<int32_t>(json->object_value(), JsonArgs(),
"not_present", &errors);
EXPECT_FALSE(value.has_value());
auto status = errors.status();
EXPECT_THAT(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(status.message(),
"errors validating JSON: ["
"field:not_present error:field not present]")
<< status;
}
// Value has the wrong type.
{
ErrorList errors;
auto value = LoadJsonObjectField<std::string>(json->object_value(),
JsonArgs(), "int", &errors);
EXPECT_FALSE(value.has_value());
auto status = errors.status();
EXPECT_THAT(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(status.message(),
"errors validating JSON: [field:int error:is not a string]")
<< status;
}
}
} // namespace
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}