using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; using NSubstitute; using Octokit.Internal; using Xunit; using static Octokit.Internal.TestSetup; namespace Octokit.Tests.Http { public class ApiConnectionTests { public class TheGetMethod { [Fact] public async Task MakesGetRequestForItem() { var getUri = new Uri("anything", UriKind.Relative); IApiResponse response = new ApiResponse(CreateResponse(HttpStatusCode.OK)); var connection = Substitute.For(); connection.Get(Args.Uri, null).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.Get(getUri); Assert.Same(response.Body, data); var calls = connection.ReceivedCalls(); connection.Received().Get(getUri, null, null); } [Fact] public async Task MakesGetRequestForItemWithAcceptsOverride() { var getUri = new Uri("anything", UriKind.Relative); const string accepts = "custom/accepts"; IApiResponse response = new ApiResponse(CreateResponse(HttpStatusCode.OK)); var connection = Substitute.For(); connection.Get(Args.Uri, null, Args.String).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.Get(getUri, null, accepts); Assert.Same(response.Body, data); connection.Received().Get(getUri, null, accepts); } [Fact] public async Task EnsuresArgumentNotNull() { var getUri = new Uri("anything", UriKind.Relative); var client = new ApiConnection(Substitute.For()); await Assert.ThrowsAsync(() => client.Get(null)); await Assert.ThrowsAsync(() => client.Get(getUri, new Dictionary(), null)); } } public class TheGetHtmlMethod { [Fact] public async Task MakesHtmlRequest() { var getUri = new Uri("anything", UriKind.Relative); IApiResponse response = new ApiResponse(CreateResponse(HttpStatusCode.OK), ""); var connection = Substitute.For(); connection.GetHtml(Args.Uri, null).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.GetHtml(getUri); Assert.Equal("", data); connection.Received().GetHtml(getUri); } [Fact] public async Task EnsuresArgumentNotNull() { var client = new ApiConnection(Substitute.For()); await Assert.ThrowsAsync(() => client.GetHtml(null)); } } public class TheGetAllMethod { [Fact] public async Task MakesGetRequestForAllItems() { var getAllUri = new Uri("anything", UriKind.Relative); IApiResponse> response = new ApiResponse>( CreateResponse(HttpStatusCode.OK), new List { new object(), new object() }); var connection = Substitute.For(); connection.Get>(Args.Uri, Args.EmptyDictionary, null).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.GetAll(getAllUri); Assert.Empty(data); connection.Received().Get>(getAllUri, Args.EmptyDictionary, null, CancellationToken.None, null); } [Fact] public async Task EnsuresArgumentNotNull() { var client = new ApiConnection(Substitute.For()); // One argument await Assert.ThrowsAsync(() => client.GetAll(null)); // Two argument await Assert.ThrowsAsync(async () => await client.GetAll(null, new Dictionary())); // Three arguments await Assert.ThrowsAsync(async () => await client.GetAll(null, new Dictionary(), "accepts")); } } public class ThePatchMethod { [Fact] public async Task MakesPatchRequestWithSuppliedData() { var patchUri = new Uri("anything", UriKind.Relative); var sentData = new object(); IApiResponse response = new ApiResponse(CreateResponse(HttpStatusCode.OK), new object()); var connection = Substitute.For(); connection.Patch(Args.Uri, Args.Object).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.Patch(patchUri, sentData); Assert.Same(data, response.Body); connection.Received().Patch(patchUri, sentData); } [Fact] public async Task MakesPatchRequestWithAcceptsOverride() { var patchUri = new Uri("anything", UriKind.Relative); var sentData = new object(); var accepts = "custom/accepts"; IApiResponse response = new ApiResponse(CreateResponse(HttpStatusCode.OK)); var connection = Substitute.For(); connection.Patch(Args.Uri, Args.Object, Args.String).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.Patch(patchUri, sentData, accepts); Assert.Same(data, response.Body); connection.Received().Patch(patchUri, sentData, accepts); } [Fact] public async Task EnsuresArgumentNotNull() { var connection = new ApiConnection(Substitute.For()); var patchUri = new Uri("", UriKind.Relative); await Assert.ThrowsAsync(() => connection.Patch(null, new object())); await Assert.ThrowsAsync(() => connection.Patch(patchUri, null)); await Assert.ThrowsAsync(() => connection.Patch(patchUri, new object(), null)); } } public class ThePostMethod { [Fact] public async Task MakesPostRequestWithoutData() { var postUri = new Uri("anything", UriKind.Relative); var statusCode = HttpStatusCode.Accepted; var connection = Substitute.For(); connection.Post(Args.Uri).Returns(Task.FromResult(statusCode)); var apiConnection = new ApiConnection(connection); await apiConnection.Post(postUri); connection.Received().Post(postUri); } [Fact] public async Task MakesPostRequestWithSuppliedData() { var postUri = new Uri("anything", UriKind.Relative); var sentData = new object(); IApiResponse response = new ApiResponse(CreateResponse(HttpStatusCode.OK), new object()); var connection = Substitute.For(); connection.Post(Args.Uri, Args.Object, null, null).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.Post(postUri, sentData); Assert.Same(data, response.Body); connection.Received().Post(postUri, sentData, null, null); } [Fact] public async Task MakesUploadRequest() { var uploadUrl = new Uri("anything", UriKind.Relative); IApiResponse response = new ApiResponse(CreateResponse(HttpStatusCode.OK), "the response"); var connection = Substitute.For(); connection.Post(Args.Uri, Arg.Any(), Args.String, Args.String) .Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var rawData = new MemoryStream(); await apiConnection.Post(uploadUrl, rawData, "accepts", "content-type"); connection.Received().Post(uploadUrl, rawData, "accepts", "content-type"); } [Fact] public async Task EnsuresArgumentsNotNull() { var postUri = new Uri("", UriKind.Relative); var connection = new ApiConnection(Substitute.For()); // 1 parameter overload await Assert.ThrowsAsync(() => connection.Post(null)); // 2 parameter overload await Assert.ThrowsAsync(() => connection.Post(null, new object())); await Assert.ThrowsAsync(() => connection.Post(postUri, null)); // 3 parameters await Assert.ThrowsAsync(() => connection.Post(null, new MemoryStream(), "anAccept", "some-content-type")); await Assert.ThrowsAsync(() => connection.Post(postUri, null, "anAccept", "some-content-type")); } } public class ThePutMethod { [Fact] public async Task MakesPutRequestWithSuppliedData() { var putUri = new Uri("anything", UriKind.Relative); var sentData = new object(); IApiResponse response = new ApiResponse(CreateResponse(HttpStatusCode.OK)); var connection = Substitute.For(); connection.Put(Args.Uri, Args.Object).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.Put(putUri, sentData); Assert.Same(data, response.Body); connection.Received().Put(putUri, sentData); } [Fact] public async Task MakesPutRequestWithSuppliedDataAndTwoFactorCode() { var putUri = new Uri("anything", UriKind.Relative); var sentData = new object(); IApiResponse response = new ApiResponse(CreateResponse(HttpStatusCode.OK)); var connection = Substitute.For(); connection.Put(Args.Uri, Args.Object, "two-factor").Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.Put(putUri, sentData, "two-factor"); Assert.Same(data, response.Body); connection.Received().Put(putUri, sentData, "two-factor"); } [Fact] public async Task EnsuresArgumentsNotNull() { var putUri = new Uri("", UriKind.Relative); var connection = new ApiConnection(Substitute.For()); // 2 parameter overload await Assert.ThrowsAsync(async () => await connection.Put(null, new object())); await Assert.ThrowsAsync(async () => await connection.Put(putUri, null)); // 3 parameters await Assert.ThrowsAsync(async () => await connection.Put(null, new MemoryStream(), "two-factor")); await Assert.ThrowsAsync(async () => await connection.Put(putUri, null, "two-factor")); await Assert.ThrowsAsync(async () => await connection.Put(putUri, new MemoryStream(), null)); await Assert.ThrowsAsync(async () => await connection.Put(putUri, new MemoryStream(), "")); } } public class TheDeleteMethod { [Fact] public async Task MakesDeleteRequest() { var deleteUri = new Uri("anything", UriKind.Relative); HttpStatusCode statusCode = HttpStatusCode.NoContent; var connection = Substitute.For(); connection.Delete(Args.Uri).Returns(Task.FromResult(statusCode)); var apiConnection = new ApiConnection(connection); await apiConnection.Delete(deleteUri); connection.Received().Delete(deleteUri); } [Fact] public async Task EnsuresArgumentNotNull() { var connection = new ApiConnection(Substitute.For()); await Assert.ThrowsAsync(() => connection.Delete(null)); } } public class TheGetQueuedOperationMethod { [Fact] public async Task WhenGetReturnsNotOkOrAcceptedApiExceptionIsThrown() { var queuedOperationUrl = new Uri("anything", UriKind.Relative); IApiResponse response = new ApiResponse(CreateResponse(HttpStatusCode.PartialContent), new object()); var connection = Substitute.For(); connection.GetResponse(queuedOperationUrl, Args.CancellationToken).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); await Assert.ThrowsAsync(() => apiConnection.GetQueuedOperation(queuedOperationUrl, Args.CancellationToken)); } [Fact] public async Task WhenGetReturnsOkThenBodyAsObjectIsReturned() { var queuedOperationUrl = new Uri("anything", UriKind.Relative); var result = new[] { new object() }; var httpResponse = CreateResponse(HttpStatusCode.OK); IApiResponse> response = new ApiResponse>(httpResponse, result); var connection = Substitute.For(); connection.GetResponse>(queuedOperationUrl, Args.CancellationToken) .Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var actualResult = await apiConnection.GetQueuedOperation(queuedOperationUrl, CancellationToken.None); Assert.Same(actualResult, result); } [Fact] public async Task WhenGetReturnsNoContentThenReturnsEmptyCollection() { var queuedOperationUrl = new Uri("anything", UriKind.Relative); var result = new[] { new object() }; var httpResponse = CreateResponse(HttpStatusCode.NoContent); IApiResponse> response = new ApiResponse>( httpResponse, result); var connection = Substitute.For(); connection.GetResponse>(queuedOperationUrl, Args.CancellationToken) .Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var actualResult = await apiConnection.GetQueuedOperation(queuedOperationUrl, CancellationToken.None); Assert.Empty(actualResult); } [Fact] public async Task GetIsRepeatedUntilHttpStatusCodeOkIsReturned() { var queuedOperationUrl = new Uri("anything", UriKind.Relative); var result = new[] { new object() }; IApiResponse> firstResponse = new ApiResponse>(CreateResponse(HttpStatusCode.Accepted), result); IApiResponse> completedResponse = new ApiResponse>(CreateResponse(HttpStatusCode.OK), result); var connection = Substitute.For(); connection.GetResponse>(queuedOperationUrl, Args.CancellationToken) .Returns(x => Task.FromResult(firstResponse), x => Task.FromResult(firstResponse), x => Task.FromResult(completedResponse)); var apiConnection = new ApiConnection(connection); await apiConnection.GetQueuedOperation(queuedOperationUrl, CancellationToken.None); connection.Received(3).GetResponse>(queuedOperationUrl, Args.CancellationToken); } [Fact(Skip = "Test triggers deadlock when run, needs to be investigated")] public async Task CanCancelQueuedOperation() { var queuedOperationUrl = new Uri("anything", UriKind.Relative); var result = new[] { new object() }; IApiResponse> accepted = new ApiResponse>(CreateResponse(HttpStatusCode.Accepted), result); var connection = Substitute.For(); connection.GetResponse>(queuedOperationUrl, Args.CancellationToken) .Returns(x => Task.FromResult(accepted)); var apiConnection = new ApiConnection(connection); var cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource.CancelAfter(100); var canceled = false; var operationResult = await apiConnection.GetQueuedOperation(queuedOperationUrl, cancellationTokenSource.Token) .ContinueWith(task => { canceled = task.IsCanceled; return task; }, TaskContinuationOptions.OnlyOnCanceled) .ContinueWith(task => task, TaskContinuationOptions.OnlyOnFaulted); Assert.True(canceled); Assert.Null(operationResult); } [Fact] public async Task EnsuresArgumentNotNull() { var connection = new ApiConnection(Substitute.For()); await Assert.ThrowsAsync(() => connection.GetQueuedOperation(null, CancellationToken.None)); } } public class TheCtor { [Fact] public void EnsuresNonNullArguments() { Assert.Throws(() => new ApiConnection(null)); } } } }