diff --git a/Octokit.Tests/Clients/TeamsClientTests.cs b/Octokit.Tests/Clients/TeamsClientTests.cs index 52fbe985a8..3ca76d5f8a 100644 --- a/Octokit.Tests/Clients/TeamsClientTests.cs +++ b/Octokit.Tests/Clients/TeamsClientTests.cs @@ -152,6 +152,22 @@ public async Task RequestsTheCorrectUrl() Args.Object); } + [Fact] + public async Task AllowsEmptyBody() + { + var connection = Substitute.For(); + + var apiConnection = new ApiConnection(connection); + + var client = new TeamsClient(apiConnection); + + await client.AddMembership(1, "user"); + + connection.Received().Put>( + Arg.Is(u => u.ToString() == "teams/1/memberships/user"), + Arg.Is(u => u == RequestBody.Empty)); + } + [Fact] public async Task EnsuresNonNullOrEmptyLogin() { diff --git a/Octokit.Tests/Http/ConnectionTests.cs b/Octokit.Tests/Http/ConnectionTests.cs index 02f1601ee7..eebc34d821 100644 --- a/Octokit.Tests/Http/ConnectionTests.cs +++ b/Octokit.Tests/Http/ConnectionTests.cs @@ -347,7 +347,8 @@ public class ThePutMethod [Fact] public async Task MakesPutRequestWithData() { - string data = SimpleJson.SerializeObject(new object()); + var body = new object(); + var expectedBody = SimpleJson.SerializeObject(body); var httpClient = Substitute.For(); IResponse response = new Response(); httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); @@ -357,20 +358,46 @@ public async Task MakesPutRequestWithData() httpClient, Substitute.For()); - await connection.Put(new Uri("endpoint", UriKind.Relative), new object()); + await connection.Put(new Uri("endpoint", UriKind.Relative), body); httpClient.Received(1).Send(Arg.Is(req => req.BaseAddress == _exampleUri && - (string)req.Body == data && + (string)req.Body == expectedBody && req.Method == HttpMethod.Put && req.ContentType == "application/x-www-form-urlencoded" && req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken); } + [Fact] + public async Task MakesPutRequestWithNoData() + { + var body = RequestBody.Empty; + var expectedBody = SimpleJson.SerializeObject(body); + var httpClient = Substitute.For(); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + var connection = new Connection(new ProductHeaderValue("OctokitTests"), + _exampleUri, + Substitute.For(), + httpClient, + Substitute.For()); + + await connection.Put(new Uri("endpoint", UriKind.Relative), body); + + Console.WriteLine(expectedBody); + + httpClient.Received(1).Send(Arg.Is(req => + req.BaseAddress == _exampleUri && + (string)req.Body == expectedBody && + req.Method == HttpMethod.Put && + req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken); + } + [Fact] public async Task MakesPutRequestWithDataAndTwoFactor() { - string data = SimpleJson.SerializeObject(new object()); + var body = new object(); + var expectedBody = SimpleJson.SerializeObject(body); var httpClient = Substitute.For(); IResponse response = new Response(); httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); @@ -380,16 +407,40 @@ public async Task MakesPutRequestWithDataAndTwoFactor() httpClient, Substitute.For()); - await connection.Put(new Uri("endpoint", UriKind.Relative), new object(), "two-factor"); + await connection.Put(new Uri("endpoint", UriKind.Relative), body, "two-factor"); httpClient.Received(1).Send(Arg.Is(req => req.BaseAddress == _exampleUri && - (string)req.Body == data && + (string)req.Body == expectedBody && req.Method == HttpMethod.Put && req.Headers["X-GitHub-OTP"] == "two-factor" && req.ContentType == "application/x-www-form-urlencoded" && req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken); } + + [Fact] + public async Task MakesPutRequestWithNoDataAndTwoFactor() + { + var body = RequestBody.Empty; + var expectedBody = SimpleJson.SerializeObject(body); + var httpClient = Substitute.For(); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + var connection = new Connection(new ProductHeaderValue("OctokitTests"), + _exampleUri, + Substitute.For(), + httpClient, + Substitute.For()); + + await connection.Put(new Uri("endpoint", UriKind.Relative), body, "two-factor"); + + httpClient.Received(1).Send(Arg.Is(req => + req.BaseAddress == _exampleUri && + (string)req.Body == expectedBody && + req.Method == HttpMethod.Put && + req.Headers["X-GitHub-OTP"] == "two-factor" && + req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken); + } } public class ThePostMethod diff --git a/Octokit/Clients/TeamsClient.cs b/Octokit/Clients/TeamsClient.cs index 4afab495d2..002358142f 100644 --- a/Octokit/Clients/TeamsClient.cs +++ b/Octokit/Clients/TeamsClient.cs @@ -164,7 +164,7 @@ public async Task AddMembership(int id, string login) try { - response = await ApiConnection.Put>(endpoint, null); + response = await ApiConnection.Put>(endpoint, RequestBody.Empty); } catch (NotFoundException) { diff --git a/Octokit/Http/RequestBody.cs b/Octokit/Http/RequestBody.cs new file mode 100644 index 0000000000..346e71d120 --- /dev/null +++ b/Octokit/Http/RequestBody.cs @@ -0,0 +1,12 @@ +namespace Octokit +{ + /// + /// Container for the static method that represents an + /// intentional empty request body to avoid overloading null. + /// + public static class RequestBody + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible")] + public static object Empty = new object(); + } +} \ No newline at end of file diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 64daa00563..8b5eedaca9 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -399,6 +399,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 9002587a63..a34a7b1ef9 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -415,6 +415,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 607ec59518..3f4b6f9cfa 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -408,6 +408,7 @@ + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 74f211dfd1..36e840bd46 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -398,6 +398,7 @@ + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 944bb68159..17d8bae84e 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -402,6 +402,7 @@ + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index 60a9607f7c..760b5bfe67 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -85,6 +85,7 @@ +