Example #1
0
        public static void DownloadDump(string serviceUrl, string zippedLogsPath, NetworkCredential credentials = null)
        {
            try
            {
                Directory.CreateDirectory(Path.GetDirectoryName(zippedLogsPath));

                var clientHandler = HttpClientHelper.CreateClientHandler(serviceUrl, credentials);
                var client        = new HttpClient(clientHandler);
                var result        = client.GetAsync(serviceUrl + "api/dump").Result;
                if (result.IsSuccessStatusCode)
                {
                    using (Stream stream = result.Content.ReadAsStreamAsync().Result)
                    {
                        using (FileStream fs = File.Open(zippedLogsPath, FileMode.Create, FileAccess.Write))
                        {
                            stream.CopyTo(fs);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                TestTracer.Trace("Failed to download dump - {0}", ex.GetBaseException().Message);
            }
        }
Example #2
0
        public static async Task VerifyUrlAsync(string url, string content = null, HttpStatusCode statusCode = HttpStatusCode.OK,
                                                string httpMethod          = "GET", string jsonPayload = "", ICredentials credentials = null)
        {
            using (var client = new HttpClient(HttpClientHelper.CreateClientHandler(url, credentials)))
            {
                client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("Kudu-Test", "1.0"));
                HttpResponseMessage response = null;
                if (String.Equals(httpMethod, "POST"))
                {
                    response = await client.PostAsync(url, new StringContent(jsonPayload, Encoding.UTF8, "application/json"));
                }
                else
                {
                    response = await client.GetAsync(url);
                }
                string responseBody = await response.Content.ReadAsStringAsync();

                Assert.True(statusCode == response.StatusCode,
                            String.Format("For {0}, Expected Status Code: {1} Actual Status Code: {2}. \r\n Response: {3}", url, statusCode, response.StatusCode, responseBody));

                if (content != null)
                {
                    Assert.Contains(content, responseBody, StringComparison.Ordinal);
                }
            }
        }
Example #3
0
 public static void KillKuduProcess(string serviceUrl, NetworkCredential credentials = null)
 {
     try
     {
         var clientHandler = HttpClientHelper.CreateClientHandler(serviceUrl, credentials);
         var client        = new HttpClient(clientHandler);
         client.DeleteAsync(serviceUrl + "api/processes/0").Wait();
     }
     catch (Exception)
     {
         // no-op
     }
 }
Example #4
0
        public static async Task <XDocument> DownloadTrace(this IApplication application, ICredentials credentials)
        {
            using (var clientHandler = HttpClientHelper.CreateClientHandler(application.ServiceUrl, credentials))
            {
                using (var client = new HttpClient(clientHandler))
                {
                    HttpResponseMessage response = await client.GetAsync(application.ServiceUrl + "api/dump");

                    Stream stream = await response.EnsureSuccessStatusCode().Content.ReadAsStreamAsync();

                    return(ZipHelper.ExtractTrace(stream));
                }
            }
        }
Example #5
0
 public static void KillKuduProcess(string serviceUrl, NetworkCredential credentials = null)
 {
     try
     {
         var clientHandler = HttpClientHelper.CreateClientHandler(serviceUrl, credentials);
         var client        = new HttpClient(clientHandler);
         client.PostAsync(serviceUrl + "api/processes/0/kill", new StringContent(string.Empty, Encoding.UTF8, "text/plain")).Wait();
         //client.DeleteAsync(serviceUrl + "api/processes/0").Wait();
     }
     catch (Exception)
     {
         // no-op
     }
 }
Example #6
0
        public virtual async Task RunIntegrationTest()
        {
            string dir        = Guid.NewGuid().ToString("N");
            string dirAddress = BaseAddress + _segmentDelimiter + dir;
            string dirAddressWithTerminatingSlash = dirAddress + _segmentDelimiter;

            // The %2520 is there to test that we can accept those characters. Here, %2520 is the URL encoded form,
            // and the actual file name has %20 (and not a space character!)
            string file        = Guid.NewGuid().ToString("N") + "%2520" + ".txt";
            string fileAddress = dirAddressWithTerminatingSlash + file;
            string fileAddressWithTerminatingSlash = fileAddress + _segmentDelimiter;

            string query = "?foo=bar";
            string baseAddressWithQuery = BaseAddress + _segmentDelimiter + query;
            string dirAddressWithQuery  = dirAddressWithTerminatingSlash + query;
            string fileAddressWithQuery = fileAddress + query;

            string deploymentFileAddress       = null;
            string customDeploymentFileAddress = null;

            if (DeploymentClient != null)
            {
                deploymentFileAddress       = string.Format("{0}{1}site{1}wwwroot{1}{2}{1}{3}", DeploymentBaseAddress, _segmentDelimiter, dir, file);
                customDeploymentFileAddress = string.Format("{0}{1}site{1}wwwroot{1}test{1}{2}{1}{3}", DeploymentBaseAddress, _segmentDelimiter, dir, file);
            }

            TestTracer.Trace("Starting RunIntegrationTest");
            TestTracer.Trace("Dir - {0}", dirAddress);
            TestTracer.Trace("File - {0}", fileAddress);
            TestTracer.Trace("DeploymentFileAddress - {0}", deploymentFileAddress);

            HttpResponseMessage response;

            // Check not found file responses
            TestTracer.Trace("==== Check not found file responses");
            response = await HttpGetAsync(dirAddress);

            Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);

            response = await HttpGetAsync(dirAddressWithTerminatingSlash);

            Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);

            response = await HttpGetAsync(fileAddress);

            Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);

            response = await HttpGetAsync(fileAddressWithTerminatingSlash);

            Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);

            // Check create file results in 201 response with etag
            TestTracer.Trace("==== Check create file results in 201 response with etag");
            response = await HttpPutAsync(fileAddress, CreateUploadContent(_fileContent0));
            await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent0);

            Assert.Equal(HttpStatusCode.Created, response.StatusCode);
            EntityTagHeaderValue originalEtag = response.Headers.ETag;

            Assert.NotNull(originalEtag);

            DateTimeOffset?lastModified = response.Content.Headers.LastModified;

            if (!_isScmEditorTest)
            {
                Assert.NotNull(lastModified);
            }

            // Check query string
            TestTracer.Trace("==== Check handle query string");
            response = await HttpGetAsync(baseAddressWithQuery);

            Assert.Equal(HttpStatusCode.OK, response.StatusCode);
            response = await HttpGetAsync(dirAddressWithQuery);

            Assert.Equal(HttpStatusCode.OK, response.StatusCode);
            response = await HttpGetAsync(fileAddressWithQuery);

            Assert.Equal(HttpStatusCode.OK, response.StatusCode);

            // Check that we get a 200 (OK) on created file with the correct etag
            TestTracer.Trace("==== Check that we get a 200 (OK) on created file with the correct etag");
            response = await HttpGetAsync(fileAddress);

            Assert.Equal(HttpStatusCode.OK, response.StatusCode);
            Assert.Equal(originalEtag, response.Headers.ETag);
            Assert.Equal(lastModified, response.Content.Headers.LastModified);
            Assert.Equal(_fileMediaType, response.Content.Headers.ContentType);

            // Check that we get a 200 (OK) on created file using HEAD with the correct etag
            TestTracer.Trace("==== Check that we get a 200 (OK) on created file using HEAD with the correct etag");
            using (HttpRequestMessage headReq = new HttpRequestMessage())
            {
                headReq.Method     = HttpMethod.Head;
                headReq.RequestUri = new Uri(fileAddress);
                response           = await Client.SendAsync(headReq);

                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                Assert.Equal(originalEtag, response.Headers.ETag);
                Assert.Equal(_fileMediaType, response.Content.Headers.ContentType);
            }

            // Check that we get a 304 (Not Modified) response if matching If-None-Match
            TestTracer.Trace("==== Check that we get a 304 (Not Modified) response if matching If-None-Match");
            using (HttpRequestMessage ifNoneMatchReq = new HttpRequestMessage())
            {
                ifNoneMatchReq.RequestUri = new Uri(fileAddress);
                ifNoneMatchReq.Headers.IfNoneMatch.Add(originalEtag);
                response = await HttpSendAsync(ifNoneMatchReq);

                Assert.Equal(HttpStatusCode.NotModified, response.StatusCode);
                Assert.Equal(originalEtag, response.Headers.ETag);
            }

            // Check that we get a 200 (OK) response if not matching If-None-Match
            TestTracer.Trace("==== Check that we get a 200 (OK) response if not matching If-None-Match");
            using (HttpRequestMessage ifNoneMatchReqBadEtag = new HttpRequestMessage())
            {
                ifNoneMatchReqBadEtag.RequestUri = new Uri(fileAddress);
                ifNoneMatchReqBadEtag.Headers.IfNoneMatch.Add(new EntityTagHeaderValue("\"NotMatching\""));
                response = await HttpSendAsync(ifNoneMatchReqBadEtag);

                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                Assert.Equal(originalEtag, response.Headers.ETag);
            }

            // Check that If-Range request with range returns 206 (Partial Content)
            TestTracer.Trace("==== Check that If-Range request with range returns 206 (Partial Content)");
            using (HttpRequestMessage ifRangeReq = new HttpRequestMessage())
            {
                ifRangeReq.RequestUri      = new Uri(fileAddress);
                ifRangeReq.Headers.IfRange = new RangeConditionHeaderValue(originalEtag);
                ifRangeReq.Headers.Range   = new RangeHeaderValue(0, 0)
                {
                    Unit = "bytes"
                };
                response = await HttpSendAsync(ifRangeReq);

                Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
                Assert.Equal(originalEtag, response.Headers.ETag);
                Assert.Equal(1, response.Content.Headers.ContentLength);
                Assert.Equal(new ContentRangeHeaderValue(0, 0, _fileContent0.Length), response.Content.Headers.ContentRange);
            }

            // Check that If-Range request with no range returns 200 (OK)
            TestTracer.Trace("==== Check that If-Range request with no range returns 200 (OK)");
            using (HttpRequestMessage ifRangeReqNoRange = new HttpRequestMessage())
            {
                ifRangeReqNoRange.RequestUri      = new Uri(fileAddress);
                ifRangeReqNoRange.Headers.IfRange = new RangeConditionHeaderValue(originalEtag);
                response = await HttpSendAsync(ifRangeReqNoRange);

                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                Assert.Equal(originalEtag, response.Headers.ETag);
            }

            // Check that If-Range request with bad range returns 416 (Requested Range Not Satisfiable)
            // including a Content-Range header
            TestTracer.Trace("==== Check that If-Range request with bad range returns 416 (Requested Range Not Satisfiable)");
            using (HttpRequestMessage ifRangeReqBadRange = new HttpRequestMessage())
            {
                ifRangeReqBadRange.RequestUri      = new Uri(fileAddress);
                ifRangeReqBadRange.Headers.IfRange = new RangeConditionHeaderValue(originalEtag);
                ifRangeReqBadRange.Headers.Range   = new RangeHeaderValue(100, 100)
                {
                    Unit = "bytes"
                };
                response = await HttpSendAsync(ifRangeReqBadRange);

                Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, response.StatusCode);
                Assert.Equal(_fileContentRange, response.Content.Headers.ContentRange);
            }

            // Check that we get a root directory view
            TestTracer.Trace("==== Check that we get a root directory view");
            response = await HttpGetAsync(BaseAddress);

            Assert.Equal(_dirMediaType, response.Content.Headers.ContentType);

            // Check that we get a directory view from folder
            TestTracer.Trace("==== Check that we get a directory view from folder");
            response = await HttpGetAsync(dirAddress);

            Assert.Equal(_dirMediaType, response.Content.Headers.ContentType);

            // Check various redirects between files and folders
            HttpClientHandler redirectHandler = HttpClientHelper.CreateClientHandler(BaseAddress, KuduClient.Credentials);

            redirectHandler.AllowAutoRedirect = false;
            using (HttpClient redirectClient = HttpClientHelper.CreateClient(BaseAddress, KuduClient.Credentials, redirectHandler))
            {
                // Ensure that requests to root without slash is redirected to one with slash
                TestTracer.Trace("==== Ensure that requests to root without slash is redirected to one with slash");
                response = await HttpGetAsync(BaseAddress, redirectClient);

                Assert.Equal(HttpStatusCode.TemporaryRedirect, response.StatusCode);
                Assert.Equal(BaseAddress + _segmentDelimiter, response.Headers.Location.AbsoluteUri);

                // Ensure that requests to directory without slash is redirected to one with slash
                TestTracer.Trace("==== Ensure that requests to directory without slash is redirected to one with slash");
                response = await HttpGetAsync(dirAddress, redirectClient);

                Assert.Equal(HttpStatusCode.TemporaryRedirect, response.StatusCode);
                Assert.Equal(dirAddressWithTerminatingSlash, response.Headers.Location.AbsoluteUri);

                // Ensure that requests to file with slash is redirected to one without slash
                TestTracer.Trace("==== Ensure that requests to file with slash is redirected to one without slash");
                response = await HttpGetAsync(fileAddressWithTerminatingSlash, redirectClient);

                Assert.Equal(HttpStatusCode.TemporaryRedirect, response.StatusCode);
                Assert.Equal(fileAddress, response.Headers.Location.AbsoluteUri);
            }

            // Check that 2nd create attempt fails
            TestTracer.Trace("==== Check that 2nd create attempt fails");
            response = await HttpPutAsync(fileAddress, CreateUploadContent(_fileContent0));

            Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
            Assert.Equal(originalEtag, response.Headers.ETag);

            // Check that we can't update a directory
            TestTracer.Trace("==== Check that we can't update a directory");
            response = await HttpPutAsync(dirAddress, CreateUploadContent(_fileContent0));

            Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);

            // Check that we can't delete a non-empty directory
            TestTracer.Trace("==== Check that we can't delete a non-empty directory");
            response = await HttpDeleteAsync(dirAddress);

            Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);

            EntityTagHeaderValue updatedEtag;

            if (_testConflictingUpdates)
            {
                // Update file with first edit based on original etag
                TestTracer.Trace("==== Update file with first edit based on original etag");
                using (HttpRequestMessage update1 = new HttpRequestMessage())
                {
                    update1.Method     = HttpMethod.Put;
                    update1.RequestUri = new Uri(fileAddress);
                    update1.Headers.IfMatch.Add(originalEtag);
                    update1.Content = CreateUploadContent(_fileContent1);

                    response = await HttpSendAsync(update1);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent1);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;
                }

                // Update file with second edit based on original etag (non-conflicting merge)
                TestTracer.Trace("==== Update file with second edit based on original etag (non-conflicting merge)");
                using (HttpRequestMessage update2 = new HttpRequestMessage())
                {
                    update2.Method     = HttpMethod.Put;
                    update2.RequestUri = new Uri(fileAddress);
                    update2.Headers.IfMatch.Add(originalEtag);
                    update2.Content = CreateUploadContent(_fileContent2);

                    response = await HttpSendAsync(update2);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent3);

                    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(updatedEtag, response.Headers.ETag);
                    Assert.Equal(_fileMediaType, response.Content.Headers.ContentType);
                    updatedEtag = response.Headers.ETag;
                }

                // Update file with third edit based on original etag (non-conflicting merge)
                TestTracer.Trace("==== Update file with third edit based on original etag (non-conflicting merge)");
                using (HttpRequestMessage update3 = new HttpRequestMessage())
                {
                    update3.Method     = HttpMethod.Put;
                    update3.RequestUri = new Uri(fileAddress);
                    update3.Headers.IfMatch.Add(originalEtag);
                    update3.Content = CreateUploadContent(_fileContent3);

                    response = await HttpSendAsync(update3);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent3);

                    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                    Assert.Equal(_fileMediaType, response.Content.Headers.ContentType);
                    updatedEtag = response.Headers.ETag;
                }

                // Update file with forth edit based on original etag (conflicting)
                TestTracer.Trace("==== Update file with forth edit based on original etag (conflicting)");
                using (HttpRequestMessage update4 = new HttpRequestMessage())
                {
                    update4.Method     = HttpMethod.Put;
                    update4.RequestUri = new Uri(fileAddress);
                    update4.Headers.IfMatch.Add(originalEtag);
                    update4.Content = CreateUploadContent(_fileContent4);

                    response = await HttpSendAsync(update4);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent3);

                    Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);
                    Assert.Equal(_conflictMediaType, response.Content.Headers.ContentType);
                    Assert.Null(response.Headers.ETag);
                    string content = await response.Content.ReadAsStringAsync();

                    Assert.True(Regex.IsMatch(content, _conflict));
                }

                // Update file with fifth edit based on invalid etag
                TestTracer.Trace("==== Update file with fifth edit based on invalid etag");
                using (HttpRequestMessage update5 = new HttpRequestMessage())
                {
                    update5.Method     = HttpMethod.Put;
                    update5.RequestUri = new Uri(fileAddress);
                    update5.Headers.IfMatch.Add(new EntityTagHeaderValue("\"invalidetag\""));
                    update5.Content = CreateUploadContent(_fileContent1);

                    response = await HttpSendAsync(update5);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent3);

                    Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                }

                // Check that update with wildcard etag succeeds
                TestTracer.Trace("==== Check that update with wildcard etag succeeds");
                using (HttpRequestMessage update6 = new HttpRequestMessage())
                {
                    update6.Method     = HttpMethod.Put;
                    update6.RequestUri = new Uri(fileAddress);
                    update6.Headers.IfMatch.Add(EntityTagHeaderValue.Any);
                    update6.Content = CreateUploadContent(_fileContent1);

                    response = await HttpSendAsync(update6);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent1);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;
                }

                TestTracer.Trace("==== Check that concurrent updates work");
                List <Task <HttpResponseMessage> > concurrentUpdates = new List <Task <HttpResponseMessage> >();
                for (int cnt = 0; cnt < 16; cnt++)
                {
                    HttpRequestMessage concurrentRequest = new HttpRequestMessage()
                    {
                        Method     = HttpMethod.Put,
                        RequestUri = new Uri(fileAddress + "?nodeploy"),
                        Content    = CreateUploadContent(_fileContent2)
                    };

                    concurrentRequest.Headers.IfMatch.Add(EntityTagHeaderValue.Any);
                    concurrentUpdates.Add(HttpSendAsync(concurrentRequest));
                }
                await Task.WhenAll(concurrentUpdates);

                IEnumerable <HttpResponseMessage> concurrentResponses = concurrentUpdates.Select(update => update.Result);
                foreach (HttpResponseMessage concurrentResponse in concurrentResponses)
                {
                    // NoContent is the expected success case.
                    // PreConditionFailed can happen due to race condition between LibGit2Sharp cleanup and lock acquisition and release.
                    // In PreConditionFailed case, nothing is written and the repo isn't updated or corrupted.
                    // This is an edge case for a legacy feature
                    Assert.True(
                        concurrentResponse.StatusCode == HttpStatusCode.NoContent ||
                        concurrentResponse.StatusCode == HttpStatusCode.PreconditionFailed,
                        $"Status code expected to be either {HttpStatusCode.NoContent} or {HttpStatusCode.PreconditionFailed} but got {concurrentResponse.StatusCode}");
                }

                TestTracer.Trace("==== Check that 'nodeploy' doesn't deploy and that the old content remains.");
                using (HttpRequestMessage request = new HttpRequestMessage())
                {
                    request.Method     = HttpMethod.Put;
                    request.RequestUri = new Uri(fileAddress + "?nodeploy");
                    request.Headers.IfMatch.Add(EntityTagHeaderValue.Any);
                    request.Content = CreateUploadContent(_fileContent2);

                    response = await HttpSendAsync(request);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent1);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;
                }

                TestTracer.Trace("==== Check passing custom commit message.");
                using (HttpRequestMessage request = new HttpRequestMessage())
                {
                    request.Method     = HttpMethod.Put;
                    request.RequestUri = new Uri(fileAddress + "?message=helloworld");
                    request.Headers.IfMatch.Add(EntityTagHeaderValue.Any);
                    request.Content = CreateUploadContent(_fileContent3);

                    response = await HttpSendAsync(request);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent3);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;
                }

                TestTracer.Trace("==== Check that custom deployment script works");
                using (HttpRequestMessage update7 = new HttpRequestMessage())
                {
                    // Upload custom deployment scripts
                    TestTracer.Trace("==== Upload custom deployment scripts");
                    updatedEtag = await UploadCustomDeploymentScripts();

                    // Upload content and validate that it gets deployed
                    TestTracer.Trace("==== Upload content and validate that it gets deployed");
                    update7.Method     = HttpMethod.Put;
                    update7.RequestUri = new Uri(fileAddress);
                    update7.Headers.IfMatch.Add(updatedEtag);
                    update7.Content = CreateUploadContent(_fileContent2);

                    response = await HttpSendAsync(update7);
                    await VerifyDeploymentAsync(customDeploymentFileAddress, HttpStatusCode.OK, _fileContent2);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;

                    // Remove custom deployment scripts
                    TestTracer.Trace("==== Remove custom deployment scripts");
                    updatedEtag = await RemoveCustomDeploymentScripts();
                }

                // Check that delete with invalid etag fails
                TestTracer.Trace("==== Check that delete with invalid etag fails");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(new EntityTagHeaderValue("\"invalidetag\""));

                    response = await HttpSendAsync(deleteRequest);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent2);

                    Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                }

                // Check that delete with conflict fails
                TestTracer.Trace("==== Check that delete with conflict fails");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(originalEtag);

                    response = await HttpSendAsync(deleteRequest);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.OK, _fileContent2);

                    Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);
                }

                // Check that delete with valid etag succeeds
                TestTracer.Trace("==== Check that delete with valid etag succeeds");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(updatedEtag);

                    response = await HttpSendAsync(deleteRequest);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.NotFound, null);

                    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                }

                // Check that 2nd delete attempt fails
                TestTracer.Trace("==== Check that 2nd delete attempt fails");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(updatedEtag);

                    response = await HttpSendAsync(deleteRequest);
                    await VerifyDeploymentAsync(deploymentFileAddress, HttpStatusCode.NotFound, null);

                    Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
                }
            }
            else
            {
                // Check that update with correct etag generates 204 Response with new etag
                TestTracer.Trace("==== Check that update with correct etag generates 204 Response with new etag");
                using (HttpRequestMessage updateRequest = new HttpRequestMessage())
                {
                    updateRequest.Method     = HttpMethod.Put;
                    updateRequest.RequestUri = new Uri(fileAddress);
                    updateRequest.Headers.IfMatch.Add(originalEtag);
                    updateRequest.Content = CreateUploadContent(_fileContent1);
                    response = await HttpSendAsync(updateRequest);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;
                }

                // Check that 2nd create attempt fails
                TestTracer.Trace("==== Check that 2nd create attempt fails");
                using (HttpRequestMessage updateRequest = new HttpRequestMessage())
                {
                    updateRequest.Method     = HttpMethod.Put;
                    updateRequest.RequestUri = new Uri(fileAddress);
                    updateRequest.Headers.IfMatch.Add(originalEtag);
                    updateRequest.Content = CreateUploadContent(_fileContent2);
                    response = await HttpSendAsync(updateRequest);

                    Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                }

                // Check that update with invalid etag fails
                TestTracer.Trace("==== Check that update with invalid etag fails");
                using (HttpRequestMessage updateRequest = new HttpRequestMessage())
                {
                    updateRequest.Method     = HttpMethod.Put;
                    updateRequest.RequestUri = new Uri(fileAddress);
                    updateRequest.Headers.IfMatch.Add(new EntityTagHeaderValue("\"invalidetag\""));
                    updateRequest.Content = CreateUploadContent(_fileContent1);
                    response = await HttpSendAsync(updateRequest);

                    Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                }

                // Check that update with wildcard etag succeeds
                TestTracer.Trace("==== Check that update with wildcard etag succeeds");
                using (HttpRequestMessage updateRequest = new HttpRequestMessage())
                {
                    updateRequest.Method     = HttpMethod.Put;
                    updateRequest.RequestUri = new Uri(fileAddress);
                    updateRequest.Headers.IfMatch.Add(EntityTagHeaderValue.Any);
                    updateRequest.Content = CreateUploadContent(_fileContent1);
                    response = await HttpSendAsync(updateRequest);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;
                }

                // Check that delete with invalid etag fails
                TestTracer.Trace("==== Check that delete with invalid etag fails");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(new EntityTagHeaderValue("\"invalidetag\""));
                    response = await HttpSendAsync(deleteRequest);

                    Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                }

                // Check that delete with valid etag succeeds
                TestTracer.Trace("==== Check that delete with valid etag succeeds");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(updatedEtag);
                    response = await HttpSendAsync(deleteRequest);

                    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                }

                // Check that 2nd delete attempt fails
                TestTracer.Trace("==== Check that 2nd delete attempt fails");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(updatedEtag);
                    response = await HttpSendAsync(deleteRequest);

                    Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
                }

                // Check that we can delete an empty directory
                TestTracer.Trace("==== Check that we can delete an empty directory");
                response = await HttpDeleteAsync(dirAddress);

                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
            }
        }
