diff --git a/Octokit.Reactive/Clients/IObservableRepositoryHooksClient.cs b/Octokit.Reactive/Clients/IObservableRepositoryHooksClient.cs index f3176e0a..53d2f592 100644 --- a/Octokit.Reactive/Clients/IObservableRepositoryHooksClient.cs +++ b/Octokit.Reactive/Clients/IObservableRepositoryHooksClient.cs @@ -10,9 +10,21 @@ namespace Octokit.Reactive /// Gets the list of hooks defined for a repository /// /// See API documentation for more information. + /// The repository's owner + /// The repository's name /// IObservable GetAll(string owner, string repositoryName); + /// + /// Gets the list of hooks defined for a repository + /// + /// See API documentation for more information. + /// The repository's owner + /// The repository's name + /// Options for changing the API response + /// + IObservable GetAll(string owner, string repositoryName, ApiOptions options); + /// /// Gets a single hook defined for a repository by id /// diff --git a/Octokit.Reactive/Clients/ObservableRepositoryHooksClient.cs b/Octokit.Reactive/Clients/ObservableRepositoryHooksClient.cs index 7b229dd2..24bca1f4 100644 --- a/Octokit.Reactive/Clients/ObservableRepositoryHooksClient.cs +++ b/Octokit.Reactive/Clients/ObservableRepositoryHooksClient.cs @@ -22,13 +22,32 @@ namespace Octokit.Reactive /// Gets the list of hooks defined for a repository /// /// See API documentation for more information. + /// The repository's owner + /// The repository's name /// public IObservable GetAll(string owner, string repositoryName) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(repositoryName, "repositoryName"); - return _connection.GetAndFlattenAllPages(ApiUrls.RepositoryHooks(owner, repositoryName)); + return GetAll(owner, repositoryName, ApiOptions.None); + } + + /// + /// Gets the list of hooks defined for a repository + /// + /// See API documentation for more information. + /// The repository's owner + /// The repository's name + /// Options for changing the API response + /// + public IObservable GetAll(string owner, string repositoryName, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(repositoryName, "repositoryName"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.RepositoryHooks(owner, repositoryName), options); } /// diff --git a/Octokit.Tests.Integration/Clients/RepositoryHooksClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoryHooksClientTests.cs index d0fd6c6e..35c1f68d 100644 --- a/Octokit.Tests.Integration/Clients/RepositoryHooksClientTests.cs +++ b/Octokit.Tests.Integration/Clients/RepositoryHooksClientTests.cs @@ -25,11 +25,70 @@ namespace Octokit.Tests.Integration.Clients var hooks = await github.Repository.Hooks.GetAll(_fixture.RepositoryOwner, _fixture.RepositoryName); - Assert.Single(hooks); - var actualHook = hooks[0]; + Assert.Equal(_fixture.ExpectedHooks.Count, hooks.Count); + var actualHook = hooks[0]; AssertHook(_fixture.ExpectedHook, actualHook); } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfHooksWithoutStart() + { + var github = Helper.GetAuthenticatedClient(); + + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var hooks = await github.Repository.Hooks.GetAll(_fixture.RepositoryOwner, _fixture.RepositoryName, options); + + Assert.Equal(_fixture.ExpectedHooks.Count, hooks.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfHooksWithStart() + { + var github = Helper.GetAuthenticatedClient(); + + var options = new ApiOptions + { + PageSize = 2, + PageCount = 1, + StartPage = 3 + }; + + var hooks = await github.Repository.Hooks.GetAll(_fixture.RepositoryOwner, _fixture.RepositoryName, options); + + Assert.Equal(1, hooks.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var github = Helper.GetAuthenticatedClient(); + + var startOptions = new ApiOptions + { + PageSize = 2, + PageCount = 1 + }; + + var firstPage = await github.Repository.Hooks.GetAll(_fixture.RepositoryOwner, _fixture.RepositoryName, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 2, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await github.Repository.Hooks.GetAll(_fixture.RepositoryOwner, _fixture.RepositoryName, skipStartOptions); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + Assert.NotEqual(firstPage[1].Id, secondPage[1].Id); + } } [Collection(RepositoriesHooksCollection.Name)] diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 2155cd1e..d790c35a 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -156,6 +156,7 @@ + diff --git a/Octokit.Tests.Integration/Reactive/ObservableRepositoryHooksClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableRepositoryHooksClientTests.cs new file mode 100644 index 00000000..76d62db1 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableRepositoryHooksClientTests.cs @@ -0,0 +1,115 @@ +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Octokit.Tests.Integration.fixtures; +using Xunit; + +namespace Octokit.Tests.Integration.Reactive +{ + public class ObservableRepositoryHooksClientTests + { + [Collection(RepositoriesHooksCollection.Name)] + public class TheGetAllMethod + { + readonly RepositoriesHooksFixture _fixture; + + public TheGetAllMethod(RepositoriesHooksFixture fixture) + { + _fixture = fixture; + } + + [IntegrationTest] + public async Task ReturnsAllHooksFromRepository() + { + var github = Helper.GetAuthenticatedClient(); + + var client = new ObservableRepositoryHooksClient(github); + + var hooks = await client.GetAll(_fixture.RepositoryOwner, _fixture.RepositoryName).ToList(); + + Assert.Equal(_fixture.ExpectedHooks.Count, hooks.Count); + + var actualHook = hooks[0]; + AssertHook(_fixture.ExpectedHook, actualHook); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfHooksWithoutStart() + { + var github = Helper.GetAuthenticatedClient(); + + var client = new ObservableRepositoryHooksClient(github); + + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var hooks = await client.GetAll(_fixture.RepositoryOwner, _fixture.RepositoryName, options).ToList(); + + Assert.Equal(_fixture.ExpectedHooks.Count, hooks.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfHooksWithStart() + { + var github = Helper.GetAuthenticatedClient(); + + var client = new ObservableRepositoryHooksClient(github); + + var options = new ApiOptions + { + PageSize = 2, + PageCount = 1, + StartPage = 3 + }; + + var hooks = await client.GetAll(_fixture.RepositoryOwner, _fixture.RepositoryName, options).ToList(); + + Assert.Equal(1, hooks.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var github = Helper.GetAuthenticatedClient(); + + var client = new ObservableRepositoryHooksClient(github); + + var startOptions = new ApiOptions + { + PageSize = 2, + PageCount = 1 + }; + + var firstPage = await client.GetAll(_fixture.RepositoryOwner, _fixture.RepositoryName, startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 2, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await client.GetAll(_fixture.RepositoryOwner, _fixture.RepositoryName, skipStartOptions).ToList(); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + Assert.NotEqual(firstPage[1].Id, secondPage[1].Id); + } + + static void AssertHook(RepositoryHook expectedHook, RepositoryHook actualHook) + { + Assert.Equal(expectedHook.Id, actualHook.Id); + Assert.Equal(expectedHook.Active, actualHook.Active); + Assert.Equal(expectedHook.Config, actualHook.Config); + Assert.Equal(expectedHook.CreatedAt, actualHook.CreatedAt); + Assert.Equal(expectedHook.Name, actualHook.Name); + Assert.Equal(expectedHook.PingUrl, actualHook.PingUrl); + Assert.Equal(expectedHook.TestUrl, actualHook.TestUrl); + Assert.Equal(expectedHook.UpdatedAt, actualHook.UpdatedAt); + Assert.Equal(expectedHook.Url, actualHook.Url); + } + } + } +} diff --git a/Octokit.Tests.Integration/fixtures/RepositoriesHooksFixture.cs b/Octokit.Tests.Integration/fixtures/RepositoriesHooksFixture.cs index cda77a8a..09b47c1c 100644 --- a/Octokit.Tests.Integration/fixtures/RepositoriesHooksFixture.cs +++ b/Octokit.Tests.Integration/fixtures/RepositoriesHooksFixture.cs @@ -8,12 +8,21 @@ namespace Octokit.Tests.Integration.fixtures readonly IGitHubClient _github; readonly Repository _repository; readonly RepositoryHook _hook; + readonly IList _hooks; public RepositoriesHooksFixture() { _github = Helper.GetAuthenticatedClient(); _repository = CreateRepository(_github); - _hook = CreateHook(_github, _repository); + _hooks = new List(5) + { + CreateHook(_github, _repository, "awscodedeploy", "deployment"), + CreateHook(_github, _repository, "awsopsworks", "push"), + CreateHook(_github, _repository, "activecollab", "push"), + CreateHook(_github, _repository, "acunote", "push"), + CreateHook(_github, _repository, "agilebench", "push") + }; + _hook = _hooks[0]; } public string RepositoryOwner { get { return _repository.Owner.Login; } } @@ -22,6 +31,8 @@ namespace Octokit.Tests.Integration.fixtures public RepositoryHook ExpectedHook { get { return _hook; } } + public IList ExpectedHooks { get { return _hooks; } } + public void Dispose() { _github.Repository.Delete(_repository.Owner.Login, _repository.Name); @@ -35,12 +46,12 @@ namespace Octokit.Tests.Integration.fixtures return repository.Result; } - static RepositoryHook CreateHook(IGitHubClient github, Repository repository) + static RepositoryHook CreateHook(IGitHubClient github, Repository repository, string hookName, string eventName) { var config = new Dictionary { { "content_type", "json" }, { "url", "http://test.com/example" } }; - var parameters = new NewRepositoryHook("apropos", config) + var parameters = new NewRepositoryHook(hookName, config) { - Events = new[] { "commit_comment" }, + Events = new[] { eventName }, Active = false }; var createdHook = github.Repository.Hooks.Create(Helper.UserName, repository.Name, parameters); diff --git a/Octokit.Tests/Clients/RepositoryHooksClientTest.cs b/Octokit.Tests/Clients/RepositoryHooksClientTest.cs index 9175e2aa..7a138807 100644 --- a/Octokit.Tests/Clients/RepositoryHooksClientTest.cs +++ b/Octokit.Tests/Clients/RepositoryHooksClientTest.cs @@ -1,7 +1,7 @@ -using NSubstitute; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using NSubstitute; using Xunit; namespace Octokit.Tests.Clients @@ -28,7 +28,28 @@ namespace Octokit.Tests.Clients client.Hooks.GetAll("fake", "repo"); - connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/hooks")); + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/hooks"), + Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new RepositoriesClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + client.Hooks.GetAll("fake", "repo", options); + + connection.Received(1) + .GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/hooks"), + options); } [Fact] diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index 0cc589e2..4d36e03a 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -220,6 +220,7 @@ + diff --git a/Octokit.Tests/Reactive/ObservableRepositoryHooksClientTests.cs b/Octokit.Tests/Reactive/ObservableRepositoryHooksClientTests.cs new file mode 100644 index 00000000..f3c7961c --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableRepositoryHooksClientTests.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableRepositoryHooksClientTests + { + public class ObservableAuthorizationsClientTests + { + const string owner = "owner"; + const string repositoryName = "name"; + + public class TheGetAllMethod + { + [Fact] + public void GetsCorrectUrl() + { + var client = Substitute.For(); + var authEndpoint = new ObservableRepositoryHooksClient(client); + var expectedUrl = string.Format("repos/{0}/{1}/hooks", owner, repositoryName); + + authEndpoint.GetAll(owner, repositoryName); + + client.Connection.Received(1).Get>(Arg.Is(u => u.ToString() == expectedUrl), + Arg.Is>(dictionary => dictionary.Count == 0), null); + } + + [Fact] + public void GetsCorrectUrlWithApiOption() + { + var gitHubClient = Substitute.For(); + var hooksClient = new ObservableRepositoryHooksClient(gitHubClient); + var expectedUrl = string.Format("repos/{0}/{1}/hooks", owner, repositoryName); + + // all properties are setted => only 2 options (StartPage, PageSize) in dictionary + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + hooksClient.GetAll(owner, repositoryName, 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 + }; + + hooksClient.GetAll(owner, repositoryName, 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 + }; + + hooksClient.GetAll(owner, repositoryName, options); + gitHubClient.Connection.Received(1) + .Get>(Arg.Is(u => u.ToString() == expectedUrl), + Arg.Is>(dictionary => dictionary.Count == 0), + null); + } + } + } + + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new ObservableRepositoryHooksClient(null)); + } + } + } +} \ No newline at end of file diff --git a/Octokit/Clients/IRepositoryHooksClient.cs b/Octokit/Clients/IRepositoryHooksClient.cs index c282a372..571306a4 100644 --- a/Octokit/Clients/IRepositoryHooksClient.cs +++ b/Octokit/Clients/IRepositoryHooksClient.cs @@ -10,9 +10,21 @@ namespace Octokit /// Gets the list of hooks defined for a repository /// /// See API documentation for more information. + /// The repository's owner + /// The repository's name /// Task> GetAll(string owner, string repositoryName); + /// + /// Gets the list of hooks defined for a repository + /// + /// See API documentation for more information. + /// The repository's owner + /// The repository's name + /// Options for changing the API response + /// + Task> GetAll(string owner, string repositoryName, ApiOptions options); + /// /// Gets a single hook by Id /// diff --git a/Octokit/Clients/RepositoryHooksClient.cs b/Octokit/Clients/RepositoryHooksClient.cs index 458a1596..f2e6e9df 100644 --- a/Octokit/Clients/RepositoryHooksClient.cs +++ b/Octokit/Clients/RepositoryHooksClient.cs @@ -18,13 +18,32 @@ namespace Octokit /// Gets the list of hooks defined for a repository /// /// See API documentation for more information. + /// The repository's owner + /// The repository's name /// public Task> GetAll(string owner, string repositoryName) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(repositoryName, "repositoryName"); - return ApiConnection.GetAll(ApiUrls.RepositoryHooks(owner, repositoryName)); + return GetAll(owner, repositoryName, ApiOptions.None); + } + + /// + /// Gets the list of hooks defined for a repository + /// + /// See API documentation for more information. + /// The repository's owner + /// The repository's name + /// Options for changing the API response + /// + public Task> GetAll(string owner, string repositoryName, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(repositoryName, "repositoryName"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.RepositoryHooks(owner, repositoryName), options); } ///