public void WatchIsLeftOpenIfNotRespondedImmediately()
        {
            var cache = new SimpleCache <string>(_ => "key");

            cache.SetSnapshot("key", new Snapshot(
                                  Enumerable.Empty <Cluster>(),
                                  Enumerable.Empty <ClusterLoadAssignment>(),
                                  Enumerable.Empty <Listener>(),
                                  Enumerable.Empty <RouteConfiguration>(),
                                  Enumerable.Empty <Secret>(),
                                  VERSION1));

            var responses        = new List <Response>();
            var discoveryRequest = new DiscoveryRequest
            {
                Node    = new Node(),
                TypeUrl = Resources.ROUTE_TYPE_URL
            };

            discoveryRequest.ResourceNames.Add(ROUTE_NAME);

            var watch = cache.CreateWatch(
                true,
                discoveryRequest,
                new[] { ROUTE_NAME }.ToHashSet(),
                responses.Add);

            AssertThatWatchIsOpenWithNoResponses(watch, responses);
        }
        public void ClearSnapshotWithWatches(bool ads)
        {
            var cache = new SimpleCache <string>(_ => "key");

            cache.SetSnapshot("key", SNAPSHOT1);
            var discoveryRequest = new DiscoveryRequest
            {
                Node    = new Node(),
                TypeUrl = ""
            };

            var watch = cache.CreateWatch(
                ads,
                discoveryRequest,
                EmptySet,
                _ => { });

            // clearSnapshot should fail and the snapshot should be left untouched
            cache.ClearSnapshot("key").Should().BeFalse();
            cache.GetSnapshot("key").Should().Be(SNAPSHOT1);
            cache.GetStatusInfo("key").Should().NotBeNull();

            watch.Cancel();

            // now that the watch is gone we should be able to clear it
            cache.ClearSnapshot("key").Should().BeTrue();
            cache.GetSnapshot("key").Should().BeNull();
            cache.GetStatusInfo("key").Should().BeNull();
        }
        public void SuccessfullyWatchAllResourceTypesWithSetBeforeWatch(bool ads)
        {
            var cache = new SimpleCache <string>(_ => "key");

            cache.SetSnapshot("key", SNAPSHOT1);

            foreach (var typeUrl in Resources.TYPE_URLS)
            {
                var responses        = new List <Response>();
                var discoveryRequest = new DiscoveryRequest
                {
                    Node    = new Node(),
                    TypeUrl = typeUrl
                };

                discoveryRequest.ResourceNames.AddRange(SNAPSHOT1.GetResources(typeUrl).Keys);

                var watch = cache.CreateWatch(
                    ads,
                    discoveryRequest,
                    EmptySet,
                    responses.Add);

                watch.Request.TypeUrl.Should().BeEquivalentTo(typeUrl);
                watch.Request.ResourceNames.Should().BeEquivalentTo(SNAPSHOT1.GetResources(typeUrl).Keys);

                AssertThatWatchReceivesSnapshot(watch, responses, SNAPSHOT1);
            }
        }
        public void SuccessfullyWatchAllResourceTypesWithSetBeforeWatchWithSameRequestVersionNewResourceHints(bool ads)
        {
            var cache = new SimpleCache <string>(_ => "key");

            cache.SetSnapshot("key", MULTIPLE_RESOURCES_SNAPSHOT2);

            // Set a watch for the current snapshot with the same version but with resource hints present
            // in the snapshot that the watch creator does not currently know about.
            //
            // Note how we're requesting the resources from MULTIPLE_RESOURCE_SNAPSHOT2 while claiming we
            // only know about the ones from SNAPSHOT2
            var watches = Resources.TYPE_URLS.ToDictionary(
                typeUrl => typeUrl,
                typeUrl =>
            {
                var responses        = new List <Response>();
                var discoveryRequest = new DiscoveryRequest
                {
                    Node        = new Node(),
                    TypeUrl     = typeUrl,
                    VersionInfo = MULTIPLE_RESOURCES_SNAPSHOT2.GetVersion(typeUrl)
                };

                discoveryRequest.ResourceNames.AddRange(MULTIPLE_RESOURCES_SNAPSHOT2.GetResources(typeUrl).Keys);

                var watch = cache.CreateWatch(
                    ads,
                    discoveryRequest,
                    SNAPSHOT2.GetResources(typeUrl).Keys.ToHashSet(),
                    responses.Add);

                return(new
                {
                    watch = watch,
                    responses = responses
                });
            });

            // The snapshot version matches for all resources, but for eds and cds there are new resources present
            // for the same version, so we expect the watches to trigger.
            AssertThatWatchReceivesSnapshot(
                watches[Resources.CLUSTER_TYPE_URL].watch,
                watches[Resources.CLUSTER_TYPE_URL].responses,
                MULTIPLE_RESOURCES_SNAPSHOT2);
            watches.Remove(Resources.CLUSTER_TYPE_URL);

            AssertThatWatchReceivesSnapshot(
                watches[Resources.ENDPOINT_TYPE_URL].watch,
                watches[Resources.ENDPOINT_TYPE_URL].responses,
                MULTIPLE_RESOURCES_SNAPSHOT2);
            watches.Remove(Resources.ENDPOINT_TYPE_URL);

            // Remaining watches should not trigger
            foreach (var w in watches.Values)
            {
                AssertThatWatchIsOpenWithNoResponses(w.watch, w.responses);
            }
        }
        public void SetSnapshotWithVersionMatchingRequestShouldLeaveWatchOpenWithoutAdditionalResponse(bool ads)
        {
            var cache = new SimpleCache <string>(_ => "key");

            cache.SetSnapshot("key", SNAPSHOT1);

            var watches = Resources.TYPE_URLS.ToDictionary(
                typeUrl => typeUrl,
                typeUrl =>
            {
                var responses        = new List <Response>();
                var discoveryRequest = new DiscoveryRequest
                {
                    Node        = new Node(),
                    TypeUrl     = typeUrl,
                    VersionInfo = SNAPSHOT1.GetVersion(typeUrl)
                };

                discoveryRequest.ResourceNames.AddRange(SNAPSHOT1.GetResources(typeUrl).Keys);

                var watch = cache.CreateWatch(
                    ads,
                    discoveryRequest,
                    SNAPSHOT1.GetResources(typeUrl).Keys.ToHashSet(),
                    responses.Add);

                return(new
                {
                    watch = watch,
                    responses = responses
                });
            });

            // The request version matches the current snapshot version, so the watches shouldn't receive any responses.
            foreach (string typeUrl in Resources.TYPE_URLS)
            {
                AssertThatWatchIsOpenWithNoResponses(watches[typeUrl].watch, watches[typeUrl].responses);
            }

            cache.SetSnapshot("key", SNAPSHOT1);

            // The request version still matches the current snapshot version, so the watches shouldn't receive any responses.
            foreach (var typeUrl in Resources.TYPE_URLS)
            {
                AssertThatWatchIsOpenWithNoResponses(watches[typeUrl].watch, watches[typeUrl].responses);
            }
        }
        public void SuccessfullyWatchAllResourceTypesWithSetBeforeWatchWithSameRequestVersionNewResourceHintsNoChange(bool ads)
        {
            var cache = new SimpleCache <string>(_ => "key");

            cache.SetSnapshot("key", SNAPSHOT2);

            // Set a watch for the current snapshot for the same version but with new resource hints not
            // present in the snapshot that the watch creator does not know about.
            //
            // Note that we're requesting the additional resources found in MULTIPLE_RESOURCE_SNAPSHOT2
            // while we only know about the resources found in SNAPSHOT2. Since SNAPSHOT2 is the current
            // snapshot, we have nothing to respond with for the new resources so we should not trigger
            // the watch.
            var watches = Resources.TYPE_URLS.ToDictionary(
                typeUrl => typeUrl,
                typeUrl =>
            {
                var responses        = new List <Response>();
                var discoveryRequest = new DiscoveryRequest
                {
                    Node        = new Node(),
                    TypeUrl     = typeUrl,
                    VersionInfo = SNAPSHOT2.GetVersion(typeUrl)
                };

                discoveryRequest.ResourceNames.AddRange(MULTIPLE_RESOURCES_SNAPSHOT2.GetResources(typeUrl).Keys);

                var watch = cache.CreateWatch(
                    ads,
                    discoveryRequest,
                    SNAPSHOT2.GetResources(typeUrl).Keys.ToHashSet(),
                    responses.Add);

                return(new
                {
                    watch = watch,
                    responses = responses
                });
            });

            // No watches should trigger since no new information will be returned
            foreach (var val in watches.Values)
            {
                AssertThatWatchIsOpenWithNoResponses(val.watch, val.responses);
            }
        }
        public void Groups(bool ads)
        {
            var cache = new SimpleCache <string>(_ => "key");

            cache.Groups.Should().BeEmpty();
            var discoveryRequest = new DiscoveryRequest
            {
                Node    = new Node(),
                TypeUrl = ""
            };

            cache.CreateWatch(
                ads,
                discoveryRequest,
                EmptySet,
                r => { });

            cache.Groups.Should().BeEquivalentTo("key");
        }
        public void WatchesAreReleasedAfterCancel(bool ads)
        {
            var cache = new SimpleCache <string>(_ => "key");

            var watches = Resources.TYPE_URLS.ToDictionary(
                typeUrl => typeUrl,
                typeUrl =>
            {
                var responses        = new List <Response>();
                var discoveryRequest = new DiscoveryRequest
                {
                    Node    = new Node(),
                    TypeUrl = typeUrl,
                };

                discoveryRequest.ResourceNames.AddRange(SNAPSHOT1.GetResources(typeUrl).Keys);

                var watch = cache.CreateWatch(
                    ads,
                    discoveryRequest,
                    EmptySet,
                    responses.Add);

                return(new
                {
                    watch = watch,
                    responses = responses
                });
            });

            var statusInfo = cache.GetStatusInfo("key");

            statusInfo.NumWatches.Should().Be(watches.Count);

            watches.Values.ToList().ForEach(w => w.watch.Cancel());

            statusInfo.NumWatches.Should().Be(0);

            watches.Values.ToList().ForEach(w => w.watch.IsCancelled.Should().BeTrue());
        }
        void InvalidNamesListShouldReturnWatcherWithNoResponseInAdsMode()
        {
            var cache = new SimpleCache <string>(_ => "key");

            cache.SetSnapshot("key", SNAPSHOT1);
            var responses = new List <Response>();

            var discoveryRequest = new DiscoveryRequest
            {
                Node    = new Node(),
                TypeUrl = Resources.ENDPOINT_TYPE_URL
            };

            discoveryRequest.ResourceNames.Add("none");

            var watch = cache.CreateWatch(
                true,
                discoveryRequest,
                EmptySet,
                responses.Add);

            AssertThatWatchIsOpenWithNoResponses(watch, responses);
        }
        public void SuccessfullyWatchAllResourceTypesWithSetAfterWatch(bool ads)
        {
            var cache = new SimpleCache <string>(_ => "key");

            var watches = Resources.TYPE_URLS
                          .ToDictionary(k => k, typeUrl =>
            {
                var responses        = new List <Response>();
                var discoveryRequest = new DiscoveryRequest
                {
                    Node    = new Node(),
                    TypeUrl = typeUrl
                };

                discoveryRequest.ResourceNames.AddRange(SNAPSHOT1.GetResources(typeUrl).Keys);

                var watch = cache.CreateWatch(
                    ads,
                    discoveryRequest,
                    EmptySet,
                    responses.Add);

                return(new
                {
                    watch = watch,
                    responses = responses
                });
            });

            cache.SetSnapshot("key", SNAPSHOT1);

            foreach (var typeUrl in Resources.TYPE_URLS)
            {
                var value = watches.GetValueOrDefault(typeUrl);
                AssertThatWatchReceivesSnapshot(value.watch, value.responses, SNAPSHOT1);
            }
        }
        public void InvalidNamesListShouldReturnWatcherWithResponseInXdsMode()
        {
            var cache     = new SimpleCache <string>(_ => "key");
            var responses = new List <Response>();

            cache.SetSnapshot("key", SNAPSHOT1);

            var discoveryRequest = new DiscoveryRequest
            {
                Node    = new Node(),
                TypeUrl = Resources.ENDPOINT_TYPE_URL
            };

            discoveryRequest.ResourceNames.Add("none");

            var watch = cache.CreateWatch(
                false,
                discoveryRequest,
                EmptySet,
                responses.Add);

            watch.IsCancelled.Should().BeFalse();
            responses.Should().NotBeEmpty();
        }