diff --git a/Octokit.Reactive/Clients/IObservableDeploymentsClient.cs b/Octokit.Reactive/Clients/IObservableDeploymentsClient.cs index 3a77e846..23fbee4a 100644 --- a/Octokit.Reactive/Clients/IObservableDeploymentsClient.cs +++ b/Octokit.Reactive/Clients/IObservableDeploymentsClient.cs @@ -16,6 +16,19 @@ namespace Octokit.Reactive /// All the s for the specified repository. IObservable GetAll(string owner, string name); + /// + /// Gets all the deployments for the specified repository. Any user with pull access + /// to a repository can view deployments. + /// + /// + /// http://developer.github.com/v3/repos/deployments/#list-deployments + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// All the s for the specified repository. + IObservable GetAll(string owner, string name, ApiOptions options); + /// /// Creates a new deployment for the specified repository. /// Users with push access can create a deployment for a given ref. diff --git a/Octokit.Reactive/Clients/ObservableDeploymentsClient.cs b/Octokit.Reactive/Clients/ObservableDeploymentsClient.cs index 3d2ab9f1..9a32ace1 100644 --- a/Octokit.Reactive/Clients/ObservableDeploymentsClient.cs +++ b/Octokit.Reactive/Clients/ObservableDeploymentsClient.cs @@ -34,8 +34,28 @@ namespace Octokit.Reactive.Clients Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); + return GetAll(owner, name, ApiOptions.None); + } + + /// + /// Gets all the deployments for the specified repository. Any user with pull access + /// to a repository can view deployments. + /// + /// + /// http://developer.github.com/v3/repos/deployments/#list-deployments + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// All the s for the specified repository. + public IObservable GetAll(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + return _connection.GetAndFlattenAllPages( - ApiUrls.Deployments(owner, name)); + ApiUrls.Deployments(owner, name), options); } /// diff --git a/Octokit.Tests.Integration/Clients/DeploymentsClientTests.cs b/Octokit.Tests.Integration/Clients/DeploymentsClientTests.cs index af308d6f..8cc015ff 100644 --- a/Octokit.Tests.Integration/Clients/DeploymentsClientTests.cs +++ b/Octokit.Tests.Integration/Clients/DeploymentsClientTests.cs @@ -1,9 +1,10 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Octokit; using Octokit.Tests.Integration; -using Xunit; using Octokit.Tests.Integration.Helpers; +using Xunit; public class DeploymentsClientTests : IDisposable { @@ -40,6 +41,40 @@ public class DeploymentsClientTests : IDisposable _commit = github.Git.Commit.Create(_context.RepositoryOwner, _context.RepositoryName, newCommit).Result; } + private IEnumerable CreateCommits(int commitCount) + { + var github = Helper.GetAuthenticatedClient(); + + var list = new List(); + + for (int i = 0; i < commitCount; i++) + { + var blob = new NewBlob + { + Content = string.Format("Hello World {0}!", i), + Encoding = EncodingType.Utf8 + }; + + var blobResult = github.Git.Blob.Create(_context.RepositoryOwner, _context.RepositoryName, blob).Result; + + var newTree = new NewTree(); + newTree.Tree.Add(new NewTreeItem + { + Type = TreeType.Blob, + Mode = FileMode.File, + Path = "README.md", + Sha = blobResult.Sha + }); + + var treeResult = github.Git.Tree.Create(_context.RepositoryOwner, _context.RepositoryName, newTree).Result; + var newCommit = new NewCommit("test-commit", treeResult.Sha); + var commit = github.Git.Commit.Create(_context.RepositoryOwner, _context.RepositoryName, newCommit).Result; + list.Add(commit); + } + + return list; + } + [IntegrationTest] public async Task CanCreateDeployment() { @@ -51,7 +86,7 @@ public class DeploymentsClientTests : IDisposable } [IntegrationTest] - public async Task CanGetDeployments() + public async Task ReturnsDeployments() { var newDeployment = new NewDeployment(_commit.Sha) { AutoMerge = false }; await _deploymentsClient.Create(_context.RepositoryOwner, _context.RepositoryName, newDeployment); @@ -61,6 +96,81 @@ public class DeploymentsClientTests : IDisposable Assert.NotEmpty(deployments); } + [IntegrationTest] + public async Task ReturnsCorrectCountOfDeploymentsWithoutStart() + { + var commits = CreateCommits(6); + foreach (var commit in commits) + { + var newDeployment = new NewDeployment(commit.Sha) { AutoMerge = false }; + await _deploymentsClient.Create(_context.RepositoryOwner, _context.RepositoryName, newDeployment); + } + + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var releases = await _deploymentsClient.GetAll(_context.RepositoryOwner, _context.RepositoryName, options); + + Assert.Equal(5, releases.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfDeploymentsWithStart() + { + var commits = CreateCommits(6); + foreach (var commit in commits) + { + var newDeployment = new NewDeployment(commit.Sha) { AutoMerge = false }; + await _deploymentsClient.Create(_context.RepositoryOwner, _context.RepositoryName, newDeployment); + } + + var options = new ApiOptions + { + PageSize = 3, + PageCount = 1, + StartPage = 2 + }; + + var releases = await _deploymentsClient.GetAll(_context.RepositoryOwner, _context.RepositoryName, options); + + Assert.Equal(3, releases.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var commits = CreateCommits(6); + foreach (var commit in commits) + { + var newDeployment = new NewDeployment(commit.Sha) { AutoMerge = false }; + await _deploymentsClient.Create(_context.RepositoryOwner, _context.RepositoryName, newDeployment); + } + + var startOptions = new ApiOptions + { + PageSize = 3, + PageCount = 1 + }; + + var firstPage = await _deploymentsClient.GetAll(_context.RepositoryOwner, _context.RepositoryName, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 3, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _deploymentsClient.GetAll(_context.RepositoryOwner, _context.RepositoryName, skipStartOptions); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + Assert.NotEqual(firstPage[1].Id, secondPage[1].Id); + Assert.NotEqual(firstPage[2].Id, secondPage[2].Id); + } + public void Dispose() { _context.Dispose(); diff --git a/Octokit.Tests/Clients/DeploymentsClientTests.cs b/Octokit.Tests/Clients/DeploymentsClientTests.cs index 3eb59569..804835cc 100644 --- a/Octokit.Tests/Clients/DeploymentsClientTests.cs +++ b/Octokit.Tests/Clients/DeploymentsClientTests.cs @@ -2,19 +2,24 @@ using System.Threading.Tasks; using NSubstitute; using Octokit; +using Octokit.Tests; using Xunit; public class DeploymentsClientTests { public class TheGetAllMethod { + private const string name = "name"; + private const string owner = "owner"; + [Fact] public async Task EnsuresNonNullArguments() { var client = new DeploymentsClient(Substitute.For()); - await Assert.ThrowsAsync(() => client.GetAll(null, "name")); - await Assert.ThrowsAsync(() => client.GetAll("owner", null)); + await Assert.ThrowsAsync(() => client.GetAll(null, name)); + await Assert.ThrowsAsync(() => client.GetAll(owner, null)); + await Assert.ThrowsAsync(() => client.GetAll(owner, name, null)); } [Fact] @@ -22,8 +27,8 @@ public class DeploymentsClientTests { var client = new DeploymentsClient(Substitute.For()); - await Assert.ThrowsAsync(() => client.GetAll("", "name")); - await Assert.ThrowsAsync(() => client.GetAll("owner", "")); + await Assert.ThrowsAsync(() => client.GetAll("", name)); + await Assert.ThrowsAsync(() => client.GetAll(owner, "")); } [Theory] @@ -36,8 +41,8 @@ public class DeploymentsClientTests { var client = new DeploymentsClient(Substitute.For()); - await Assert.ThrowsAsync(() => client.GetAll(whitespace, "name")); - await Assert.ThrowsAsync(() => client.GetAll("owner", whitespace)); + await Assert.ThrowsAsync(() => client.GetAll(whitespace, name)); + await Assert.ThrowsAsync(() => client.GetAll(owner, whitespace)); } [Fact] @@ -45,16 +50,36 @@ public class DeploymentsClientTests { var connection = Substitute.For(); var client = new DeploymentsClient(connection); - var expectedUrl = "repos/owner/name/deployments"; + var expectedUrl = string.Format("repos/{0}/{1}/deployments", owner, name); - client.GetAll("owner", "name"); - connection.Received(1).GetAll(Arg.Is(u => u.ToString() == expectedUrl)); + client.GetAll(owner, name); + connection.Received(1) + .GetAll(Arg.Is(u => u.ToString() == expectedUrl), Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new DeploymentsClient(connection); + var expectedUrl = string.Format("repos/{0}/{1}/deployments", owner, name); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + client.GetAll(owner, name, options); + connection.Received(1) + .GetAll(Arg.Is(u => u.ToString() == expectedUrl), options); } } public class TheCreateMethod { - readonly NewDeployment newDeployment = new NewDeployment("aRef"); + private readonly NewDeployment newDeployment = new NewDeployment("aRef"); [Fact] public async Task EnsuresNonNullArguments() diff --git a/Octokit.Tests/Reactive/ObservableDeploymentsClientTests.cs b/Octokit.Tests/Reactive/ObservableDeploymentsClientTests.cs index 9005cc7e..477d1d78 100644 --- a/Octokit.Tests/Reactive/ObservableDeploymentsClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableDeploymentsClientTests.cs @@ -13,8 +13,10 @@ namespace Octokit.Tests.Reactive { public class TheGetAllMethod { - readonly IGitHubClient _githubClient; - readonly ObservableDeploymentsClient _client; + private readonly IGitHubClient _githubClient; + private readonly ObservableDeploymentsClient _client; + private const string owner = "owner"; + private const string name = "name"; public TheGetAllMethod() { @@ -25,43 +27,88 @@ namespace Octokit.Tests.Reactive [Fact] public void EnsuresNonNullArguments() { - Assert.Throws(() => _client.GetAll(null, "repo")); - Assert.Throws(() => _client.GetAll("owner", null)); + Assert.Throws(() => _client.GetAll(null, name)); + Assert.Throws(() => _client.GetAll(owner, null)); + Assert.Throws(() => _client.GetAll(owner, name, null)); } [Fact] public void EnsuresNonEmptyArguments() { - Assert.Throws(() => _client.GetAll("", "repo")); - Assert.Throws(() => _client.GetAll("owner", "")); + Assert.Throws(() => _client.GetAll("", name)); + Assert.Throws(() => _client.GetAll(owner, "")); } [Fact] public async Task EnsuresNonWhitespaceArguments() { await AssertEx.ThrowsWhenGivenWhitespaceArgument( - async whitespace => await _client.GetAll(whitespace, "repo")); + async whitespace => await _client.GetAll(whitespace, name)); await AssertEx.ThrowsWhenGivenWhitespaceArgument( - async whitespace => await _client.GetAll("owner", whitespace)); + async whitespace => await _client.GetAll(owner, whitespace)); } [Fact] - public void CallsDeploymentsUrl() + public void RequestsCorrectUrl() { - var expectedUri = ApiUrls.Deployments("owner", "repo"); + var expectedUrl = string.Format("repos/{0}/{1}/deployments", owner, name); - _client.GetAll("owner", "repo"); - _githubClient.Connection - .Received(1) - .Get>(Arg.Is(expectedUri), - Arg.Any>(), Arg.Any()); + _client.GetAll(owner, name); + _githubClient.Connection.Received(1) + .Get>(Arg.Is(u => u.ToString() == expectedUrl), + Arg.Is>(dictionary => dictionary.Count == 0), + Arg.Any()); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var expectedUrl = string.Format("repos/{0}/{1}/deployments", owner, name); + + // all properties are setted => only 2 options (StartPage, PageSize) in dictionary + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + _client.GetAll(owner, name, options); + _githubClient.Connection.Received(1) + .Get>(Arg.Is(u => u.ToString() == expectedUrl), + Arg.Is>(dictionary => dictionary.Count == 2), + null); + + // StartPage is setted => only 1 option (StartPage) in dictionary + options = new ApiOptions + { + StartPage = 1 + }; + + _client.GetAll(owner, name, options); + _githubClient.Connection.Received(1) + .Get>(Arg.Is(u => u.ToString() == expectedUrl), + Arg.Is>(dictionary => dictionary.Count == 1), + null); + + // PageCount is setted => none of options in dictionary + options = new ApiOptions + { + PageCount = 1 + }; + + _client.GetAll(owner, name, options); + _githubClient.Connection.Received(1) + .Get>(Arg.Is(u => u.ToString() == expectedUrl), + Arg.Is>(dictionary => dictionary.Count == 0), + null); } } public class TheCreateMethod { - IGitHubClient _githubClient; - ObservableDeploymentsClient _client; + private readonly IGitHubClient _githubClient; + private ObservableDeploymentsClient _client; public TheCreateMethod() { @@ -117,9 +164,10 @@ namespace Octokit.Tests.Reactive var newDeployment = new NewDeployment("ref"); _client.Create("owner", "repo", newDeployment); + _githubClient.Repository.Deployment.Received(1).Create(Arg.Is("owner"), - Arg.Is("repo"), - Arg.Is(newDeployment)); + Arg.Is("repo"), + Arg.Is(newDeployment)); } } @@ -132,4 +180,4 @@ namespace Octokit.Tests.Reactive } } } -} \ No newline at end of file +} diff --git a/Octokit/Clients/DeploymentsClient.cs b/Octokit/Clients/DeploymentsClient.cs index ba84fe28..b543e73e 100644 --- a/Octokit/Clients/DeploymentsClient.cs +++ b/Octokit/Clients/DeploymentsClient.cs @@ -34,10 +34,30 @@ namespace Octokit /// All the s for the specified repository. public Task> GetAll(string owner, string name) { - Ensure.ArgumentNotNullOrEmptyString(owner, "login"); + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - return ApiConnection.GetAll(ApiUrls.Deployments(owner, name)); + return GetAll(owner, name, ApiOptions.None); + } + + /// + /// Gets all the deployments for the specified repository. Any user with pull access + /// to a repository can view deployments. + /// + /// + /// http://developer.github.com/v3/repos/deployments/#list-deployments + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// All the s for the specified repository. + public Task> GetAll(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Deployments(owner, name), options); } /// diff --git a/Octokit/Clients/IDeploymentsClient.cs b/Octokit/Clients/IDeploymentsClient.cs index 97a8b245..3cbcd80f 100644 --- a/Octokit/Clients/IDeploymentsClient.cs +++ b/Octokit/Clients/IDeploymentsClient.cs @@ -24,6 +24,19 @@ namespace Octokit /// All the s for the specified repository. Task> GetAll(string owner, string name); + /// + /// Gets all the deployments for the specified repository. Any user with pull access + /// to a repository can view deployments. + /// + /// + /// http://developer.github.com/v3/repos/deployments/#list-deployments + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// All the s for the specified repository. + Task> GetAll(string owner, string name, ApiOptions options); + /// /// Creates a new deployment for the specified repository. /// Users with push access can create a deployment for a given ref.