public static StoreResponse ReturnThrottledStoreResponseOnItemOperation(
            Uri physicalAddress,
            ResourceOperation resourceOperation,
            DocumentServiceRequest request,
            Guid activityId,
            string errorMessage)
        {
            if (request.ResourceType == ResourceType.Document)
            {
                StoreRequestNameValueCollection headers = new StoreRequestNameValueCollection();
                headers.Add(HttpConstants.HttpHeaders.ActivityId, activityId.ToString());
                headers.Add(WFConstants.BackendHeaders.SubStatus, ((int)SubStatusCodes.WriteForbidden).ToString(CultureInfo.InvariantCulture));
                headers.Add(HttpConstants.HttpHeaders.RetryAfterInMilliseconds, TimeSpan.FromMilliseconds(100).TotalMilliseconds.ToString(CultureInfo.InvariantCulture));
                headers.Add(HttpConstants.HttpHeaders.RequestCharge, ((double)9001).ToString(CultureInfo.InvariantCulture));

                StoreResponse storeResponse = new StoreResponse()
                {
                    Status       = 429,
                    Headers      = headers,
                    ResponseBody = new MemoryStream(Encoding.UTF8.GetBytes(errorMessage))
                };

                return(storeResponse);
            }

            return(null);
        }
Exemplo n.º 2
0
        public async Task AddPartitionKeyRangeToContinuationTokenOnNotNullBackendContinuation()
        {
            ResolvedRangeInfo currentPartitionKeyRange = new ResolvedRangeInfo(new PartitionKeyRange {
                Id = "1", MinInclusive = "B", MaxExclusive = "C"
            }, null);
            Mock <IRoutingMapProvider> routingMapProvider = new Mock <IRoutingMapProvider>();

            routingMapProvider.Setup(m => m.TryGetOverlappingRangesAsync(
                                         It.IsAny <string>(),
                                         It.IsAny <Range <string> >(),
                                         It.IsAny <bool>()
                                         )).Returns(Task.FromResult <IReadOnlyList <PartitionKeyRange> >(null)).Verifiable();

            PartitionRoutingHelper          partitionRoutingHelper = new PartitionRoutingHelper();
            StoreRequestNameValueCollection headers = new StoreRequestNameValueCollection();

            headers.Add(HttpConstants.HttpHeaders.Continuation, "something");
            bool result = await partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
                headers,
                null,
                routingMapProvider.Object,
                CollectionId,
                currentPartitionKeyRange,
                RntdbEnumerationDirection.Reverse
                );

            Assert.IsTrue(true);
            routingMapProvider.Verify(m => m.TryGetOverlappingRangesAsync(
                                          It.IsAny <string>(),
                                          It.IsAny <Range <string> >(),
                                          It.IsAny <bool>()
                                          ), Times.Never);
        }
        private async Task <DocumentServiceResponse> GetMasterAddressesViaGatewayAsync(
            DocumentServiceRequest request,
            ResourceType resourceType,
            string resourceAddress,
            string entryUrl,
            bool forceRefresh,
            bool useMasterCollectionResolver)
        {
            INameValueCollection addressQuery = new StoreRequestNameValueCollection
            {
                { HttpConstants.QueryStrings.Url, HttpUtility.UrlEncode(entryUrl) }
            };

            INameValueCollection headers = new StoreRequestNameValueCollection();

            if (forceRefresh)
            {
                headers.Set(HttpConstants.HttpHeaders.ForceRefresh, bool.TrueString);
            }

            if (useMasterCollectionResolver)
            {
                headers.Set(HttpConstants.HttpHeaders.UseMasterCollectionResolver, bool.TrueString);
            }

            if (request.ForceCollectionRoutingMapRefresh)
            {
                headers.Set(HttpConstants.HttpHeaders.ForceCollectionRoutingMapRefresh, bool.TrueString);
            }

            addressQuery.Add(HttpConstants.QueryStrings.Filter, this.protocolFilter);

            string resourceTypeToSign = PathsHelper.GetResourcePath(resourceType);

            headers.Set(HttpConstants.HttpHeaders.XDate, DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture));
            using (ITrace trace = Trace.GetRootTrace(nameof(GetMasterAddressesViaGatewayAsync), TraceComponent.Authorization, TraceLevel.Info))
            {
                string token = await this.tokenProvider.GetUserAuthorizationTokenAsync(
                    resourceAddress,
                    resourceTypeToSign,
                    HttpConstants.HttpMethods.Get,
                    headers,
                    AuthorizationTokenType.PrimaryMasterKey,
                    trace);

                headers.Set(HttpConstants.HttpHeaders.Authorization, token);

                Uri targetEndpoint = UrlUtility.SetQuery(this.addressEndpoint, UrlUtility.CreateQuery(addressQuery));

                string identifier = GatewayAddressCache.LogAddressResolutionStart(request, targetEndpoint);
                using (HttpResponseMessage httpResponseMessage = await this.httpClient.GetAsync(
                           uri: targetEndpoint,
                           additionalHeaders: headers,
                           resourceType: resourceType,
                           timeoutPolicy: HttpTimeoutPolicyControlPlaneRetriableHotPath.Instance,
                           clientSideRequestStatistics: request.RequestContext?.ClientRequestStatistics,
                           cancellationToken: default))
        private async Task <FeedResource <Address> > GetMasterAddressesViaGatewayAsync(
            DocumentServiceRequest request,
            ResourceType resourceType,
            string resourceAddress,
            string entryUrl,
            bool forceRefresh,
            bool useMasterCollectionResolver)
        {
            INameValueCollection addressQuery = new StoreRequestNameValueCollection
            {
                { HttpConstants.QueryStrings.Url, HttpUtility.UrlEncode(entryUrl) }
            };

            INameValueCollection headers = new StoreRequestNameValueCollection();

            if (forceRefresh)
            {
                headers.Set(HttpConstants.HttpHeaders.ForceRefresh, bool.TrueString);
            }

            if (useMasterCollectionResolver)
            {
                headers.Set(HttpConstants.HttpHeaders.UseMasterCollectionResolver, bool.TrueString);
            }

            if (request.ForceCollectionRoutingMapRefresh)
            {
                headers.Set(HttpConstants.HttpHeaders.ForceCollectionRoutingMapRefresh, bool.TrueString);
            }

            addressQuery.Add(HttpConstants.QueryStrings.Filter, this.protocolFilter);

            string resourceTypeToSign = PathsHelper.GetResourcePath(resourceType);

            headers.Set(HttpConstants.HttpHeaders.XDate, DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture));
            (string token, string _) = await this.tokenProvider.GetUserAuthorizationAsync(
                resourceAddress,
                resourceTypeToSign,
                HttpConstants.HttpMethods.Get,
                headers,
                AuthorizationTokenType.PrimaryMasterKey);

            headers.Set(HttpConstants.HttpHeaders.Authorization, token);

            Uri targetEndpoint = UrlUtility.SetQuery(this.addressEndpoint, UrlUtility.CreateQuery(addressQuery));

            string identifier = GatewayAddressCache.LogAddressResolutionStart(request, targetEndpoint);

            using (HttpResponseMessage httpResponseMessage = await this.httpClient.GetAsync(
                       uri: targetEndpoint,
                       additionalHeaders: headers,
                       resourceType: resourceType,
                       diagnosticsContext: null,
                       cancellationToken: default))
        public static void ThrowForbiddendExceptionOnItemOperation(
            Uri physicalAddress,
            DocumentServiceRequest request,
            string activityId,
            string errorMessage)
        {
            if (request.ResourceType == ResourceType.Document)
            {
                StoreRequestNameValueCollection headers = new StoreRequestNameValueCollection();
                headers.Add(HttpConstants.HttpHeaders.ActivityId, activityId.ToString());
                headers.Add(WFConstants.BackendHeaders.SubStatus, ((int)SubStatusCodes.WriteForbidden).ToString(CultureInfo.InvariantCulture));
                headers.Add(HttpConstants.HttpHeaders.RequestCharge, ((double)9001).ToString(CultureInfo.InvariantCulture));

                ForbiddenException forbiddenException = new ForbiddenException(
                    errorMessage,
                    headers,
                    physicalAddress);

                throw forbiddenException;
            }
        }
        public async Task AddPartitionKeyRangeToContinuationTokenOnSplit()
        {
            const string BackendToken = "backendToken";
            StoreRequestNameValueCollection   headers = new StoreRequestNameValueCollection();
            List <CompositeContinuationToken> compositeContinuationTokensFromSplit = new List <CompositeContinuationToken>
            {
                new CompositeContinuationToken {
                    Token = "someToken", Range = new Range <string>("A", "B", true, false)
                },
                new CompositeContinuationToken {
                    Token = "anotherToken", Range = new Range <string>("B", "C", true, false)
                }
            };

            PartitionRoutingHelper partitionRoutingHelper = new PartitionRoutingHelper();

            //With backend header
            headers.Add(HttpConstants.HttpHeaders.Continuation, BackendToken);
            ResolvedRangeInfo resolvedRangeInfo = new ResolvedRangeInfo(new PartitionKeyRange(), new List <CompositeContinuationToken>(compositeContinuationTokensFromSplit));
            bool result = await partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
                headers,
                null,
                null,
                null,
                resolvedRangeInfo,
                NoOpTrace.Singleton,
                RntdbEnumerationDirection.Reverse);

            List <CompositeContinuationToken> compositeContinuationTokens = JsonConvert.DeserializeObject <List <CompositeContinuationToken> >(headers.Get(HttpConstants.HttpHeaders.Continuation));

            Assert.IsTrue(result);
            Assert.AreEqual(compositeContinuationTokensFromSplit.Count, compositeContinuationTokens.Count);
            Assert.AreEqual(BackendToken, compositeContinuationTokens.First().Token);
            Assert.AreNotEqual(BackendToken, compositeContinuationTokens.Last().Token);

            //Without backend header
            headers.Remove(HttpConstants.HttpHeaders.Continuation);
            resolvedRangeInfo = new ResolvedRangeInfo(new PartitionKeyRange(), new List <CompositeContinuationToken>(compositeContinuationTokensFromSplit));
            result            = await partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
                headers,
                null,
                null,
                null,
                resolvedRangeInfo,
                NoOpTrace.Singleton,
                RntdbEnumerationDirection.Reverse);

            compositeContinuationTokens = JsonConvert.DeserializeObject <List <CompositeContinuationToken> >(headers.Get(HttpConstants.HttpHeaders.Continuation));
            Assert.IsTrue(result);
            Assert.IsTrue(compositeContinuationTokens.Count == compositeContinuationTokensFromSplit.Count - 1);
            Assert.AreEqual(compositeContinuationTokensFromSplit.Last().Token, compositeContinuationTokens.First().Token);
        }
