diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a14a8f59..ee2c1423 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,6 +105,7 @@ owned by that account. Then set the following environment variables: `OCTOKIT_GITHUBUSERNAME` (set this to the test account's username) `OCTOKIT_GITHUBPASSWORD` (set this to the test account's password) `OCTOKIT_GITHUBORGANIZATION` (set this to the test account's organization) +`OCTOKIT_PRIVATEREPOSITORIES` (set this to `TRUE` to indicate account has access to private repositories) Once these are set, the integration tests will be executed both when running the FullBuild MSBuild target, and when running the diff --git a/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs index 1960f146..625f5bf9 100644 --- a/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs +++ b/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Octokit; @@ -8,7 +9,7 @@ using Octokit.Tests.Helpers; public class RepositoriesClientTests { - public class TheCreateMethodForUser : IDisposable + public class TheCreateMethodForUser { [IntegrationTest] public async Task CreatesANewPublicRepository() @@ -43,30 +44,42 @@ public class RepositoriesClientTests } } - [IntegrationTest] + [PaidAccountTest] public async Task CreatesANewPrivateRepository() { var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; + + var userDetails = await github.User.Current(); + if (userDetails.Plan.PrivateRepos == 0) + { + throw new Exception("Test cannot complete, account is on free plan"); + } + var repoName = Helper.MakeNameWithTimestamp("private-repo"); - var createdRepository = await github.Repository.Create(new NewRepository - { - Name = repoName, - Private = true - }); + Repository createdRepository = null; try { + createdRepository = await github.Repository.Create(new NewRepository + { + Name = repoName, + Private = true + }); + Assert.True(createdRepository.Private); var repository = await github.Repository.Get(Helper.UserName, repoName); Assert.True(repository.Private); } finally { - Helper.DeleteRepo(createdRepository); + if (createdRepository != null) + { + Helper.DeleteRepo(createdRepository); + } } } @@ -288,7 +301,7 @@ public class RepositoriesClientTests } } - [IntegrationTest(Skip = "see https://github.com/octokit/octokit.net/issues/533 for the resolution to this failing tests")] + [PaidAccountTest] public async Task ThrowsPrivateRepositoryQuotaExceededExceptionWhenOverQuota() { var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) @@ -296,46 +309,36 @@ public class RepositoriesClientTests Credentials = Helper.Credentials }; - for (int i = 0; i < 5; i++) + var userDetails = await github.User.Current(); + var freePrivateSlots = userDetails.Plan.PrivateRepos - userDetails.OwnedPrivateRepos; + + if (userDetails.Plan.PrivateRepos == 0) { - var repoName = Helper.MakeNameWithTimestamp("private-repo" + i); - var repository = new NewRepository { Name = repoName, Private = true }; - await github.Repository.Create(repository); + throw new Exception("Test cannot complete, account is on free plan"); } - var thrown = await AssertEx.Throws( - async () => await github.Repository.Create(new NewRepository { Name = "x-private", Private = true })); - Assert.NotNull(thrown); - } + var createRepoTasks = + Enumerable.Range(0, (int)freePrivateSlots) + .Select(x => + { + var repoName = Helper.MakeNameWithTimestamp("private-repo-" + x); + var repository = new NewRepository { Name = repoName, Private = true }; + return github.Repository.Create(repository); + }); - public void Dispose() - { - var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) - { - Credentials = Helper.Credentials - }; + var createdRepositories = await Task.WhenAll(createRepoTasks); try { - // clean all the repositories for the current user - var repositories = github.Repository.GetAllForCurrent().Result; - - foreach (var repository in repositories.Where(x => x.Owner.Login == Helper.Credentials.Login)) - { - try - { - // only cleanup repositories the current user owns - github.Repository.Delete(repository.Owner.Login, repository.Name).Wait(); - } - catch (Exception) - { - - } - } + await Assert.ThrowsAsync( + () => github.Repository.Create(new NewRepository { Name = "x-private", Private = true })); } - catch (Exception ex) + finally { - Console.WriteLine("An unexpected exception occurred while retrieving repositories for the current user: " + ex); + var deleteRepos = createdRepositories + .Select(repo => github.Repository.Delete(repo.Owner.Login, repo.Name)); + + Task.WhenAll(deleteRepos).Wait(); } } } @@ -430,10 +433,17 @@ public class RepositoriesClientTests Assert.Equal("http://aUrl.to/nowhere", _repository.Homepage); } - [IntegrationTest] + [PaidAccountTest] public async Task UpdatesPrivate() { var github = CreateGitHubClient(); + + var userDetails = await github.User.Current(); + if (userDetails.Plan.PrivateRepos == 0) + { + throw new Exception("Test cannot complete, account is on free plan"); + } + var repoName = Helper.MakeNameWithTimestamp("public-repo"); _repository = await github.Repository.Create(new NewRepository { Name = repoName, AutoInit = true }); var update = new RepositoryUpdate { Name = repoName, Private = true }; diff --git a/Octokit.Tests.Integration/Helper.cs b/Octokit.Tests.Integration/Helper.cs index d2c73c90..3bdf981b 100644 --- a/Octokit.Tests.Integration/Helper.cs +++ b/Octokit.Tests.Integration/Helper.cs @@ -40,6 +40,14 @@ namespace Octokit.Tests.Integration public static Credentials Credentials { get { return _credentialsThunk.Value; }} + public static bool IsPaidAccount + { + get + { + return !String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("OCTOKIT_PRIVATEREPOSITORIES")); + } + } + public static void DeleteRepo(Repository repository) { if (repository != null) diff --git a/Octokit.Tests.Integration/Helpers/IntegrationTestAttribute.cs b/Octokit.Tests.Integration/Helpers/IntegrationTestAttribute.cs index 5d89feed..2836a787 100644 --- a/Octokit.Tests.Integration/Helpers/IntegrationTestAttribute.cs +++ b/Octokit.Tests.Integration/Helpers/IntegrationTestAttribute.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; +using System.Collections.Generic; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; using Xunit.Sdk; @@ -14,18 +10,12 @@ namespace Octokit.Tests.Integration { public IEnumerable Discover(ITestMethod testMethod, IAttributeInfo factAttribute) { - if (Helper.Credentials == null) - { - return Enumerable.Empty(); - } - else - { - return new [] { new XunitTestCase(testMethod) }; - } + return Helper.Credentials == null + ? Enumerable.Empty() + : new [] { new XunitTestCase(testMethod) }; } } - [XunitTestCaseDiscoverer("Octokit.Tests.Integration.IntegrationTestDiscoverer", "Octokit.Tests.Integration")] public class IntegrationTestAttribute : FactAttribute { diff --git a/Octokit.Tests.Integration/Helpers/PaidAccountTestAttribute.cs b/Octokit.Tests.Integration/Helpers/PaidAccountTestAttribute.cs new file mode 100644 index 00000000..04cd7b88 --- /dev/null +++ b/Octokit.Tests.Integration/Helpers/PaidAccountTestAttribute.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Linq; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Octokit.Tests.Integration +{ + public class PaidAccountTestDiscoverer : IXunitTestCaseDiscoverer + { + public IEnumerable Discover(ITestMethod testMethod, IAttributeInfo factAttribute) + { + if (Helper.Credentials == null) + return Enumerable.Empty(); + + if (!Helper.IsPaidAccount) + return Enumerable.Empty(); + + return new[] { new XunitTestCase(testMethod) }; + } + } + + [XunitTestCaseDiscoverer("Octokit.Tests.Integration.PaidAccountTestDiscoverer", "Octokit.Tests.Integration")] + public class PaidAccountTestAttribute : FactAttribute + { + } +} \ No newline at end of file diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 4c3917aa..61701b99 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -91,6 +91,7 @@ + diff --git a/Octokit/Clients/RepositoriesClient.cs b/Octokit/Clients/RepositoriesClient.cs index 818e36c0..346c96b8 100644 --- a/Octokit/Clients/RepositoriesClient.cs +++ b/Octokit/Clients/RepositoriesClient.cs @@ -94,6 +94,15 @@ namespace Octokit newRepository.Name, baseAddress, e); } + + if (String.Equals( + "please upgrade your plan to create a new private repository.", + errorMessage, + StringComparison.OrdinalIgnoreCase)) + { + throw new PrivateRepositoryQuotaExceededException(e); + } + if (String.Equals( "name can't be private. You are over your quota.", errorMessage, diff --git a/Octokit/Helpers/ApiErrorExtensions.cs b/Octokit/Helpers/ApiErrorExtensions.cs index 90a05f3d..bc74fc3a 100644 --- a/Octokit/Helpers/ApiErrorExtensions.cs +++ b/Octokit/Helpers/ApiErrorExtensions.cs @@ -7,7 +7,7 @@ namespace Octokit public static string FirstErrorMessageSafe(this ApiError apiError) { if (apiError == null) return null; - if (apiError.Errors == null) return null; + if (apiError.Errors == null) return apiError.Message; var firstError = apiError.Errors.FirstOrDefault(); if (firstError == null) return null; return firstError.Message;