using System; using System.Collections.Generic; using System.Net; using System.Threading.Tasks; using NSubstitute; using Octokit.Tests.Helpers; using Xunit; namespace Octokit.Tests.Clients { /// /// Client tests mostly just need to make sure they call the IApiConnection with the correct /// relative Uri. No need to fake up the response. All *those* tests are in ApiConnectionTests.cs. /// public class RepositoriesClientTests { public class TheConstructor { [Fact] public void EnsuresNonNullArguments() { Assert.Throws(() => new RepositoriesClient(null)); } } public class TheCreateMethodForUser { [Fact] public async Task EnsuresNonNullArguments() { var client = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => client.Create(null)); } [Fact] public void UsesTheUserReposUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.Create(new NewRepository("aName")); connection.Received().Post(Arg.Is(u => u.ToString() == "user/repos"), Arg.Any()); } [Fact] public void TheNewRepositoryDescription() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); var newRepository = new NewRepository("aName"); client.Create(newRepository); connection.Received().Post(Args.Uri, newRepository); } [Fact] public async Task ThrowsRepositoryExistsExceptionWhenRepositoryExistsForCurrentUser() { var newRepository = new NewRepository("aName"); var response = Substitute.For(); response.StatusCode.Returns((HttpStatusCode)422); response.Body.Returns(@"{""message"":""Validation Failed"",""documentation_url"":" + @"""http://developer.github.com/v3/repos/#create"",""errors"":[{""resource"":""Repository""," + @"""code"":""custom"",""field"":""name"",""message"":""name already exists on this account""}]}"); var credentials = new Credentials("haacked", "pwd"); var connection = Substitute.For(); connection.Connection.BaseAddress.Returns(GitHubClient.GitHubApiUrl); connection.Connection.Credentials.Returns(credentials); connection.Post(Args.Uri, newRepository) .Returns>(_ => { throw new ApiValidationException(response); }); var client = new RepositoriesClient(connection); var exception = await Assert.ThrowsAsync( () => client.Create(newRepository)); Assert.False(exception.OwnerIsOrganization); Assert.Null(exception.Organization); Assert.Equal("aName", exception.RepositoryName); Assert.Null(exception.ExistingRepositoryWebUrl); } [Fact] public async Task ThrowsExceptionWhenPrivateRepositoryQuotaExceeded() { var newRepository = new NewRepository("aName") { Private = true }; var response = Substitute.For(); response.StatusCode.Returns((HttpStatusCode)422); response.Body.Returns(@"{""message"":""Validation Failed"",""documentation_url"":" + @"""http://developer.github.com/v3/repos/#create"",""errors"":[{""resource"":""Repository""," + @"""code"":""custom"",""field"":""name"",""message"":" + @"""name can't be private. You are over your quota.""}]}"); var credentials = new Credentials("haacked", "pwd"); var connection = Substitute.For(); connection.Connection.BaseAddress.Returns(GitHubClient.GitHubApiUrl); connection.Connection.Credentials.Returns(credentials); connection.Post(Args.Uri, newRepository) .Returns>(_ => { throw new ApiValidationException(response); }); var client = new RepositoriesClient(connection); var exception = await Assert.ThrowsAsync( () => client.Create(newRepository)); Assert.NotNull(exception); } } public class TheCreateMethodForOrganization { [Fact] public async Task EnsuresNonNullArguments() { var client = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => client.Create(null, new NewRepository("aName"))); await Assert.ThrowsAsync(() => client.Create("aLogin", null)); } [Fact] public async Task UsesTheOrganizatinosReposUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); await client.Create("theLogin", new NewRepository("aName")); connection.Received().Post( Arg.Is(u => u.ToString() == "orgs/theLogin/repos"), Args.NewRepository); } [Fact] public async Task TheNewRepositoryDescription() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); var newRepository = new NewRepository("aName"); await client.Create("aLogin", newRepository); connection.Received().Post(Args.Uri, newRepository); } [Fact] public async Task ThrowsRepositoryExistsExceptionWhenRepositoryExistsForSpecifiedOrg() { var newRepository = new NewRepository("aName"); var response = Substitute.For(); response.StatusCode.Returns((HttpStatusCode)422); response.Body.Returns(@"{""message"":""Validation Failed"",""documentation_url"":" + @"""http://developer.github.com/v3/repos/#create"",""errors"":[{""resource"":""Repository""," + @"""code"":""custom"",""field"":""name"",""message"":""name already exists on this account""}]}"); var connection = Substitute.For(); connection.Connection.BaseAddress.Returns(GitHubClient.GitHubApiUrl); connection.Post(Args.Uri, newRepository) .Returns>(_ => { throw new ApiValidationException(response); }); var client = new RepositoriesClient(connection); var exception = await Assert.ThrowsAsync( () => client.Create("illuminati", newRepository)); Assert.True(exception.OwnerIsOrganization); Assert.Equal("illuminati", exception.Organization); Assert.Equal("aName", exception.RepositoryName); Assert.Equal(new Uri("https://github.com/illuminati/aName"), exception.ExistingRepositoryWebUrl); Assert.Equal("There is already a repository named 'aName' in the organization 'illuminati'.", exception.Message); } [Fact] public async Task ThrowsValidationException() { var newRepository = new NewRepository("aName"); var response = Substitute.For(); response.StatusCode.Returns((HttpStatusCode)422); response.Body.Returns(@"{""message"":""Validation Failed"",""documentation_url"":" + @"""http://developer.github.com/v3/repos/#create"",""errors"":[]}"); var connection = Substitute.For(); connection.Connection.BaseAddress.Returns(GitHubClient.GitHubApiUrl); connection.Post(Args.Uri, newRepository) .Returns>(_ => { throw new ApiValidationException(response); }); var client = new RepositoriesClient(connection); var exception = await Assert.ThrowsAsync( () => client.Create("illuminati", newRepository)); Assert.Null(exception as RepositoryExistsException); } [Fact] public async Task ThrowsRepositoryExistsExceptionForEnterpriseInstance() { var newRepository = new NewRepository("aName"); var response = Substitute.For(); response.StatusCode.Returns((HttpStatusCode)422); response.Body.Returns(@"{""message"":""Validation Failed"",""documentation_url"":" + @"""http://developer.github.com/v3/repos/#create"",""errors"":[{""resource"":""Repository""," + @"""code"":""custom"",""field"":""name"",""message"":""name already exists on this account""}]}"); var connection = Substitute.For(); connection.Connection.BaseAddress.Returns(new Uri("https://example.com")); connection.Post(Args.Uri, newRepository) .Returns>(_ => { throw new ApiValidationException(response); }); var client = new RepositoriesClient(connection); var exception = await Assert.ThrowsAsync( () => client.Create("illuminati", newRepository)); Assert.Equal("aName", exception.RepositoryName); Assert.Equal(new Uri("https://example.com/illuminati/aName"), exception.ExistingRepositoryWebUrl); } } public class TheDeleteMethod { [Fact] public async Task EnsuresNonNullArguments() { var client = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => client.Delete(null, "aRepoName")); await Assert.ThrowsAsync(() => client.Delete("anOwner", null)); } [Fact] public async Task RequestsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); await client.Delete("theOwner", "theRepoName"); connection.Received().Delete(Arg.Is(u => u.ToString() == "repos/theOwner/theRepoName")); } } public class TheGetMethod { [Fact] public void RequestsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.Get("fake", "repo"); connection.Received().Get(Arg.Is(u => u.ToString() == "repos/fake/repo")); } [Fact] public async Task EnsuresNonNullArguments() { var client = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => client.Get(null, "name")); await Assert.ThrowsAsync(() => client.Get("owner", null)); } } public class TheGetAllPublicMethod { [Fact] public void RequestsTheCorrectUrlAndReturnsRepositories() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllPublic(); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "/repositories")); } } public class TheGetAllPublicSinceMethod { [Fact] public void RequestsTheCorrectUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllPublic(new PublicRepositoryRequest(364)); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "/repositories?since=364")); } [Fact] public void SendsTheCorrectParameter() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllPublic(new PublicRepositoryRequest(364)); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "/repositories?since=364")); } } public class TheGetAllForCurrentMethod { [Fact] public void RequestsTheCorrectUrlAndReturnsRepositories() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllForCurrent(); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "user/repos")); } [Fact] public void CanFilterByType() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); var request = new RepositoryRequest { Type = RepositoryType.All }; client.GetAllForCurrent(request); connection.Received() .GetAll( Arg.Is(u => u.ToString() == "user/repos"), Arg.Is>(d => d["type"] == "all")); } [Fact] public void CanFilterBySort() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); var request = new RepositoryRequest { Type = RepositoryType.Private, Sort = RepositorySort.FullName }; client.GetAllForCurrent(request); connection.Received() .GetAll( Arg.Is(u => u.ToString() == "user/repos"), Arg.Is>(d => d["type"] == "private" && d["sort"] == "full_name")); } [Fact] public void CanFilterBySortDirection() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); var request = new RepositoryRequest { Type = RepositoryType.Member, Sort = RepositorySort.Updated, Direction = SortDirection.Ascending }; client.GetAllForCurrent(request); connection.Received() .GetAll( Arg.Is(u => u.ToString() == "user/repos"), Arg.Is>(d => d["type"] == "member" && d["sort"] == "updated" && d["direction"] == "asc")); } [Fact] public void CanFilterByVisibility() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); var request = new RepositoryRequest { Visibility = RepositoryVisibility.Private }; client.GetAllForCurrent(request); connection.Received() .GetAll( Arg.Is(u => u.ToString() == "user/repos"), Arg.Is>(d => d["visibility"] == "private")); } [Fact] public void CanFilterByAffiliation() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); var request = new RepositoryRequest { Affiliation = RepositoryAffiliation.Owner, Sort = RepositorySort.FullName }; client.GetAllForCurrent(request); connection.Received() .GetAll( Arg.Is(u => u.ToString() == "user/repos"), Arg.Is>(d => d["affiliation"] == "owner" && d["sort"] == "full_name")); } } public class TheGetAllForUserMethod { [Fact] public void RequestsTheCorrectUrlAndReturnsRepositories() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllForUser("username"); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "users/username/repos")); } [Fact] public async Task EnsuresNonNullArguments() { var reposEndpoint = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => reposEndpoint.GetAllForUser(null)); } } public class TheGetAllForOrgMethod { [Fact] public void RequestsTheCorrectUrlAndReturnsRepositories() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllForOrg("orgname"); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "orgs/orgname/repos")); } [Fact] public async Task EnsuresNonNullArguments() { var reposEndpoint = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => reposEndpoint.GetAllForOrg(null)); } } public class TheGetAllBranchesMethod { [Fact] public void ReturnsBranches() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllBranches("owner", "name"); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "repos/owner/name/branches"), null, "application/vnd.github.loki-preview+json"); } [Fact] public async Task EnsuresArguments() { var client = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => client.GetAllBranches(null, "repo")); await Assert.ThrowsAsync(() => client.GetAllBranches("owner", null)); await Assert.ThrowsAsync(() => client.GetAllBranches("", "repo")); await Assert.ThrowsAsync(() => client.GetAllBranches("owner", "")); } } public class TheGetAllContributorsMethod { [Fact] public void GetsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllContributors("owner", "name"); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "repos/owner/name/contributors"), Arg.Any>()); } [Fact] public async Task EnsuresArguments() { var client = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => client.GetAllContributors(null, "repo")); await Assert.ThrowsAsync(() => client.GetAllContributors("owner", null)); await Assert.ThrowsAsync(() => client.GetAllContributors("", "repo")); await Assert.ThrowsAsync(() => client.GetAllContributors("owner", "")); } } public class TheGetAllLanguagesMethod { [Fact] public void GetsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllLanguages("owner", "name"); connection.Received() .Get>(Arg.Is(u => u.ToString() == "repos/owner/name/languages")); } [Fact] public async Task EnsuresNonNullArguments() { var client = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => client.GetAllLanguages(null, "repo")); await Assert.ThrowsAsync(() => client.GetAllLanguages("owner", null)); await Assert.ThrowsAsync(() => client.GetAllLanguages("", "repo")); await Assert.ThrowsAsync(() => client.GetAllLanguages("owner", "")); } } public class TheGetAllTeamsMethod { [Fact] public void GetsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllTeams("owner", "name"); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "repos/owner/name/teams")); } [Fact] public async Task EnsuresNonNullArguments() { var client = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => client.GetAllTeams(null, "repo")); await Assert.ThrowsAsync(() => client.GetAllTeams("owner", null)); await Assert.ThrowsAsync(() => client.GetAllTeams("", "repo")); await Assert.ThrowsAsync(() => client.GetAllTeams("owner", "")); } } public class TheGetAllTagsMethod { [Fact] public void GetsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetAllTags("owner", "name"); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "repos/owner/name/tags")); } [Fact] public async Task EnsuresNonNullArguments() { var client = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => client.GetAllTags(null, "repo")); await Assert.ThrowsAsync(() => client.GetAllTags("owner", null)); await Assert.ThrowsAsync(() => client.GetAllTags("", "repo")); await Assert.ThrowsAsync(() => client.GetAllTags("owner", "")); } } public class TheGetBranchMethod { [Fact] public void GetsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); client.GetBranch("owner", "repo", "branch"); connection.Received() .Get(Arg.Is(u => u.ToString() == "repos/owner/repo/branches/branch"), null, "application/vnd.github.loki-preview+json"); } [Fact] public async Task EnsuresNonNullArguments() { var client = new RepositoriesClient(Substitute.For()); await Assert.ThrowsAsync(() => client.GetBranch(null, "repo", "branch")); await Assert.ThrowsAsync(() => client.GetBranch("owner", null, "branch")); await Assert.ThrowsAsync(() => client.GetBranch("owner", "repo", null)); await Assert.ThrowsAsync(() => client.GetBranch("", "repo", "branch")); await Assert.ThrowsAsync(() => client.GetBranch("owner", "", "branch")); await Assert.ThrowsAsync(() => client.GetBranch("owner", "repo", "")); } } public class TheEditMethod { [Fact] public void PatchesCorrectUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); var update = new RepositoryUpdate(); client.Edit("owner", "repo", update); connection.Received() .Patch(Arg.Is(u => u.ToString() == "repos/owner/repo"), Arg.Any()); } [Fact] public async Task EnsuresNonNullArguments() { var client = new RepositoriesClient(Substitute.For()); var update = new RepositoryUpdate(); await Assert.ThrowsAsync(() => client.Edit(null, "repo", update)); await Assert.ThrowsAsync(() => client.Edit("owner", null, update)); await Assert.ThrowsAsync(() => client.Edit("owner", "repo", null)); await Assert.ThrowsAsync(() => client.Edit("", "repo", update)); await Assert.ThrowsAsync(() => client.Edit("owner", "", update)); } } public class TheCompareMethod { [Fact] public async Task EnsureNonNullArguments() { var client = new RepositoryCommitsClient(Substitute.For()); await Assert.ThrowsAsync(() => client.Compare(null, "repo", "base", "head")); await Assert.ThrowsAsync(() => client.Compare("", "repo", "base", "head")); await Assert.ThrowsAsync(() => client.Compare("owner", null, "base", "head")); await Assert.ThrowsAsync(() => client.Compare("owner", "", "base", "head")); await Assert.ThrowsAsync(() => client.Compare("owner", "repo", null, "head")); await Assert.ThrowsAsync(() => client.Compare("owner", "repo", "", "head")); await Assert.ThrowsAsync(() => client.Compare("owner", "repo", "base", null)); await Assert.ThrowsAsync(() => client.Compare("owner", "repo", "base", "")); } [Fact] public void GetsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoryCommitsClient(connection); client.Compare("owner", "repo", "base", "head"); connection.Received() .Get(Arg.Is(u => u.ToString() == "repos/owner/repo/compare/base...head")); } [Fact] public void EncodesUrl() { var connection = Substitute.For(); var client = new RepositoryCommitsClient(connection); client.Compare("owner", "repo", "base", "shiftkey/my-cool-branch"); connection.Received() .Get(Arg.Is(u => u.ToString() == "repos/owner/repo/compare/base...shiftkey%2Fmy-cool-branch")); } } public class TheGetCommitMethod { [Fact] public async Task EnsureNonNullArguments() { var client = new RepositoryCommitsClient(Substitute.For()); await Assert.ThrowsAsync(() => client.Get(null, "repo", "reference")); await Assert.ThrowsAsync(() => client.Get("", "repo", "reference")); await Assert.ThrowsAsync(() => client.Get("owner", null, "reference")); await Assert.ThrowsAsync(() => client.Get("owner", "", "reference")); await Assert.ThrowsAsync(() => client.Get("owner", "repo", null)); await Assert.ThrowsAsync(() => client.Get("owner", "repo", "")); } [Fact] public void GetsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoryCommitsClient(connection); client.Get("owner", "name", "reference"); connection.Received() .Get(Arg.Is(u => u.ToString() == "repos/owner/name/commits/reference")); } } public class TheGetAllCommitsMethod { [Fact] public async Task EnsureNonNullArguments() { var client = new RepositoryCommitsClient(Substitute.For()); await Assert.ThrowsAsync(() => client.GetAll(null, "repo")); await Assert.ThrowsAsync(() => client.GetAll("", "repo")); await Assert.ThrowsAsync(() => client.GetAll("owner", null)); await Assert.ThrowsAsync(() => client.GetAll("owner", "")); await Assert.ThrowsAsync(() => client.GetAll("owner", "repo", null)); } [Fact] public void GetsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoryCommitsClient(connection); client.GetAll("owner", "name"); connection.Received() .GetAll(Arg.Is(u => u.ToString() == "repos/owner/name/commits"), Arg.Any>()); } } public class TheEditBranchMethod { [Fact] public void GetsCorrectUrl() { var connection = Substitute.For(); var client = new RepositoriesClient(connection); var update = new BranchUpdate(); const string previewAcceptsHeader = "application/vnd.github.loki-preview+json"; client.EditBranch("owner", "repo", "branch", update); connection.Received() .Patch(Arg.Is(u => u.ToString() == "repos/owner/repo/branches/branch"), Arg.Any(), previewAcceptsHeader); } [Fact] public async Task EnsuresNonNullArguments() { var client = new RepositoriesClient(Substitute.For()); var update = new BranchUpdate(); await Assert.ThrowsAsync(() => client.EditBranch(null, "repo", "branch", update)); await Assert.ThrowsAsync(() => client.EditBranch("owner", null, "branch", update)); await Assert.ThrowsAsync(() => client.EditBranch("owner", "repo", null, update)); await Assert.ThrowsAsync(() => client.EditBranch("owner", "repo", "branch", null)); await Assert.ThrowsAsync(() => client.EditBranch("", "repo", "branch", update)); await Assert.ThrowsAsync(() => client.EditBranch("owner", "", "branch", update)); await Assert.ThrowsAsync(() => client.EditBranch("owner", "repo", "", update)); } } } }