Exemplo n.º 7
0
        public void AuthorizationBaselineTests()
        {
            string key = "VGhpcyBpcyBhIHNhbXBsZSBzdHJpbmc=";

            for (int i = 0; i < this.AuthorizationBaseline.Length; i++)
            {
                string[] baseline                   = this.AuthorizationBaseline[i];
                string[] baselineResults            = this.AuthorizationBaselineResults[i];
                StoreRequestNameValueCollection nvc = new StoreRequestNameValueCollection();
                nvc.Add(HttpConstants.HttpHeaders.XDate, baseline[4]);
                Uri    uri           = new Uri(baseline[0]);
                string authorization = AuthorizationHelper.GenerateKeyAuthorizationSignature(
                    verb: baseline[2],
                    uri: uri,
                    headers: nvc,
                    stringHMACSHA256Helper: new StringHMACSHA256Hash(key));

                string authorization2 = AuthorizationHelper.GenerateKeyAuthorizationSignature(
                    verb: baseline[2],
                    resourceId: baseline[1],
                    resourceType: baseline[3],
                    headers: nvc,
                    new StringHMACSHA256Hash(key),
                    out string payload2);

                string authorization3 = AuthorizationHelper.GenerateKeyAuthorizationSignature(
                    verb: baseline[2],
                    resourceId: baseline[1],
                    resourceType: baseline[3],
                    headers: nvc,
                    key);

                Assert.AreEqual(authorization, baselineResults[0]);
                Assert.AreEqual(authorization2, baselineResults[0]);
                Assert.AreEqual(authorization3, baselineResults[0]);
                Assert.AreEqual(payload2.Replace("\n", "[n]"), baselineResults[1]);

                AuthorizationHelper.ParseAuthorizationToken(authorization, out ReadOnlyMemory <char> typeOutput1, out ReadOnlyMemory <char> versionoutput1, out ReadOnlyMemory <char> tokenOutput1);
                Assert.AreEqual("master", typeOutput1.ToString());
                AuthorizationHelper.ParseAuthorizationToken(authorization2, out ReadOnlyMemory <char> typeOutput2, out ReadOnlyMemory <char> versionoutput2, out ReadOnlyMemory <char> tokenOutput2);
                Assert.AreEqual("master", typeOutput2.ToString());
                AuthorizationHelper.ParseAuthorizationToken(authorization3, out ReadOnlyMemory <char> typeOutput3, out ReadOnlyMemory <char> versionoutput3, out ReadOnlyMemory <char> tokenOutput3);
                Assert.AreEqual("master", typeOutput3.ToString());

                Assert.IsTrue(AuthorizationHelper.CheckPayloadUsingKey(tokenOutput1, baseline[2], baseline[1], baseline[3], nvc, key));
                Assert.IsTrue(AuthorizationHelper.CheckPayloadUsingKey(tokenOutput2, baseline[2], baseline[1], baseline[3], nvc, key));
                Assert.IsTrue(AuthorizationHelper.CheckPayloadUsingKey(tokenOutput3, baseline[2], baseline[1], baseline[3], nvc, key));
            }
        }