Example #7
0
        public async Task RunIntegrationTest()
        {
            string dir        = Guid.NewGuid().ToString("N");
            string dirAddress = BaseAddress + _segmentDelimiter + dir;
            string dirAddressWithTerminatingSlash = dirAddress + _segmentDelimiter;

            string file        = Guid.NewGuid().ToString("N") + ".txt";
            string fileAddress = dirAddressWithTerminatingSlash + file;
            string fileAddressWithTerminatingSlash = fileAddress + _segmentDelimiter;

            string deploymentFileAddress       = null;
            string customDeploymentFileAddress = null;

            if (DeploymentClient != null)
            {
                deploymentFileAddress       = string.Format("{0}{1}site{1}wwwroot{1}{2}{1}{3}", DeploymentBaseAddress, _segmentDelimiter, dir, file);
                customDeploymentFileAddress = string.Format("{0}{1}site{1}wwwroot{1}test{1}{2}{1}{3}", DeploymentBaseAddress, _segmentDelimiter, dir, file);
            }

            TestTracer.Trace("Starting RunIntegrationTest");
            TestTracer.Trace("Dir - {0}", dirAddress);
            TestTracer.Trace("File - {0}", fileAddress);
            TestTracer.Trace("DeploymentFileAddress - {0}", deploymentFileAddress);

            HttpResponseMessage response;

            // Check not found file responses
            TestTracer.Trace("==== Check not found file responses");
            response = await HttpGetAsync(dirAddress);

            Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);

            response = await HttpGetAsync(dirAddressWithTerminatingSlash);

            Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);

            response = await HttpGetAsync(fileAddress);

            Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);

            response = await HttpGetAsync(fileAddressWithTerminatingSlash);

            Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);

            // Check create file results in 201 response with etag
            TestTracer.Trace("==== Check create file results in 201 response with etag");
            response = await HttpPutAsync(fileAddress, CreateUploadContent(_fileContent0));
            await VerifyDeployment(deploymentFileAddress, HttpStatusCode.OK, _fileContent0);

            Assert.Equal(HttpStatusCode.Created, response.StatusCode);
            EntityTagHeaderValue originalEtag = response.Headers.ETag;

            Assert.NotNull(originalEtag);

            // Check that we get a 200 (OK) on created file with the correct etag
            TestTracer.Trace("==== Check that we get a 200 (OK) on created file with the correct etag");
            response = await HttpGetAsync(fileAddress);

            Assert.Equal(HttpStatusCode.OK, response.StatusCode);
            Assert.Equal(originalEtag, response.Headers.ETag);
            Assert.Equal(_fileMediaType, response.Content.Headers.ContentType);

            // Check that we get a 200 (OK) on created file using HEAD with the correct etag
            TestTracer.Trace("==== Check that we get a 200 (OK) on created file using HEAD with the correct etag");
            using (HttpRequestMessage headReq = new HttpRequestMessage())
            {
                headReq.Method     = HttpMethod.Head;
                headReq.RequestUri = new Uri(fileAddress);
                response           = await Client.SendAsync(headReq);

                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                Assert.Equal(originalEtag, response.Headers.ETag);
                Assert.Equal(_fileMediaType, response.Content.Headers.ContentType);
            }

            // Check that we get a 304 (Not Modified) response if matching If-None-Match
            TestTracer.Trace("==== Check that we get a 304 (Not Modified) response if matching If-None-Match");
            using (HttpRequestMessage ifNoneMatchReq = new HttpRequestMessage())
            {
                ifNoneMatchReq.RequestUri = new Uri(fileAddress);
                ifNoneMatchReq.Headers.IfNoneMatch.Add(originalEtag);
                response = await HttpSendAsync(ifNoneMatchReq);

                Assert.Equal(HttpStatusCode.NotModified, response.StatusCode);
                Assert.Equal(originalEtag, response.Headers.ETag);
            }

            // Check that we get a 200 (OK) response if not matching If-None-Match
            TestTracer.Trace("==== Check that we get a 200 (OK) response if not matching If-None-Match");
            using (HttpRequestMessage ifNoneMatchReqBadEtag = new HttpRequestMessage())
            {
                ifNoneMatchReqBadEtag.RequestUri = new Uri(fileAddress);
                ifNoneMatchReqBadEtag.Headers.IfNoneMatch.Add(new EntityTagHeaderValue("\"NotMatching\""));
                response = await HttpSendAsync(ifNoneMatchReqBadEtag);

                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                Assert.Equal(originalEtag, response.Headers.ETag);
            }

            // Check that If-Range request with range returns 206 (Partial Content)
            TestTracer.Trace("==== Check that If-Range request with range returns 206 (Partial Content)");
            using (HttpRequestMessage ifRangeReq = new HttpRequestMessage())
            {
                ifRangeReq.RequestUri      = new Uri(fileAddress);
                ifRangeReq.Headers.IfRange = new RangeConditionHeaderValue(originalEtag);
                ifRangeReq.Headers.Range   = new RangeHeaderValue(0, 0)
                {
                    Unit = "bytes"
                };
                response = await HttpSendAsync(ifRangeReq);

                Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
                Assert.Equal(originalEtag, response.Headers.ETag);
                Assert.Equal(1, response.Content.Headers.ContentLength);
                Assert.Equal(new ContentRangeHeaderValue(0, 0, _fileContent0.Length), response.Content.Headers.ContentRange);
            }

            // Check that If-Range request with no range returns 200 (OK)
            TestTracer.Trace("==== Check that If-Range request with no range returns 200 (OK)");
            using (HttpRequestMessage ifRangeReqNoRange = new HttpRequestMessage())
            {
                ifRangeReqNoRange.RequestUri      = new Uri(fileAddress);
                ifRangeReqNoRange.Headers.IfRange = new RangeConditionHeaderValue(originalEtag);
                response = await HttpSendAsync(ifRangeReqNoRange);

                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                Assert.Equal(originalEtag, response.Headers.ETag);
            }

            // Check that If-Range request with bad range returns 416 (Requested Range Not Satisfiable)
            // including a Content-Range header
            TestTracer.Trace("==== Check that If-Range request with bad range returns 416 (Requested Range Not Satisfiable)");
            using (HttpRequestMessage ifRangeReqBadRange = new HttpRequestMessage())
            {
                ifRangeReqBadRange.RequestUri      = new Uri(fileAddress);
                ifRangeReqBadRange.Headers.IfRange = new RangeConditionHeaderValue(originalEtag);
                ifRangeReqBadRange.Headers.Range   = new RangeHeaderValue(100, 100)
                {
                    Unit = "bytes"
                };
                response = await HttpSendAsync(ifRangeReqBadRange);

                Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, response.StatusCode);
                Assert.Equal(_fileContentRange, response.Content.Headers.ContentRange);
            }

            // Check that we get a root directory view
            TestTracer.Trace("==== Check that we get a root directory view");
            response = await HttpGetAsync(BaseAddress);

            Assert.Equal(_dirMediaType, response.Content.Headers.ContentType);

            // Check that we get a directory view from folder
            TestTracer.Trace("==== Check that we get a directory view from folder");
            response = await HttpGetAsync(dirAddress);

            Assert.Equal(_dirMediaType, response.Content.Headers.ContentType);

            // Check various redirects between files and folders
            HttpClientHandler redirectHandler = HttpClientHelper.CreateClientHandler(BaseAddress, KuduClient.Credentials);

            redirectHandler.AllowAutoRedirect = false;
            using (HttpClient redirectClient = HttpClientHelper.CreateClient(BaseAddress, KuduClient.Credentials, redirectHandler))
            {
                // Ensure that requests to root without slash is redirected to one with slash
                TestTracer.Trace("==== Ensure that requests to root without slash is redirected to one with slash");
                response = await HttpGetAsync(BaseAddress, redirectClient);

                Assert.Equal(HttpStatusCode.TemporaryRedirect, response.StatusCode);
                Assert.Equal(BaseAddress + _segmentDelimiter, response.Headers.Location.AbsoluteUri);

                // Ensure that requests to directory without slash is redirected to one with slash
                TestTracer.Trace("==== Ensure that requests to directory without slash is redirected to one with slash");
                response = await HttpGetAsync(dirAddress, redirectClient);

                Assert.Equal(HttpStatusCode.TemporaryRedirect, response.StatusCode);
                Assert.Equal(dirAddressWithTerminatingSlash, response.Headers.Location.AbsoluteUri);

                // Ensure that requests to file with slash is redirected to one without slash
                TestTracer.Trace("==== Ensure that requests to file with slash is redirected to one without slash");
                response = await HttpGetAsync(fileAddressWithTerminatingSlash, redirectClient);

                Assert.Equal(HttpStatusCode.TemporaryRedirect, response.StatusCode);
                Assert.Equal(fileAddress, response.Headers.Location.AbsoluteUri);
            }

            // Check that 2nd create attempt fails
            TestTracer.Trace("==== Check that 2nd create attempt fails");
            response = await HttpPutAsync(fileAddress, CreateUploadContent(_fileContent0));

            Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
            Assert.Equal(originalEtag, response.Headers.ETag);

            // Check that we can't update a directory
            TestTracer.Trace("==== Check that we can't update a directory");
            response = await HttpPutAsync(dirAddress, CreateUploadContent(_fileContent0));

            Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);

            // Check that we can't delete a non-empty directory
            TestTracer.Trace("==== Check that we can't delete a non-empty directory");
            response = await HttpDeleteAsync(dirAddress);

            Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);

            EntityTagHeaderValue updatedEtag;

            if (_testConflictingUpdates)
            {
                // Update file with first edit based on original etag
                TestTracer.Trace("==== Update file with first edit based on original etag");
                using (HttpRequestMessage update1 = new HttpRequestMessage())
                {
                    update1.Method     = HttpMethod.Put;
                    update1.RequestUri = new Uri(fileAddress);
                    update1.Headers.IfMatch.Add(originalEtag);
                    update1.Content = CreateUploadContent(_fileContent1);

                    response = await HttpSendAsync(update1);
                    await VerifyDeployment(deploymentFileAddress, HttpStatusCode.OK, _fileContent1);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;
                }

                // Update file with second edit based on original etag (non-conflicting merge)
                TestTracer.Trace("==== Update file with second edit based on original etag (non-conflicting merge)");
                using (HttpRequestMessage update2 = new HttpRequestMessage())
                {
                    update2.Method     = HttpMethod.Put;
                    update2.RequestUri = new Uri(fileAddress);
                    update2.Headers.IfMatch.Add(originalEtag);
                    update2.Content = CreateUploadContent(_fileContent2);

                    response = await HttpSendAsync(update2);
                    await VerifyDeployment(deploymentFileAddress, HttpStatusCode.OK, _fileContent3);

                    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(updatedEtag, response.Headers.ETag);
                    Assert.Equal(_fileMediaType, response.Content.Headers.ContentType);
                    updatedEtag = response.Headers.ETag;
                }

                // Update file with third edit based on original etag (non-conflicting merge)
                TestTracer.Trace("==== Update file with third edit based on original etag (non-conflicting merge)");
                using (HttpRequestMessage update3 = new HttpRequestMessage())
                {
                    update3.Method     = HttpMethod.Put;
                    update3.RequestUri = new Uri(fileAddress);
                    update3.Headers.IfMatch.Add(originalEtag);
                    update3.Content = CreateUploadContent(_fileContent3);

                    response = await HttpSendAsync(update3);
                    await VerifyDeployment(deploymentFileAddress, HttpStatusCode.OK, _fileContent3);

                    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                    Assert.Equal(_fileMediaType, response.Content.Headers.ContentType);
                    updatedEtag = response.Headers.ETag;
                }

                // Update file with forth edit based on original etag (conflicting)
                TestTracer.Trace("==== Update file with forth edit based on original etag (conflicting)");
                using (HttpRequestMessage update4 = new HttpRequestMessage())
                {
                    update4.Method     = HttpMethod.Put;
                    update4.RequestUri = new Uri(fileAddress);
                    update4.Headers.IfMatch.Add(originalEtag);
                    update4.Content = CreateUploadContent(_fileContent4);

                    response = await HttpSendAsync(update4);
                    await VerifyDeployment(deploymentFileAddress, HttpStatusCode.OK, _fileContent3);

                    Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);
                    Assert.Equal(_conflictMediaType, response.Content.Headers.ContentType);
                    Assert.Null(response.Headers.ETag);
                    string content = await response.Content.ReadAsStringAsync();

                    Assert.True(content.StartsWith(_conflict));
                }

                // The previous conflict results in a git cleanup which at times takes time. During this interval the server responds with ServerUnavailable.
                // To work aroudn this, we'll simply add a bit of sleep timing.
                Thread.Sleep(TimeSpan.FromSeconds(3));

                // Update file with fifth edit based on invalid etag
                TestTracer.Trace("==== Update file with fifth edit based on invalid etag");
                using (HttpRequestMessage update5 = new HttpRequestMessage())
                {
                    update5.Method     = HttpMethod.Put;
                    update5.RequestUri = new Uri(fileAddress);
                    update5.Headers.IfMatch.Add(new EntityTagHeaderValue("\"invalidetag\""));
                    update5.Content = CreateUploadContent(_fileContent1);

                    response = await HttpSendAsync(update5);
                    await VerifyDeployment(deploymentFileAddress, HttpStatusCode.OK, _fileContent3);

                    Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                }

                // Check that update with wildcard etag succeeds
                TestTracer.Trace("==== Check that update with wildcard etag succeeds");
                using (HttpRequestMessage update6 = new HttpRequestMessage())
                {
                    update6.Method     = HttpMethod.Put;
                    update6.RequestUri = new Uri(fileAddress);
                    update6.Headers.IfMatch.Add(EntityTagHeaderValue.Any);
                    update6.Content = CreateUploadContent(_fileContent1);

                    response = await HttpSendAsync(update6);
                    await VerifyDeployment(deploymentFileAddress, HttpStatusCode.OK, _fileContent1);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;
                }

                // Check that custom deployment script works
                using (HttpRequestMessage update7 = new HttpRequestMessage())
                {
                    // Upload custom deployment scripts
                    TestTracer.Trace("==== Upload custom deployment scripts");
                    updatedEtag = await UploadCustomDeploymentScripts();

                    // Upload content and validate that it gets deployed
                    TestTracer.Trace("==== Upload content and validate that it gets deployed");
                    update7.Method     = HttpMethod.Put;
                    update7.RequestUri = new Uri(fileAddress);
                    update7.Headers.IfMatch.Add(updatedEtag);
                    update7.Content = CreateUploadContent(_fileContent2);

                    response = await HttpSendAsync(update7);
                    await VerifyDeployment(customDeploymentFileAddress, HttpStatusCode.OK, _fileContent2);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;

                    // Remove custom deployment scripts
                    TestTracer.Trace("==== Remove custom deployment scripts");
                    updatedEtag = await RemoveCustomDeploymentScripts();
                }

                // Check that delete with invalid etag fails
                TestTracer.Trace("==== Check that delete with invalid etag fails");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(new EntityTagHeaderValue("\"invalidetag\""));

                    response = await HttpSendAsync(deleteRequest);
                    await VerifyDeployment(deploymentFileAddress, HttpStatusCode.OK, _fileContent2);

                    Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                }

                // Check that delete with conflict fails
                TestTracer.Trace("==== Check that delete with conflict fails");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(originalEtag);

                    response = await HttpSendAsync(deleteRequest);
                    await VerifyDeployment(deploymentFileAddress, HttpStatusCode.OK, _fileContent2);

                    Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);
                }

                // Check that delete with valid etag succeeds
                TestTracer.Trace("==== Check that delete with valid etag succeeds");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(updatedEtag);

                    response = await HttpSendAsync(deleteRequest);
                    await VerifyDeployment(deploymentFileAddress, HttpStatusCode.NotFound, null);

                    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                }

                // Check that 2nd delete attempt fails
                TestTracer.Trace("==== Check that 2nd delete attempt fails");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(updatedEtag);

                    response = await HttpSendAsync(deleteRequest);
                    await VerifyDeployment(deploymentFileAddress, HttpStatusCode.NotFound, null);

                    Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
                }
            }
            else
            {
                // Check that update with correct etag generates 204 Response with new etag
                TestTracer.Trace("==== Check that update with correct etag generates 204 Response with new etag");
                using (HttpRequestMessage updateRequest = new HttpRequestMessage())
                {
                    updateRequest.Method     = HttpMethod.Put;
                    updateRequest.RequestUri = new Uri(fileAddress);
                    updateRequest.Headers.IfMatch.Add(originalEtag);
                    updateRequest.Content = CreateUploadContent(_fileContent1);
                    response = await HttpSendAsync(updateRequest);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;
                }

                // Check that 2nd create attempt fails
                TestTracer.Trace("==== Check that 2nd create attempt fails");
                using (HttpRequestMessage updateRequest = new HttpRequestMessage())
                {
                    updateRequest.Method     = HttpMethod.Put;
                    updateRequest.RequestUri = new Uri(fileAddress);
                    updateRequest.Headers.IfMatch.Add(originalEtag);
                    updateRequest.Content = CreateUploadContent(_fileContent2);
                    response = await HttpSendAsync(updateRequest);

                    Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                }

                // Check that update with invalid etag fails
                TestTracer.Trace("==== Check that update with invalid etag fails");
                using (HttpRequestMessage updateRequest = new HttpRequestMessage())
                {
                    updateRequest.Method     = HttpMethod.Put;
                    updateRequest.RequestUri = new Uri(fileAddress);
                    updateRequest.Headers.IfMatch.Add(new EntityTagHeaderValue("\"invalidetag\""));
                    updateRequest.Content = CreateUploadContent(_fileContent1);
                    response = await HttpSendAsync(updateRequest);

                    Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                }

                // Check that update with wildcard etag succeeds
                TestTracer.Trace("==== Check that update with wildcard etag succeeds");
                using (HttpRequestMessage updateRequest = new HttpRequestMessage())
                {
                    updateRequest.Method     = HttpMethod.Put;
                    updateRequest.RequestUri = new Uri(fileAddress);
                    updateRequest.Headers.IfMatch.Add(EntityTagHeaderValue.Any);
                    updateRequest.Content = CreateUploadContent(_fileContent1);
                    response = await HttpSendAsync(updateRequest);

                    Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
                    Assert.NotNull(response.Headers.ETag);
                    Assert.NotEqual(originalEtag, response.Headers.ETag);
                    updatedEtag = response.Headers.ETag;
                }

                // Check that delete with invalid etag fails
                TestTracer.Trace("==== Check that delete with invalid etag fails");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(new EntityTagHeaderValue("\"invalidetag\""));
                    response = await HttpSendAsync(deleteRequest);

                    Assert.Equal(HttpStatusCode.PreconditionFailed, response.StatusCode);
                    Assert.Equal(updatedEtag, response.Headers.ETag);
                }

                // Check that delete with valid etag succeeds
                TestTracer.Trace("==== Check that delete with valid etag succeeds");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(updatedEtag);
                    response = await HttpSendAsync(deleteRequest);

                    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                }

                // Check that 2nd delete attempt fails
                TestTracer.Trace("==== Check that 2nd delete attempt fails");
                using (HttpRequestMessage deleteRequest = new HttpRequestMessage())
                {
                    deleteRequest.Method     = HttpMethod.Delete;
                    deleteRequest.RequestUri = new Uri(fileAddress);
                    deleteRequest.Headers.IfMatch.Add(updatedEtag);
                    response = await HttpSendAsync(deleteRequest);

                    Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
                }

                // Check that we can delete an empty directory
                TestTracer.Trace("==== Check that we can delete an empty directory");
                response = await HttpDeleteAsync(dirAddress);

                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
            }
        }