using System; using System.Collections.Generic; using System.Reactive.Linq; using System.Threading.Tasks; using NSubstitute; using Octokit.Internal; using Octokit.Reactive; using Xunit; namespace Octokit.Tests.Reactive { public class ObservableRepositoriesClientTests { public class TheCtor { [Fact] public void EnsuresNonNullArguments() { Assert.Throws( () => new ObservableRepositoriesClient(null)); } } public class TheDeleteMethod { [Fact] public async Task RequestsCorrectUrl() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); await client.Delete("owner", "name"); gitHubClient.Received().Repository.Delete("owner", "name"); } [Fact] public async Task RequestsCorrectUrlWithRepositoryId() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); await client.Delete(1); gitHubClient.Received().Repository.Delete(1); } [Fact] public async Task EnsuresNonNullArguments() { var client = new ObservableRepositoriesClient(Substitute.For()); Assert.Throws(() => client.Delete(null, "name")); Assert.Throws(() => client.Delete("owner", null)); Assert.Throws(() => client.Delete("", "name")); Assert.Throws(() => client.Delete("owner", "")); } } public class TheGetMethod { [Fact] public async Task RequestsCorrectUrl() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); await client.Get("owner", "name"); gitHubClient.Received().Repository.Get("owner", "name"); } [Fact] public async Task RequestsCorrectUrlWithRepositoryId() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); await client.Get(1); gitHubClient.Repository.Get(1); } // This isn't really a test specific to this method. This is just as good a place as any to test // that our API methods returns the right kind of observables. [Fact] public async Task IsALukeWarmObservable() { var repository = new Repository(); var response = Task.Factory.StartNew>(() => new ApiResponse(new Response(), repository)); var connection = Substitute.For(); connection.Get(Args.Uri, null, "application/vnd.github.polaris-preview+json").Returns(response); var gitHubClient = new GitHubClient(connection); var client = new ObservableRepositoriesClient(gitHubClient); var observable = client.Get("stark", "ned"); connection.Received(1).Get(Args.Uri, null, "application/vnd.github.polaris-preview+json"); var result = await observable; connection.Received(1).Get(Args.Uri, null, "application/vnd.github.polaris-preview+json"); var result2 = await observable; // TODO: If we change this to a warm observable, we'll need to change this to Received(2) connection.Received(1).Get(Args.Uri, null, "application/vnd.github.polaris-preview+json"); Assert.Same(repository, result); Assert.Same(repository, result2); } // This isn't really a test specific to this method. This is just as good a place as any to test // that our API methods returns the right kind of observables. [Fact] public async Task IsALukeWarmObservableWithRepositoryId() { var repository = new Repository(); var response = Task.Factory.StartNew>(() => new ApiResponse(new Response(), repository)); var connection = Substitute.For(); connection.Get(Args.Uri, null, "application/vnd.github.polaris-preview+json").Returns(response); var gitHubClient = new GitHubClient(connection); var client = new ObservableRepositoriesClient(gitHubClient); var observable = client.Get(1); connection.Received(1).Get(Args.Uri, null, "application/vnd.github.polaris-preview+json"); var result = await observable; connection.Received(1).Get(Args.Uri, null, "application/vnd.github.polaris-preview+json"); var result2 = await observable; // TODO: If we change this to a warm observable, we'll need to change this to Received(2) connection.Received(1).Get(Args.Uri, null, "application/vnd.github.polaris-preview+json"); Assert.Same(repository, result); Assert.Same(repository, result2); } [Fact] public async Task EnsuresNonNullArguments() { var client = new ObservableRepositoriesClient(Substitute.For()); Assert.Throws(() => client.Get(null, "name")); Assert.Throws(() => client.Get("owner", null)); Assert.Throws(() => client.Get("", "name")); Assert.Throws(() => client.Get("owner", "")); } } public class TheGetAllForCurrentMethod { [Fact] public async Task ReturnsEveryPageOfRepositories() { var firstPageUrl = new Uri("user/repos", UriKind.Relative); var secondPageUrl = new Uri("https://example.com/page/2"); var firstPageLinks = new Dictionary { { "next", secondPageUrl } }; var firstPageResponse = new ApiResponse>( CreateResponseWithApiInfo(firstPageLinks), new List { new Repository(1), new Repository(2), new Repository(3) }); var thirdPageUrl = new Uri("https://example.com/page/3"); var secondPageLinks = new Dictionary { { "next", thirdPageUrl } }; var secondPageResponse = new ApiResponse> ( CreateResponseWithApiInfo(secondPageLinks), new List { new Repository(4), new Repository(5), new Repository(6) }); var lastPageResponse = new ApiResponse>( new Response(), new List { new Repository(7) }); var gitHubClient = Substitute.For(); gitHubClient.Connection.Get>(firstPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); gitHubClient.Connection.Get>(secondPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); gitHubClient.Connection.Get>(thirdPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); var repositoriesClient = new ObservableRepositoriesClient(gitHubClient); var results = await repositoriesClient.GetAllForCurrent().ToArray(); Assert.Equal(7, results.Length); gitHubClient.Connection.Received(1).Get>(firstPageUrl, Arg.Any>(), null); gitHubClient.Connection.Received(1).Get>(secondPageUrl, Arg.Any>(), null); gitHubClient.Connection.Received(1).Get>(thirdPageUrl, Arg.Any>(), null); } [Fact(Skip = "See https://github.com/octokit/octokit.net/issues/1011 for issue to investigate this further")] public async Task StopsMakingNewRequestsWhenTakeIsFulfilled() { var firstPageUrl = new Uri("user/repos", UriKind.Relative); var secondPageUrl = new Uri("https://example.com/page/2"); var firstPageLinks = new Dictionary { { "next", secondPageUrl } }; var firstPageResponse = new ApiResponse> ( CreateResponseWithApiInfo(firstPageLinks), new List { new Repository(1), new Repository(2), new Repository(3) } ); var thirdPageUrl = new Uri("https://example.com/page/3"); var secondPageLinks = new Dictionary { { "next", thirdPageUrl } }; var secondPageResponse = new ApiResponse> ( CreateResponseWithApiInfo(secondPageLinks), new List { new Repository(4), new Repository(5), new Repository(6) } ); var fourthPageUrl = new Uri("https://example.com/page/4"); var thirdPageLinks = new Dictionary { { "next", fourthPageUrl } }; var thirdPageResponse = new ApiResponse> ( CreateResponseWithApiInfo(thirdPageLinks), new List { new Repository(7) } ); var lastPageResponse = new ApiResponse> ( new Response(), new List { new Repository(8) } ); var gitHubClient = Substitute.For(); gitHubClient.Connection.GetResponse>(firstPageUrl) .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); gitHubClient.Connection.GetResponse>(secondPageUrl) .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); gitHubClient.Connection.GetResponse>(thirdPageUrl) .Returns(Task.Factory.StartNew>>(() => thirdPageResponse)); gitHubClient.Connection.GetResponse>(fourthPageUrl) .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); var repositoriesClient = new ObservableRepositoriesClient(gitHubClient); var results = await repositoriesClient.GetAllForCurrent().Take(4).ToArray(); Assert.Equal(4, results.Length); gitHubClient.Connection.Received(1).Get>(firstPageUrl, null, null); gitHubClient.Connection.Received(1).Get>(secondPageUrl, null, null); gitHubClient.Connection.Received(0).Get>(thirdPageUrl, null, null); gitHubClient.Connection.Received(0).Get>(fourthPageUrl, null, null); } } public class TheGetAllPublicRepositoriesSinceMethod { [Fact] public async Task ReturnsEveryPageOfRepositories() { var firstPageUrl = new Uri("repositories?since=364", UriKind.Relative); var secondPageUrl = new Uri("https://example.com/page/2"); var firstPageLinks = new Dictionary { { "next", secondPageUrl } }; IApiResponse> firstPageResponse = new ApiResponse>( CreateResponseWithApiInfo(firstPageLinks), new List { new Repository(364), new Repository(365), new Repository(366) }); var thirdPageUrl = new Uri("https://example.com/page/3"); var secondPageLinks = new Dictionary { { "next", thirdPageUrl } }; IApiResponse> secondPageResponse = new ApiResponse> ( CreateResponseWithApiInfo(secondPageLinks), new List { new Repository(367), new Repository(368), new Repository(369) }); IApiResponse> lastPageResponse = new ApiResponse>( new Response(), new List { new Repository(370) }); var gitHubClient = Substitute.For(); gitHubClient.Connection.Get>(firstPageUrl, null, null) .Returns(Task.FromResult(firstPageResponse)); gitHubClient.Connection.Get>(secondPageUrl, null, null) .Returns(Task.FromResult(secondPageResponse)); gitHubClient.Connection.Get>(thirdPageUrl, null, null) .Returns(Task.FromResult(lastPageResponse)); var repositoriesClient = new ObservableRepositoriesClient(gitHubClient); var results = await repositoriesClient.GetAllPublic(new PublicRepositoryRequest(364L)).ToArray(); Assert.Equal(7, results.Length); gitHubClient.Connection.Received(1).Get>(firstPageUrl, null, null); gitHubClient.Connection.Received(1).Get>(secondPageUrl, null, null); gitHubClient.Connection.Received(1).Get>(thirdPageUrl, null, null); } } public class TheGetCommitMethod { [Fact] public void EnsuresNonNullArguments() { var client = new ObservableRepositoriesClient(Substitute.For()); Assert.Throws(() => client.Commit.Get(null, "repo", "reference")); Assert.Throws(() => client.Commit.Get("owner", null, "reference")); Assert.Throws(() => client.Commit.Get("owner", "repo", null)); Assert.Throws(() => client.Commit.Get("", "repo", "reference")); Assert.Throws(() => client.Commit.Get("owner", "", "reference")); Assert.Throws(() => client.Commit.Get("owner", "repo", "")); } [Fact] public void CallsCorrectApi() { var github = Substitute.For(); var client = new ObservableRepositoriesClient(github); client.Commit.Get("owner", "repo", "reference"); github.Repository.Commit.Received(1).Get("owner", "repo", "reference"); } } public class TheGetAllCommitsMethod { [Fact] public void EnsuresNonNullArguments() { var client = new ObservableRepositoriesClient(Substitute.For()); Assert.Throws(() => client.Commit.GetAll(null, "repo")); Assert.Throws(() => client.Commit.GetAll("owner", null)); Assert.Throws(() => client.Commit.GetAll("owner", "repo", null, ApiOptions.None)); Assert.Throws(() => client.Commit.GetAll("", "repo")); Assert.Throws(() => client.Commit.GetAll("owner", "")); } [Fact] public void RequestsTheCorrectUrl() { var github = Substitute.For(); var client = new ObservableRepositoriesClient(github); var expected = new Uri("repos/owner/repo/commits", UriKind.Relative); client.Commit.GetAll("owner", "repo"); github.Connection.Received(1).Get>(expected, Arg.Any>(), null); } } public class TheGetAllContributorsMethod { [Fact] public void RequestsTheCorrectUrl() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repos/owner/repo/contributors", UriKind.Relative); client.GetAllContributors("owner", "repo"); gitHubClient.Connection.Received(1) .Get>(expected, Args.EmptyDictionary, null); } [Fact] public void RequestsTheCorrectUrlWithRepositoryId() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repositories/1/contributors", UriKind.Relative); client.GetAllContributors(1); gitHubClient.Connection.Received(1) .Get>(expected, Args.EmptyDictionary, null); } [Fact] public void RequestsTheCorrectUrlWithApiOptions() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repos/owner/repo/contributors", UriKind.Relative); var options = new ApiOptions { PageCount = 1, StartPage = 1, PageSize = 1 }; client.GetAllContributors("owner", "repo", options); gitHubClient.Connection.Received(1) .Get>(expected, Arg.Is>(d => d.Count == 2 && d["page"] == "1" && d["per_page"] == "1"), null); } [Fact] public void RequestsTheCorrectUrlWithApiOptionsWithRepositoryId() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repositories/1/contributors", UriKind.Relative); var options = new ApiOptions { PageCount = 1, StartPage = 1, PageSize = 1 }; client.GetAllContributors(1, options); gitHubClient.Connection.Received(1) .Get>(expected, Arg.Is>(d => d.Count == 2), null); } [Fact] public void RequestsTheCorrectUrlIncludeAnonymous() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); client.GetAllContributors("owner", "name", true); gitHubClient.Connection.Received() .Get>(Arg.Is(u => u.ToString() == "repos/owner/name/contributors"), Arg.Is>(d => d["anon"] == "1"), null); } [Fact] public void RequestsTheCorrectUrlWithRepositoryIdIncludeAnonymous() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); client.GetAllContributors(1, true); gitHubClient.Connection.Received() .Get>(Arg.Is(u => u.ToString() == "repositories/1/contributors"), Arg.Is>(d => d["anon"] == "1"), null); } [Fact] public void RequestsTheCorrectUrlWithApiOptionsIncludeAnonymous() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var options = new ApiOptions { PageCount = 1, StartPage = 1, PageSize = 1 }; client.GetAllContributors("owner", "name", true, options); gitHubClient.Connection.Received() .Get>(Arg.Is(u => u.ToString() == "repos/owner/name/contributors"), Arg.Is>(d => d.Count == 3 && d["anon"] == "1" && d["page"] == "1" && d["per_page"] == "1"), null); } [Fact] public void RequestsTheCorrectUrlWithRepositoryIdWithApiOptionsIncludeAnonymous() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var options = new ApiOptions { PageCount = 1, StartPage = 1, PageSize = 1 }; client.GetAllContributors(1, true, options); gitHubClient.Connection.Received() .Get>(Arg.Is(u => u.ToString() == "repositories/1/contributors"), Arg.Is>(d => d.Count == 3 && d["anon"] == "1" && d["page"] == "1" && d["per_page"] == "1"), null); } [Fact] public void EnsuresNonNullArguments() { var client = new ObservableRepositoriesClient(Substitute.For()); Assert.Throws(() => client.GetAllContributors(null, "repo")); Assert.Throws(() => client.GetAllContributors("owner", null)); Assert.Throws(() => client.GetAllContributors(null, "repo", ApiOptions.None)); Assert.Throws(() => client.GetAllContributors("owner", null, ApiOptions.None)); Assert.Throws(() => client.GetAllContributors("owner", "repo", null)); Assert.Throws(() => client.GetAllContributors(null, "repo", false, ApiOptions.None)); Assert.Throws(() => client.GetAllContributors("owner", null, false, ApiOptions.None)); Assert.Throws(() => client.GetAllContributors("owner", "repo", false, null)); Assert.Throws(() => client.GetAllContributors(1, null)); Assert.Throws(() => client.GetAllContributors(1, false, null)); Assert.Throws(() => client.GetAllContributors("", "repo")); Assert.Throws(() => client.GetAllContributors("owner", "")); Assert.Throws(() => client.GetAllContributors("", "repo", ApiOptions.None)); Assert.Throws(() => client.GetAllContributors("owner", "", ApiOptions.None)); Assert.Throws(() => client.GetAllContributors("", "repo", false, ApiOptions.None)); Assert.Throws(() => client.GetAllContributors("owner", "", false, ApiOptions.None)); } } public class TheGetAllLanguagesMethod { [Fact] public void RequestsTheCorrectUrl() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repos/owner/repo/languages", UriKind.Relative); client.GetAllLanguages("owner", "repo"); gitHubClient.Connection.Received(1).GetResponse>>(expected); } [Fact] public void RequestsTheCorrectUrlWithRepositoryId() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repositories/1/languages", UriKind.Relative); client.GetAllLanguages(1); gitHubClient.Connection.Received(1).GetResponse>>(expected); } [Fact] public void EnsuresNonNullArguments() { var client = new ObservableRepositoriesClient(Substitute.For()); Assert.Throws(() => client.GetAllLanguages(null, "repo")); Assert.Throws(() => client.GetAllLanguages("owner", null)); Assert.Throws(() => client.GetAllLanguages("", "repo")); Assert.Throws(() => client.GetAllLanguages("owner", "")); } } public class TheGetAllTeamsMethod { [Fact] public void RequestsTheCorrectUrl() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repos/owner/repo/teams", UriKind.Relative); client.GetAllTeams("owner", "repo"); gitHubClient.Connection.Received(1).Get>(expected, Arg.Any>(), Arg.Any()); } [Fact] public void RequestsTheCorrectUrlWithRepositoryId() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repositories/1/teams", UriKind.Relative); client.GetAllTeams(1); gitHubClient.Connection.Received(1).Get>(expected, Arg.Any>(), Arg.Any()); } [Fact] public void RequestsTheCorrectUrlWithApiOptions() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repos/owner/repo/teams", UriKind.Relative); var options = new ApiOptions { PageCount = 1, StartPage = 1, PageSize = 1 }; client.GetAllTeams("owner", "repo", options); gitHubClient.Connection.Received(1).Get>(expected, Arg.Is>(d => d.Count == 2 && d["page"] == "1" && d["per_page"] == "1"), Arg.Any()); } [Fact] public void RequestsTheCorrectUrlWithRepositoryIdWithApiOptions() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repositories/1/teams", UriKind.Relative); var options = new ApiOptions { PageCount = 1, StartPage = 1, PageSize = 1 }; client.GetAllTeams(1, options); gitHubClient.Connection.Received(1).Get>(expected, Arg.Is>(d => d.Count == 2 && d["page"] == "1" && d["per_page"] == "1"), Arg.Any()); } [Fact] public void EnsuresNonNullArguments() { var client = new ObservableRepositoriesClient(Substitute.For()); Assert.Throws(() => client.GetAllTeams(null, "repo")); Assert.Throws(() => client.GetAllTeams("owner", null)); Assert.Throws(() => client.GetAllTeams(null, "repo", ApiOptions.None)); Assert.Throws(() => client.GetAllTeams("owner", null, ApiOptions.None)); Assert.Throws(() => client.GetAllTeams("owner", "repo", null)); Assert.Throws(() => client.GetAllTeams(1, null)); Assert.Throws(() => client.GetAllTeams("", "repo")); Assert.Throws(() => client.GetAllTeams("owner", "")); Assert.Throws(() => client.GetAllTeams("", "repo", ApiOptions.None)); Assert.Throws(() => client.GetAllTeams("owner", "", ApiOptions.None)); } } public class TheGetAllTagsMethod { [Fact] public void RequestsTheCorrectUrl() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repos/owner/repo/tags", UriKind.Relative); client.GetAllTags("owner", "repo"); gitHubClient.Connection.Received(1).Get>(expected, Arg.Any>(), null); } [Fact] public void RequestsTheCorrectUrlWithRepositoryId() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repositories/1/tags", UriKind.Relative); client.GetAllTags(1); gitHubClient.Connection.Received(1).Get>(expected, Arg.Any>(), null); } [Fact] public void RequestsTheCorrectUrlWithApiOptions() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repos/owner/repo/tags", UriKind.Relative); var options = new ApiOptions { PageCount = 1, StartPage = 1, PageSize = 1 }; client.GetAllTags("owner", "repo", options); gitHubClient.Connection.Received(1).Get>(expected, Arg.Is>(d => d.Count == 2 && d["page"] == "1" && d["per_page"] == "1"), null); } [Fact] public void RequestsTheCorrectUrlWithRepositoryIdWithApiOptions() { var gitHubClient = Substitute.For(); var client = new ObservableRepositoriesClient(gitHubClient); var expected = new Uri("repositories/1/tags", UriKind.Relative); var options = new ApiOptions { PageCount = 1, StartPage = 1, PageSize = 1 }; client.GetAllTags(1, options); gitHubClient.Connection.Received(1).Get>(expected, Arg.Is>(d => d.Count == 2 && d["page"] == "1" && d["per_page"] == "1"), null); } [Fact] public void EnsuresNonNullArguments() { var client = new ObservableRepositoriesClient(Substitute.For()); Assert.Throws(() => client.GetAllTags(null, "repo")); Assert.Throws(() => client.GetAllTags("owner", null)); Assert.Throws(() => client.GetAllTags(null, "repo", ApiOptions.None)); Assert.Throws(() => client.GetAllTags("owner", null, ApiOptions.None)); Assert.Throws(() => client.GetAllTags("owner", "repo", null)); Assert.Throws(() => client.GetAllTags(1, null)); Assert.Throws(() => client.GetAllTags("", "repo")); Assert.Throws(() => client.GetAllTags("owner", "")); Assert.Throws(() => client.GetAllTags("", "repo", ApiOptions.None)); Assert.Throws(() => client.GetAllTags("owner", "", ApiOptions.None)); } } public class TheEditMethod { [Fact] public void PatchsTheCorrectUrl() { var github = Substitute.For(); var client = new ObservableRepositoriesClient(github); var update = new RepositoryUpdate("anyreponame"); client.Edit("owner", "repo", update); github.Repository.Received(1).Edit("owner", "repo", update); } [Fact] public void PatchsTheCorrectUrlWithRepositoryId() { var github = Substitute.For(); var client = new ObservableRepositoriesClient(github); var update = new RepositoryUpdate("anyreponame"); client.Edit(1, update); github.Repository.Received(1).Edit(1, update); } [Fact] public async Task EnsuresNonNullArguments() { var client = new ObservableRepositoriesClient(Substitute.For()); var update = new RepositoryUpdate("anyreponame"); Assert.Throws(() => client.Edit(null, "repo", update)); Assert.Throws(() => client.Edit("owner", null, update)); Assert.Throws(() => client.Edit("owner", "repo", null)); Assert.Throws(() => client.Edit(1, null)); Assert.Throws(() => client.Edit("", "repo", update)); Assert.Throws(() => client.Edit("owner", "", update)); } } static IResponse CreateResponseWithApiInfo(IDictionary links) { var response = Substitute.For(); response.ApiInfo.Returns(new ApiInfo(links, new List(), new List(), "etag", new RateLimit(new Dictionary()))); return response; } } }