Exemplo n.º 8
0
        public void CompositeContinuationTokenIsNotPassedToBackend()
        {
            Range <string>             expectedRange = new Range <string>("A", "B", true, false);
            string                     expectedToken = "someToken";
            CompositeContinuationToken compositeContinuationToken = new CompositeContinuationToken {
                Range = expectedRange, Token = expectedToken
            };
            string continuation = JsonConvert.SerializeObject(compositeContinuationToken);
            PartitionRoutingHelper          partitionRoutingHelper = new PartitionRoutingHelper();
            StoreRequestNameValueCollection headers = new StoreRequestNameValueCollection();

            headers.Add(HttpConstants.HttpHeaders.Continuation, continuation);
            Range <string> range = partitionRoutingHelper.ExtractPartitionKeyRangeFromContinuationToken(headers, out List <CompositeContinuationToken> compositeContinuationTokens);

            Assert.IsTrue(expectedRange.Equals(range));
            Assert.AreEqual(expectedToken, headers.Get(HttpConstants.HttpHeaders.Continuation)); //not a composite token
        }
Exemplo n.º 9
0
        public void AuthorizationGenerateAndCheckKeyAuthSignature()
        {
            Random r = new Random();

            byte[] hashKey = new byte[16];
            r.NextBytes(hashKey);
            string key = Convert.ToBase64String(hashKey);

            foreach (string method in this.HttpMethods)
            {
                foreach (string resourceType in this.ResourceTypesArray)
                {
                    foreach (string resourceName in this.ResourceNameValues)
                    {
                        StoreRequestNameValueCollection nvc = new StoreRequestNameValueCollection();
                        nvc.Add(HttpConstants.HttpHeaders.XDate, new DateTime(2020, 02, 01, 10, 00, 00).ToString("r"));
                        string authorizationKey = AuthorizationHelper.GenerateKeyAuthorizationSignature(
                            method,
                            resourceName,
                            resourceType,
                            nvc,
                            new StringHMACSHA256Hash(key),
                            out string payload);

                        AuthorizationHelper.ParseAuthorizationToken(authorizationKey,
                                                                    out ReadOnlyMemory <char> typeOutput,
                                                                    out ReadOnlyMemory <char> versionOutput,
                                                                    out ReadOnlyMemory <char> tokenOutput);
                        Assert.AreEqual("master", typeOutput.ToString());

                        Assert.IsTrue(AuthorizationHelper.CheckPayloadUsingKey(
                                          tokenOutput,
                                          method,
                                          resourceName,
                                          resourceType,
                                          nvc,
                                          key));
                    }
                }
            }
        }
