@@ -130,15 +130,19 @@ class XdsClientTest : public ::testing::Test {
130130 public:
131131 explicit FakeXdsServer(
132132 absl::string_view server_uri = kDefaultXdsServerUrl,
133- bool ignore_resource_deletion = false)
133+ bool ignore_resource_deletion = false,
134+ bool resource_timer_is_transient_failure = false)
134135 : server_uri_(server_uri),
135- ignore_resource_deletion_(ignore_resource_deletion) {}
136+ ignore_resource_deletion_(ignore_resource_deletion),
137+ resource_timer_is_transient_failure_(
138+ resource_timer_is_transient_failure) {}
139+
136140 const std::string& server_uri() const override { return server_uri_; }
137141 bool IgnoreResourceDeletion() const override {
138142 return ignore_resource_deletion_;
139143 }
140144 bool ResourceTimerIsTransientFailure() const override {
141- return false; // FIXME
145+ return resource_timer_is_transient_failure_;
142146 }
143147 bool Equals(const XdsServer& other) const override {
144148 const auto& o = static_cast<const FakeXdsServer&>(other);
@@ -152,6 +156,7 @@ class XdsClientTest : public ::testing::Test {
152156 private:
153157 std::string server_uri_;
154158 bool ignore_resource_deletion_ = false;
159+ bool resource_timer_is_transient_failure_ = false;
155160 };
156161
157162 class FakeAuthority : public Authority {
@@ -1527,7 +1532,7 @@ TEST_F(XdsClientTest, ResourceValidationFailure) {
15271532 ASSERT_TRUE(error.has_value());
15281533 EXPECT_EQ(error->code(), absl::StatusCode::kInvalidArgument);
15291534 EXPECT_EQ(error->message(),
1530- "invalid resource: INVALID_ARGUMENT: errors validating JSON: "
1535+ "invalid resource: errors validating JSON: "
15311536 "[field:value error:is not a number] (node ID:xds_client_test)")
15321537 << *error;
15331538 // Check metric data.
@@ -1564,7 +1569,7 @@ TEST_F(XdsClientTest, ResourceValidationFailure) {
15641569 ASSERT_TRUE(error.has_value());
15651570 EXPECT_EQ(error->code(), absl::StatusCode::kInvalidArgument);
15661571 EXPECT_EQ(error->message(),
1567- "invalid resource: INVALID_ARGUMENT: errors validating JSON: "
1572+ "invalid resource: errors validating JSON: "
15681573 "[field:value error:is not a number] (node ID:xds_client_test)")
15691574 << *error;
15701575 // Now server sends an updated version of the resource.
@@ -1728,14 +1733,14 @@ TEST_F(XdsClientTest, ResourceValidationFailureMultipleResources) {
17281733 ASSERT_TRUE(error.has_value());
17291734 EXPECT_EQ(error->code(), absl::StatusCode::kInvalidArgument);
17301735 EXPECT_EQ(error->message(),
1731- "invalid resource: INVALID_ARGUMENT: errors validating JSON: "
1736+ "invalid resource: errors validating JSON: "
17321737 "[field:value error:is not a number] (node ID:xds_client_test)")
17331738 << *error;
17341739 error = watcher3->WaitForNextError();
17351740 ASSERT_TRUE(error.has_value());
17361741 EXPECT_EQ(error->code(), absl::StatusCode::kInvalidArgument);
17371742 EXPECT_EQ(error->message(),
1738- "invalid resource: INVALID_ARGUMENT: JSON parsing failed: "
1743+ "invalid resource: JSON parsing failed: "
17391744 "[JSON parse error at index 15] (node ID:xds_client_test)")
17401745 << *error;
17411746 // It cannot delivery an error for foo2, because the client doesn't know
@@ -1874,7 +1879,7 @@ TEST_F(XdsClientTest, ResourceValidationFailureForCachedResource) {
18741879 ASSERT_TRUE(error.has_value());
18751880 EXPECT_EQ(error->code(), absl::StatusCode::kInvalidArgument);
18761881 EXPECT_EQ(error->message(),
1877- "invalid resource: INVALID_ARGUMENT: errors validating JSON: "
1882+ "invalid resource: errors validating JSON: "
18781883 "[field:value error:is not a number] (node ID:xds_client_test)")
18791884 << *error;
18801885 // Check metric data.
@@ -2635,6 +2640,14 @@ TEST_F(XdsClientTest, ConnectionFailsWithCachedResource) {
26352640}
26362641
26372642TEST_F(XdsClientTest, ResourceDoesNotExistUponTimeout) {
2643+ event_engine_->SetRunAfterDurationCallback(
2644+ [&](grpc_event_engine::experimental::EventEngine::Duration duration) {
2645+ grpc_event_engine::experimental::EventEngine::Duration expected =
2646+ std::chrono::seconds(15);
2647+ EXPECT_EQ(duration, expected)
2648+ << "Expected: " << expected.count()
2649+ << "\nActual: " << duration.count();
2650+ });
26382651 InitXdsClient();
26392652 // Start a watch for "foo1".
26402653 auto watcher = StartFooWatch("foo1");
@@ -2720,6 +2733,111 @@ TEST_F(XdsClientTest, ResourceDoesNotExistUponTimeout) {
27202733 EXPECT_TRUE(stream->IsOrphaned());
27212734}
27222735
2736+ TEST_F(XdsClientTest, ResourceTimerIsTransientFailure) {
2737+ event_engine_->SetRunAfterDurationCallback(
2738+ [&](grpc_event_engine::experimental::EventEngine::Duration duration) {
2739+ grpc_event_engine::experimental::EventEngine::Duration expected =
2740+ std::chrono::seconds(30);
2741+ EXPECT_EQ(duration, expected)
2742+ << "Expected: " << expected.count()
2743+ << "\nActual: " << duration.count();
2744+ });
2745+ InitXdsClient(FakeXdsBootstrap::Builder().SetServers(
2746+ {FakeXdsBootstrap::FakeXdsServer(kDefaultXdsServerUrl, false, true)}));
2747+ // Start a watch for "foo1".
2748+ auto watcher = StartFooWatch("foo1");
2749+ // Watcher should initially not see any resource reported.
2750+ EXPECT_FALSE(watcher->HasEvent());
2751+ // Check metric data.
2752+ EXPECT_THAT(metrics_reporter_->resource_updates_valid(),
2753+ ::testing::ElementsAre());
2754+ EXPECT_THAT(metrics_reporter_->resource_updates_invalid(),
2755+ ::testing::ElementsAre());
2756+ EXPECT_THAT(GetResourceCounts(),
2757+ ::testing::ElementsAre(::testing::Pair(
2758+ ResourceCountLabelsEq(XdsClient::kOldStyleAuthority,
2759+ XdsFooResourceType::Get()->type_url(),
2760+ "requested"),
2761+ 1)));
2762+ // XdsClient should have created an ADS stream.
2763+ auto stream = WaitForAdsStream();
2764+ ASSERT_TRUE(stream != nullptr);
2765+ // XdsClient should have sent a subscription request on the ADS stream.
2766+ auto request = WaitForRequest(stream.get());
2767+ ASSERT_TRUE(request.has_value());
2768+ CheckRequest(*request, XdsFooResourceType::Get()->type_url(),
2769+ /*version_info=*/"", /*response_nonce=*/"",
2770+ /*error_detail=*/absl::OkStatus(),
2771+ /*resource_names=*/{"foo1"});
2772+ CheckRequestNode(*request); // Should be present on the first request.
2773+ // Do not send a response, but wait for the resource to be reported as
2774+ // not existing.
2775+ auto error = watcher->WaitForNextError();
2776+ ASSERT_TRUE(error.has_value());
2777+ EXPECT_EQ(error,
2778+ absl::UnavailableError(absl::StrCat(
2779+ "xDS server ", kDefaultXdsServerUrl,
2780+ " not responding (node ID:xds_client_test)")));
2781+ // Check metric data.
2782+ EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData(
2783+ ::testing::ElementsAre(), ::testing::ElementsAre(), ::testing::_));
2784+ EXPECT_THAT(GetResourceCounts(),
2785+ ::testing::ElementsAre(::testing::Pair(
2786+ ResourceCountLabelsEq(XdsClient::kOldStyleAuthority,
2787+ XdsFooResourceType::Get()->type_url(),
2788+ "requested"),
2789+ 1)));
2790+ // Start a new watcher for the same resource. It should immediately
2791+ // receive the same does-not-exist notification.
2792+ auto watcher2 = StartFooWatch("foo1");
2793+ error = watcher2->WaitForNextError();
2794+ ASSERT_TRUE(error.has_value());
2795+ EXPECT_EQ(error,
2796+ absl::UnavailableError(absl::StrCat(
2797+ "xDS server ", kDefaultXdsServerUrl,
2798+ " not responding (node ID:xds_client_test)")));
2799+ // Now server sends a response.
2800+ stream->SendMessageToClient(
2801+ ResponseBuilder(XdsFooResourceType::Get()->type_url())
2802+ .set_version_info("1")
2803+ .set_nonce("A")
2804+ .AddFooResource(XdsFooResource("foo1", 6))
2805+ .Serialize());
2806+ // XdsClient should have delivered the response to the watchers.
2807+ auto resource = watcher->WaitForNextResource();
2808+ ASSERT_NE(resource, nullptr);
2809+ EXPECT_EQ(resource->name, "foo1");
2810+ EXPECT_EQ(resource->value, 6);
2811+ resource = watcher2->WaitForNextResource();
2812+ ASSERT_NE(resource, nullptr);
2813+ EXPECT_EQ(resource->name, "foo1");
2814+ EXPECT_EQ(resource->value, 6);
2815+ // Check metric data.
2816+ EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData(
2817+ ::testing::ElementsAre(::testing::Pair(
2818+ ::testing::Pair(kDefaultXdsServerUrl,
2819+ XdsFooResourceType::Get()->type_url()),
2820+ 1)),
2821+ ::testing::ElementsAre(), ::testing::_));
2822+ EXPECT_THAT(
2823+ GetResourceCounts(),
2824+ ::testing::ElementsAre(::testing::Pair(
2825+ ResourceCountLabelsEq(XdsClient::kOldStyleAuthority,
2826+ XdsFooResourceType::Get()->type_url(), "acked"),
2827+ 1)));
2828+ // XdsClient should have sent an ACK message to the xDS server.
2829+ request = WaitForRequest(stream.get());
2830+ ASSERT_TRUE(request.has_value());
2831+ CheckRequest(*request, XdsFooResourceType::Get()->type_url(),
2832+ /*version_info=*/"1", /*response_nonce=*/"A",
2833+ /*error_detail=*/absl::OkStatus(),
2834+ /*resource_names=*/{"foo1"});
2835+ // Cancel watch.
2836+ CancelFooWatch(watcher.get(), "foo1");
2837+ CancelFooWatch(watcher2.get(), "foo1");
2838+ EXPECT_TRUE(stream->IsOrphaned());
2839+ }
2840+
27232841TEST_F(XdsClientTest, ResourceDoesNotExistAfterStreamRestart) {
27242842 InitXdsClient();
27252843 // Metrics should initially be empty.
0 commit comments