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); } }
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); } } }
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 } }
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)); } } }
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 } }
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); } }
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); } }