Exemplo n.º 10
0
        public async Task QueryRequestRateTest(bool directMode)
        {
            string firstItemIdAndPk = "BasicQueryItem" + Guid.NewGuid();

            // Prevent the test from changing the static client
            {
                CosmosClient client    = directMode ? DirectCosmosClient : GatewayCosmosClient;
                Container    container = client.GetContainer(DatabaseId, ContainerId);

                List <string> createdIds = new List <string>()
                {
                    firstItemIdAndPk,
                    "BasicQueryItem2" + Guid.NewGuid(),
                    "BasicQueryItem3" + Guid.NewGuid()
                };

                foreach (string id in createdIds)
                {
                    dynamic item = new
                    {
                        id = id,
                        pk = id,
                    };

                    await container.CreateItemAsync <dynamic>(item : item);
                }
            }

            CosmosClient clientWithThrottle;

            if (directMode)
            {
                clientWithThrottle = TestCommon.CreateCosmosClient();
            }
            else
            {
                clientWithThrottle = TestCommon.CreateCosmosClient((builder) => builder.WithConnectionModeGateway());
            }

            Container containerWithThrottle = clientWithThrottle.GetContainer(DatabaseId, ContainerId);

            // Do a read to warm up all the caches to prevent them from getting the throttle errors
            using (await containerWithThrottle.ReadItemStreamAsync(firstItemIdAndPk, new PartitionKey(firstItemIdAndPk))) { }

            Documents.IStoreModel        storeModel = clientWithThrottle.ClientContext.DocumentClient.StoreModel;
            Mock <Documents.IStoreModel> mockStore  = new Mock <Documents.IStoreModel>();

            clientWithThrottle.ClientContext.DocumentClient.StoreModel = mockStore.Object;

            // Cause 429 after the first call
            int    callCount    = 0;
            string activityId   = null;
            string errorMessage = "QueryRequestRateTest Resource Not Found";

            mockStore.Setup(x => x.ProcessMessageAsync(It.IsAny <Documents.DocumentServiceRequest>(), It.IsAny <CancellationToken>()))
            .Returns <Documents.DocumentServiceRequest, CancellationToken>((dsr, token) =>
            {
                callCount++;

                if (callCount > 1)
                {
                    INameValueCollection headers = new StoreRequestNameValueCollection();
                    headers.Add(Documents.HttpConstants.HttpHeaders.RetryAfterInMilliseconds, "42");
                    activityId = Guid.NewGuid().ToString();
                    headers.Add(Documents.HttpConstants.HttpHeaders.ActivityId, activityId);
                    Documents.DocumentServiceResponse response = new Documents.DocumentServiceResponse(
                        body: TestCommon.GenerateStreamFromString(@"{""Errors"":[""" + errorMessage + @"""]}"),
                        headers: headers,
                        statusCode: (HttpStatusCode)429,
                        clientSideRequestStatistics: dsr.RequestContext.ClientRequestStatistics);

                    return(Task.FromResult(response));
                }

                return(storeModel.ProcessMessageAsync(dsr, token));
            });

            List <dynamic> results = new List <dynamic>();

            try
            {
                using (FeedIterator <dynamic> feedIterator = containerWithThrottle.GetItemQueryIterator <dynamic>(
                           "select * from T where STARTSWITH(T.id, \"BasicQueryItem\")",
                           requestOptions: new QueryRequestOptions()
                {
                    MaxItemCount = 1,
                    MaxConcurrency = 1
                }))
                {
                    while (feedIterator.HasMoreResults)
                    {
                        FeedResponse <dynamic> response = await feedIterator.ReadNextAsync();

                        Assert.IsTrue(response.Count <= 1);
                        Assert.IsTrue(response.Resource.Count() <= 1);

                        results.AddRange(response);
                    }
                }
                Assert.Fail("Should throw 429 exception after the first page.");
            }
            catch (CosmosException ce)
            {
                Assert.IsTrue(ce.RetryAfter.HasValue);
                Assert.AreEqual(42, ce.RetryAfter.Value.TotalMilliseconds);
                Assert.AreEqual(activityId, ce.ActivityId);
                Assert.IsNotNull(ce.DiagnosticsContext);
                Assert.IsTrue(ce.Message.Contains(errorMessage));
            }

            callCount = 0;
            FeedIterator streamIterator = containerWithThrottle.GetItemQueryStreamIterator(
                "select * from T where STARTSWITH(T.id, \"BasicQueryItem\")",
                requestOptions: new QueryRequestOptions()
            {
                MaxItemCount   = 1,
                MaxConcurrency = 1
            });

            // First request should be a success
            using (ResponseMessage response = await streamIterator.ReadNextAsync())
            {
                response.EnsureSuccessStatusCode();
                Assert.IsNotNull(response.Content);
            }

            // Second page should be a failure
            using (ResponseMessage response = await streamIterator.ReadNextAsync())
            {
                Assert.AreEqual(429, (int)response.StatusCode);
                Assert.AreEqual("42", response.Headers.RetryAfterLiteral);
                Assert.AreEqual(activityId, response.Headers.ActivityId);
                Assert.IsNotNull(response.DiagnosticsContext);
                Assert.IsTrue(response.ErrorMessage.Contains(errorMessage));
            }
        }
Exemplo n.º 11
0
        public void VerifyAllKnownProperties()
        {
            Dictionary <string, string> httpHeadersMap = typeof(HttpConstants.HttpHeaders).GetFields(BindingFlags.Public | BindingFlags.Static)
                                                         .ToDictionary(x => x.Name, x => (string)x.GetValue(null));
            Dictionary <string, string> backendHeadersMap = typeof(WFConstants.BackendHeaders).GetFields(BindingFlags.Public | BindingFlags.Static)
                                                            .ToDictionary(x => x.Name, x => (string)x.GetValue(null));

            PropertyInfo[] optimizedResponseHeaders = typeof(StoreRequestNameValueCollection).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                                      .Where(x => !string.Equals("Item", x.Name)).ToArray();

            StoreRequestNameValueCollection headers = new StoreRequestNameValueCollection();

            foreach (PropertyInfo propertyInfo in optimizedResponseHeaders)
            {
                Assert.AreEqual(0, headers.Count());
                Assert.AreEqual(0, headers.Keys().Count());

                // Test property first
                string value = Guid.NewGuid().ToString();
                propertyInfo.SetValue(headers, value);
                Assert.AreEqual(value, propertyInfo.GetValue(headers));

                if (!httpHeadersMap.TryGetValue(propertyInfo.Name, out string key))
                {
                    if (!backendHeadersMap.TryGetValue(propertyInfo.Name, out key))
                    {
                        Assert.Fail($"The property name {propertyInfo.Name} should match a header constant name");
                    }
                }

                Assert.AreEqual(1, headers.Count());
                Assert.AreEqual(1, headers.Keys().Count());
                Assert.AreEqual(key, headers.Keys().First());
                Assert.AreEqual(value, headers.Get(key));
                Assert.AreEqual(value, headers.Get(key.ToUpper()));
                Assert.AreEqual(value, headers.Get(key.ToLower()));

                // Reset the value back to null
                propertyInfo.SetValue(headers, null);

                Assert.AreEqual(0, headers.Count());
                Assert.AreEqual(0, headers.Keys().Count());

                // Check adding via the interface sets the property correctly
                headers.Add(key, value);
                Assert.AreEqual(value, propertyInfo.GetValue(headers));
                Assert.AreEqual(value, headers.Get(key));

                Assert.AreEqual(1, headers.Count());
                Assert.AreEqual(1, headers.Keys().Count());
                Assert.AreEqual(key, headers.Keys().First());
                Assert.AreEqual(value, headers.Get(key));

                // Check setting via the interface sets the property correctly
                value = Guid.NewGuid().ToString();
                headers.Set(key, value);
                Assert.AreEqual(value, propertyInfo.GetValue(headers));
                Assert.AreEqual(value, headers.Get(key));

                Assert.AreEqual(1, headers.Count());
                Assert.AreEqual(1, headers.Keys().Count());
                Assert.AreEqual(key, headers.Keys().First());
                Assert.AreEqual(value, headers.Get(key));

                // Check setting via the interface sets the property correctly
                headers.Remove(key);
                Assert.AreEqual(null, propertyInfo.GetValue(headers));
                Assert.AreEqual(null, headers.Get(key));

                Assert.AreEqual(0, headers.Count());
                Assert.AreEqual(0, headers.Keys().Count());
            }
        }
Exemplo n.º 12
0
        internal static void ValidateQuery <T>(DocumentClient client, string collectionLink, string queryProperty, string queryPropertyValue, int expectedCount, INameValueCollection headers = null)
            where T : Resource, new()
        {
            INameValueCollection inputHeaders = headers;

            headers = new StoreRequestNameValueCollection();
            if (inputHeaders != null)
            {
                headers.Add(inputHeaders); // dont mess with the input headers
            }

            int       maxTries         = 5;
            const int minIndexInterval = 5000; // 5 seconds

            while (maxTries-- > 0)
            {
                DocumentFeedResponse <dynamic> resourceFeed = null;
                IDocumentQuery <dynamic>       queryService = null;
                string queryString = @"select * from root r where r." + queryProperty + @"=""" + queryPropertyValue + @"""";
                if (typeof(T) == typeof(Database))
                {
                    queryService = client.CreateDatabaseQuery(queryString).AsDocumentQuery();
                }
                else if (typeof(T) == typeof(DocumentCollection))
                {
                    queryService = client.CreateDocumentCollectionQuery(collectionLink, queryString).AsDocumentQuery();
                }
                else if (typeof(T) == typeof(Document))
                {
                    queryService = client.CreateDocumentQuery(collectionLink, queryString).AsDocumentQuery();
                }
                else
                {
                    Assert.Fail("Unexpected type");
                }

                while (queryService.HasMoreResults)
                {
                    resourceFeed = queryService.ExecuteNextAsync().Result;

                    if (resourceFeed.Count > 0)
                    {
                        Assert.IsNotNull(resourceFeed, "Query result is null");
                        Assert.AreNotEqual(0, resourceFeed.Count, "Query result is invalid");

                        foreach (T resource in resourceFeed)
                        {
                            if (queryProperty.Equals("name", StringComparison.CurrentCultureIgnoreCase))
                            {
                                Assert.AreEqual(resource.Id, queryPropertyValue, "Result contain invalid result");
                            }
                        }
                        return;
                    }
                }

                Task.Delay(minIndexInterval);
            }

            Assert.Fail("Query did not return result after max tries");
        }