xref: /aosp_15_r20/external/grpc-grpc/test/core/service_config/service_config_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 // Copyright 2019 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "src/core/service_config/service_config.h"
18 
19 #include <stdint.h>
20 
21 #include <memory>
22 #include <string>
23 
24 #include "absl/status/status.h"
25 #include "absl/status/statusor.h"
26 #include "absl/strings/str_cat.h"
27 #include "absl/types/optional.h"
28 #include "gmock/gmock.h"
29 #include "gtest/gtest.h"
30 
31 #include <grpc/grpc.h>
32 
33 #include "src/core/lib/channel/channel_args.h"
34 #include "src/core/lib/config/core_configuration.h"
35 #include "src/core/lib/gprpp/ref_counted_ptr.h"
36 #include "src/core/lib/gprpp/validation_errors.h"
37 #include "src/core/lib/json/json.h"
38 #include "src/core/lib/json/json_args.h"
39 #include "src/core/lib/json/json_object_loader.h"
40 #include "src/core/service_config/service_config_impl.h"
41 #include "src/core/service_config/service_config_parser.h"
42 #include "test/core/util/test_config.h"
43 
44 namespace grpc_core {
45 namespace testing {
46 
47 // Set this channel arg to true to disable parsing.
48 #define GRPC_ARG_DISABLE_PARSING "disable_parsing"
49 
50 class TestParsedConfig1 : public ServiceConfigParser::ParsedConfig {
51  public:
value() const52   uint32_t value() const { return value_; }
53 
JsonLoader(const JsonArgs &)54   static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
55     static const auto* loader =
56         JsonObjectLoader<TestParsedConfig1>()
57             .OptionalField("global_param", &TestParsedConfig1::value_)
58             .Finish();
59     return loader;
60   }
61 
62  private:
63   uint32_t value_;
64 };
65 
66 class TestParser1 : public ServiceConfigParser::Parser {
67  public:
name() const68   absl::string_view name() const override { return "test_parser_1"; }
69 
ParseGlobalParams(const ChannelArgs & args,const Json & json,ValidationErrors * errors)70   std::unique_ptr<ServiceConfigParser::ParsedConfig> ParseGlobalParams(
71       const ChannelArgs& args, const Json& json,
72       ValidationErrors* errors) override {
73     if (args.GetBool(GRPC_ARG_DISABLE_PARSING).value_or(false)) {
74       return nullptr;
75     }
76     return LoadFromJson<std::unique_ptr<TestParsedConfig1>>(json, JsonArgs(),
77                                                             errors);
78   }
79 };
80 
81 class TestParsedConfig2 : public ServiceConfigParser::ParsedConfig {
82  public:
value() const83   uint32_t value() const { return value_; }
84 
JsonLoader(const JsonArgs &)85   static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
86     static const auto* loader =
87         JsonObjectLoader<TestParsedConfig2>()
88             .OptionalField("method_param", &TestParsedConfig2::value_)
89             .Finish();
90     return loader;
91   }
92 
93  private:
94   uint32_t value_;
95 };
96 
97 class TestParser2 : public ServiceConfigParser::Parser {
98  public:
name() const99   absl::string_view name() const override { return "test_parser_2"; }
100 
ParsePerMethodParams(const ChannelArgs & args,const Json & json,ValidationErrors * errors)101   std::unique_ptr<ServiceConfigParser::ParsedConfig> ParsePerMethodParams(
102       const ChannelArgs& args, const Json& json,
103       ValidationErrors* errors) override {
104     if (args.GetBool(GRPC_ARG_DISABLE_PARSING).value_or(false)) {
105       return nullptr;
106     }
107     return LoadFromJson<std::unique_ptr<TestParsedConfig2>>(json, JsonArgs(),
108                                                             errors);
109   }
110 };
111 
112 class ServiceConfigTest : public ::testing::Test {
113  protected:
SetUp()114   void SetUp() override {
115     builder_ = std::make_unique<CoreConfiguration::WithSubstituteBuilder>(
116         [](CoreConfiguration::Builder* builder) {
117           builder->service_config_parser()->RegisterParser(
118               std::make_unique<TestParser1>());
119           builder->service_config_parser()->RegisterParser(
120               std::make_unique<TestParser2>());
121         });
122     EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex(
123                   "test_parser_1"),
124               0);
125     EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex(
126                   "test_parser_2"),
127               1);
128   }
129 
130  private:
131   std::unique_ptr<CoreConfiguration::WithSubstituteBuilder> builder_;
132 };
133 
TEST_F(ServiceConfigTest,JsonParseError)134 TEST_F(ServiceConfigTest, JsonParseError) {
135   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), "");
136   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
137   EXPECT_THAT(std::string(service_config.status().message()),
138               ::testing::StartsWith("JSON parsing failed"))
139       << service_config.status();
140 }
141 
TEST_F(ServiceConfigTest,EmptyConfig)142 TEST_F(ServiceConfigTest, EmptyConfig) {
143   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), "{}");
144   ASSERT_TRUE(service_config.ok()) << service_config.status();
145   EXPECT_EQ((*service_config)->json_string(), "{}");
146 }
147 
TEST_F(ServiceConfigTest,SkipMethodConfigWithNoNameOrEmptyName)148 TEST_F(ServiceConfigTest, SkipMethodConfigWithNoNameOrEmptyName) {
149   const char* test_json =
150       "{\"methodConfig\": ["
151       "  {\"method_param\":1},"
152       "  {\"name\":[], \"method_param\":1},"
153       "  {\"name\":[{\"service\":\"TestServ\"}], \"method_param\":2}"
154       "]}";
155   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
156   ASSERT_TRUE(service_config.ok()) << service_config.status();
157   auto vector_ptr =
158       (*service_config)
159           ->GetMethodParsedConfigVector(
160               grpc_slice_from_static_string("/TestServ/TestMethod"));
161   ASSERT_EQ(vector_ptr->size(), 2UL);
162   auto parsed_config = ((*vector_ptr)[1]).get();
163   EXPECT_EQ(static_cast<TestParsedConfig1*>(parsed_config)->value(), 2);
164 }
165 
TEST_F(ServiceConfigTest,ErrorDuplicateMethodConfigNames)166 TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNames) {
167   const char* test_json =
168       "{\"methodConfig\": ["
169       "  {\"name\":[{\"service\":\"TestServ\"}]},"
170       "  {\"name\":[{\"service\":\"TestServ\"}]}"
171       "]}";
172   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
173   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
174   EXPECT_EQ(service_config.status().message(),
175             "errors validating service config: ["
176             "field:methodConfig[1].name[0] "
177             "error:multiple method configs for path /TestServ/]")
178       << service_config.status();
179 }
180 
TEST_F(ServiceConfigTest,ErrorDuplicateMethodConfigNamesWithNullMethod)181 TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNamesWithNullMethod) {
182   const char* test_json =
183       "{\"methodConfig\": ["
184       "  {\"name\":[{\"service\":\"TestServ\",\"method\":null}]},"
185       "  {\"name\":[{\"service\":\"TestServ\"}]}"
186       "]}";
187   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
188   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
189   EXPECT_EQ(service_config.status().message(),
190             "errors validating service config: ["
191             "field:methodConfig[1].name[0] "
192             "error:multiple method configs for path /TestServ/]")
193       << service_config.status();
194 }
195 
TEST_F(ServiceConfigTest,ErrorDuplicateMethodConfigNamesWithEmptyMethod)196 TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNamesWithEmptyMethod) {
197   const char* test_json =
198       "{\"methodConfig\": ["
199       "  {\"name\":[{\"service\":\"TestServ\",\"method\":\"\"}]},"
200       "  {\"name\":[{\"service\":\"TestServ\"}]}"
201       "]}";
202   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
203   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
204   EXPECT_EQ(service_config.status().message(),
205             "errors validating service config: ["
206             "field:methodConfig[1].name[0] "
207             "error:multiple method configs for path /TestServ/]")
208       << service_config.status();
209 }
210 
TEST_F(ServiceConfigTest,ErrorDuplicateDefaultMethodConfigs)211 TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigs) {
212   const char* test_json =
213       "{\"methodConfig\": ["
214       "  {\"name\":[{}]},"
215       "  {\"name\":[{}]}"
216       "]}";
217   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
218   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
219   EXPECT_EQ(service_config.status().message(),
220             "errors validating service config: ["
221             "field:methodConfig[1].name[0] "
222             "error:duplicate default method config]")
223       << service_config.status();
224 }
225 
TEST_F(ServiceConfigTest,ErrorDuplicateDefaultMethodConfigsWithNullService)226 TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigsWithNullService) {
227   const char* test_json =
228       "{\"methodConfig\": ["
229       "  {\"name\":[{\"service\":null}]},"
230       "  {\"name\":[{}]}"
231       "]}";
232   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
233   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
234   EXPECT_EQ(service_config.status().message(),
235             "errors validating service config: ["
236             "field:methodConfig[1].name[0] "
237             "error:duplicate default method config]")
238       << service_config.status();
239 }
240 
TEST_F(ServiceConfigTest,ErrorDuplicateDefaultMethodConfigsWithEmptyService)241 TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigsWithEmptyService) {
242   const char* test_json =
243       "{\"methodConfig\": ["
244       "  {\"name\":[{\"service\":\"\"}]},"
245       "  {\"name\":[{}]}"
246       "]}";
247   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
248   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
249   EXPECT_EQ(service_config.status().message(),
250             "errors validating service config: ["
251             "field:methodConfig[1].name[0] "
252             "error:duplicate default method config]")
253       << service_config.status();
254 }
255 
TEST_F(ServiceConfigTest,ValidMethodConfig)256 TEST_F(ServiceConfigTest, ValidMethodConfig) {
257   const char* test_json =
258       "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}]}]}";
259   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
260   ASSERT_TRUE(service_config.ok()) << service_config.status();
261 }
262 
TEST_F(ServiceConfigTest,Parser1BasicTest1)263 TEST_F(ServiceConfigTest, Parser1BasicTest1) {
264   const char* test_json = "{\"global_param\":5}";
265   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
266   ASSERT_TRUE(service_config.ok()) << service_config.status();
267   EXPECT_EQ((static_cast<TestParsedConfig1*>(
268                  (*service_config)->GetGlobalParsedConfig(0)))
269                 ->value(),
270             5);
271   EXPECT_EQ((*service_config)
272                 ->GetMethodParsedConfigVector(
273                     grpc_slice_from_static_string("/TestServ/TestMethod")),
274             nullptr);
275 }
276 
TEST_F(ServiceConfigTest,Parser1BasicTest2)277 TEST_F(ServiceConfigTest, Parser1BasicTest2) {
278   const char* test_json = "{\"global_param\":1000}";
279   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
280   ASSERT_TRUE(service_config.ok()) << service_config.status();
281   EXPECT_EQ((static_cast<TestParsedConfig1*>(
282                  (*service_config)->GetGlobalParsedConfig(0)))
283                 ->value(),
284             1000);
285 }
286 
TEST_F(ServiceConfigTest,Parser1DisabledViaChannelArg)287 TEST_F(ServiceConfigTest, Parser1DisabledViaChannelArg) {
288   const ChannelArgs args = ChannelArgs().Set(GRPC_ARG_DISABLE_PARSING, 1);
289   const char* test_json = "{\"global_param\":5}";
290   auto service_config = ServiceConfigImpl::Create(args, test_json);
291   ASSERT_TRUE(service_config.ok()) << service_config.status();
292   EXPECT_EQ((*service_config)->GetGlobalParsedConfig(0), nullptr);
293 }
294 
TEST_F(ServiceConfigTest,Parser1ErrorInvalidType)295 TEST_F(ServiceConfigTest, Parser1ErrorInvalidType) {
296   const char* test_json = "{\"global_param\":[]}";
297   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
298   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
299   EXPECT_EQ(service_config.status().message(),
300             "errors validating service config: ["
301             "field:global_param error:is not a number]")
302       << service_config.status();
303 }
304 
TEST_F(ServiceConfigTest,Parser1ErrorInvalidValue)305 TEST_F(ServiceConfigTest, Parser1ErrorInvalidValue) {
306   const char* test_json = "{\"global_param\":-5}";
307   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
308   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
309   EXPECT_EQ(service_config.status().message(),
310             "errors validating service config: ["
311             "field:global_param error:failed to parse non-negative number]")
312       << service_config.status();
313 }
314 
TEST_F(ServiceConfigTest,Parser2BasicTest)315 TEST_F(ServiceConfigTest, Parser2BasicTest) {
316   const char* test_json =
317       "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
318       "\"method_param\":5}]}";
319   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
320   ASSERT_TRUE(service_config.ok()) << service_config.status();
321   const auto* vector_ptr =
322       (*service_config)
323           ->GetMethodParsedConfigVector(
324               grpc_slice_from_static_string("/TestServ/TestMethod"));
325   ASSERT_NE(vector_ptr, nullptr);
326   auto parsed_config = ((*vector_ptr)[1]).get();
327   EXPECT_EQ(static_cast<TestParsedConfig1*>(parsed_config)->value(), 5);
328 }
329 
TEST_F(ServiceConfigTest,Parser2DisabledViaChannelArg)330 TEST_F(ServiceConfigTest, Parser2DisabledViaChannelArg) {
331   const ChannelArgs args = ChannelArgs().Set(GRPC_ARG_DISABLE_PARSING, 1);
332   const char* test_json =
333       "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
334       "\"method_param\":5}]}";
335   auto service_config = ServiceConfigImpl::Create(args, test_json);
336   ASSERT_TRUE(service_config.ok()) << service_config.status();
337   const auto* vector_ptr =
338       (*service_config)
339           ->GetMethodParsedConfigVector(
340               grpc_slice_from_static_string("/TestServ/TestMethod"));
341   ASSERT_NE(vector_ptr, nullptr);
342   auto parsed_config = ((*vector_ptr)[1]).get();
343   EXPECT_EQ(parsed_config, nullptr);
344 }
345 
TEST_F(ServiceConfigTest,Parser2ErrorInvalidType)346 TEST_F(ServiceConfigTest, Parser2ErrorInvalidType) {
347   const char* test_json =
348       "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
349       "\"method_param\":[]}]}";
350   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
351   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
352   EXPECT_EQ(service_config.status().message(),
353             "errors validating service config: ["
354             "field:methodConfig[0].method_param error:is not a number]")
355       << service_config.status();
356 }
357 
TEST_F(ServiceConfigTest,Parser2ErrorInvalidValue)358 TEST_F(ServiceConfigTest, Parser2ErrorInvalidValue) {
359   const char* test_json =
360       "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
361       "\"method_param\":-5}]}";
362   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
363   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
364   EXPECT_EQ(service_config.status().message(),
365             "errors validating service config: ["
366             "field:methodConfig[0].method_param "
367             "error:failed to parse non-negative number]")
368       << service_config.status();
369 }
370 
TEST(ServiceConfigParserDeathTest,DoubleRegistration)371 TEST(ServiceConfigParserDeathTest, DoubleRegistration) {
372   GTEST_FLAG_SET(death_test_style, "threadsafe");
373   CoreConfiguration::Reset();
374   ASSERT_DEATH_IF_SUPPORTED(
375       CoreConfiguration::WithSubstituteBuilder builder(
376           [](CoreConfiguration::Builder* builder) {
377             builder->service_config_parser()->RegisterParser(
378                 std::make_unique<TestParser1>());
379             builder->service_config_parser()->RegisterParser(
380                 std::make_unique<TestParser1>());
381           }),
382       "test_parser_1.*already registered");
383 }
384 
385 // This parser always adds errors
386 class ErrorParser : public ServiceConfigParser::Parser {
387  public:
ErrorParser(absl::string_view name)388   explicit ErrorParser(absl::string_view name) : name_(name) {}
389 
name() const390   absl::string_view name() const override { return name_; }
391 
ParsePerMethodParams(const ChannelArgs &,const Json &,ValidationErrors * errors)392   std::unique_ptr<ServiceConfigParser::ParsedConfig> ParsePerMethodParams(
393       const ChannelArgs& /*arg*/, const Json& /*json*/,
394       ValidationErrors* errors) override {
395     ValidationErrors::ScopedField field(errors, absl::StrCat(".", name_));
396     errors->AddError("method error");
397     return nullptr;
398   }
399 
ParseGlobalParams(const ChannelArgs &,const Json &,ValidationErrors * errors)400   std::unique_ptr<ServiceConfigParser::ParsedConfig> ParseGlobalParams(
401       const ChannelArgs& /*arg*/, const Json& /*json*/,
402       ValidationErrors* errors) override {
403     ValidationErrors::ScopedField field(errors, absl::StrCat(".", name_));
404     errors->AddError("global error");
405     return nullptr;
406   }
407 
408  private:
409   absl::string_view name_;
410 };
411 
412 // Test parsing with ErrorParsers which always add errors
413 class ErroredParsersScopingTest : public ::testing::Test {
414  protected:
SetUp()415   void SetUp() override {
416     builder_ = std::make_unique<CoreConfiguration::WithSubstituteBuilder>(
417         [](CoreConfiguration::Builder* builder) {
418           builder->service_config_parser()->RegisterParser(
419               std::make_unique<ErrorParser>("ep1"));
420           builder->service_config_parser()->RegisterParser(
421               std::make_unique<ErrorParser>("ep2"));
422         });
423     EXPECT_EQ(
424         CoreConfiguration::Get().service_config_parser().GetParserIndex("ep1"),
425         0);
426     EXPECT_EQ(
427         CoreConfiguration::Get().service_config_parser().GetParserIndex("ep2"),
428         1);
429   }
430 
431  private:
432   std::unique_ptr<CoreConfiguration::WithSubstituteBuilder> builder_;
433 };
434 
TEST_F(ErroredParsersScopingTest,GlobalParams)435 TEST_F(ErroredParsersScopingTest, GlobalParams) {
436   const char* test_json = "{}";
437   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
438   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
439   EXPECT_EQ(service_config.status().message(),
440             "errors validating service config: ["
441             "field:ep1 error:global error; field:ep2 error:global error]")
442       << service_config.status();
443 }
444 
TEST_F(ErroredParsersScopingTest,MethodParams)445 TEST_F(ErroredParsersScopingTest, MethodParams) {
446   const char* test_json = "{\"methodConfig\": [{}]}";
447   auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
448   EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
449   EXPECT_EQ(service_config.status().message(),
450             "errors validating service config: ["
451             "field:ep1 error:global error; "
452             "field:ep2 error:global error; "
453             "field:methodConfig[0].ep1 error:method error; "
454             "field:methodConfig[0].ep2 error:method error]")
455       << service_config.status();
456 }
457 
458 }  // namespace testing
459 }  // namespace grpc_core
460 
main(int argc,char ** argv)461 int main(int argc, char** argv) {
462   ::testing::InitGoogleTest(&argc, argv);
463   grpc::testing::TestEnvironment env(&argc, argv);
464   grpc_init();
465   int ret = RUN_ALL_TESTS();
466   grpc_shutdown();
467   return ret;
468 }
469