From fb6adcb92b10db683fcf24bc2e4e79e6108062b6 Mon Sep 17 00:00:00 2001 From: Daniel Cazzulino Date: Tue, 8 Apr 2014 01:25:25 -0300 Subject: [PATCH] Implemented Get / IsMember for teams Issue #331. Added integration tests and a new attribute that requires a new environment variable with an organization to test. Conflicts: Octokit.Tests.Integration/Octokit.Tests.Integration.csproj --- .../Clients/TeamsClientTests.cs | 123 ++++++++++++++++++ Octokit.Tests.Integration/Helper.cs | 11 ++ .../Octokit.Tests.Integration.csproj | 4 +- .../OrganizationTestAttribute.cs | 19 +++ Octokit/Clients/ITeamsClient.cs | 21 +++ Octokit/Clients/TeamsClient.cs | 42 +++++- Octokit/Helpers/ApiUrls.cs | 13 +- 7 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 Octokit.Tests.Integration/Clients/TeamsClientTests.cs create mode 100644 Octokit.Tests.Integration/OrganizationTestAttribute.cs diff --git a/Octokit.Tests.Integration/Clients/TeamsClientTests.cs b/Octokit.Tests.Integration/Clients/TeamsClientTests.cs new file mode 100644 index 00000000..2017e3f4 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/TeamsClientTests.cs @@ -0,0 +1,123 @@ +using System.Linq; +using System.Net; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit; +using Octokit.Internal; +using Octokit.Tests.Helpers; +using Octokit.Tests.Integration; +using Xunit; +using System; +using System.Collections.Generic; +using Xunit.Sdk; + +public class TeamsClientTests +{ + public class TheCreateMethod + { + [OrganizationTest] + public async Task FailsWhenNotAuthenticated() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")); + var newTeam = new NewTeam("Test"); + + var e = await AssertEx.Throws(async + () => await github.Organization.Team.CreateTeam(Helper.Organization, newTeam)); + + Assert.Equal(HttpStatusCode.Unauthorized, e.StatusCode); + } + + [OrganizationTest] + public async Task FailsWhenAuthenticatedWithBadCredentials() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) + { + Credentials = new Credentials(Helper.Credentials.Login, "bad-password") + }; + var newTeam = new NewTeam("Test"); + + var e = await AssertEx.Throws(async + () => await github.Organization.Team.CreateTeam(Helper.Organization, newTeam)); + Assert.Equal(HttpStatusCode.Unauthorized, e.StatusCode); + } + + [OrganizationTest] + public async Task SucceedsWhenAuthenticated() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) + { + Credentials = Helper.Credentials + }; + + var newTeam = new NewTeam(Guid.NewGuid().ToString()); + + var team = await github.Organization.Team.CreateTeam(Helper.Organization, newTeam); + + Assert.Equal(newTeam.Name, team.Name); + } + } + + public class TheIsMemberMethod + { + readonly Team team; + + public TheIsMemberMethod() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; + + team = github.Organization.Team.GetAllTeams(Helper.Organization).Result.First(); + } + + //TODO: seems like a bug in Github: it's actually returning the membership information! + //Maybe because it's a public organization? + //[OrganizationTest] + public async Task FailsWhenNotAuthenticated() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")); + + var e = await AssertEx.Throws(async + () => await github.Organization.Team.IsMember(team.Id, Helper.UserName)); + + Assert.Equal(HttpStatusCode.Unauthorized, e.StatusCode); + } + + [OrganizationTest] + public async Task FailsWhenAuthenticatedWithBadCredentials() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) + { + Credentials = new Credentials(Helper.Credentials.Login, "bad-password") + }; + + var e = await AssertEx.Throws(async + () => await github.Organization.Team.IsMember(team.Id, Helper.UserName)); + Assert.Equal(HttpStatusCode.Unauthorized, e.StatusCode); + } + + [OrganizationTest] + public async Task GetsIsMemberWhenAuthenticated() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) + { + Credentials = Helper.Credentials + }; + + var isMember = await github.Organization.Team.IsMember(team.Id, Helper.UserName); + + Assert.True(isMember); + } + + [OrganizationTest] + public async Task GetsIsMemberFalseForNonMemberWhenAuthenticated() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) + { + Credentials = Helper.Credentials + }; + + var isMember = await github.Organization.Team.IsMember(team.Id, "foo"); + + Assert.False(isMember); + } + } +} diff --git a/Octokit.Tests.Integration/Helper.cs b/Octokit.Tests.Integration/Helper.cs index caf73046..3b44476f 100644 --- a/Octokit.Tests.Integration/Helper.cs +++ b/Octokit.Tests.Integration/Helper.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Net.Http.Headers; namespace Octokit.Tests.Integration @@ -9,6 +10,7 @@ namespace Octokit.Tests.Integration { var githubUsername = Environment.GetEnvironmentVariable("OCTOKIT_GITHUBUSERNAME"); UserName = githubUsername; + Organization = Environment.GetEnvironmentVariable("OCTOKIT_GITHUBORGANIZATION"); var githubToken = Environment.GetEnvironmentVariable("OCTOKIT_OAUTHTOKEN"); @@ -23,7 +25,16 @@ namespace Octokit.Tests.Integration return new Credentials(githubUsername, githubPassword); }); + static Helper() + { + // Force reading of environment variables. + // This wasn't happening if UserName/Organization were + // retrieved before Credentials. + Debug.WriteIf(Credentials == null, "No credentials specified."); + } + public static string UserName { get; private set; } + public static string Organization { get; private set; } public static Credentials Credentials { get { return _credentialsThunk.Value; }} diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 31d36283..dc62d366 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -78,9 +78,11 @@ + + @@ -121,4 +123,4 @@ --> - \ No newline at end of file + diff --git a/Octokit.Tests.Integration/OrganizationTestAttribute.cs b/Octokit.Tests.Integration/OrganizationTestAttribute.cs new file mode 100644 index 00000000..61b00cfb --- /dev/null +++ b/Octokit.Tests.Integration/OrganizationTestAttribute.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Xunit.Sdk; + +namespace Octokit.Tests.Integration +{ + public class OrganizationTestAttribute : IntegrationTestAttribute + { + protected override IEnumerable EnumerateTestCommands(IMethodInfo testMethod) + { + if (Helper.Organization == null) + return new[] + { + new SkipCommand(testMethod, MethodUtility.GetDisplayName(testMethod), "Automation settings not configured. Please set the OCTOKIT_GITHUBORGANIZATION environment variable to a GitHub organization owned by the test account specified in OCTOKIT_GITHUBUSERNAME.") + }; + else + return base.EnumerateTestCommands(testMethod); + } + } +} diff --git a/Octokit/Clients/ITeamsClient.cs b/Octokit/Clients/ITeamsClient.cs index 8cafd72b..be759a8e 100644 --- a/Octokit/Clients/ITeamsClient.cs +++ b/Octokit/Clients/ITeamsClient.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; #endif using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; namespace Octokit { @@ -13,6 +14,18 @@ namespace Octokit /// public interface ITeamsClient { + /// + /// Gets a single by identifier. + /// + /// + /// https://developer.github.com/v3/orgs/teams/#get-team + /// + /// The team identifier. + /// The with the given identifier. + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", + Justification = "Method makes a network request")] + Task Get(int id); + /// /// Returns all s for the current org. /// @@ -41,5 +54,13 @@ namespace Octokit /// Task Delete(int id); + /// + /// Gets whether the user with the given + /// is a member of the team with the given . + /// + /// The team to check. + /// The user to check. + /// if the user is a member of the team; otherwise. + Task IsMember(int id, string login); } } diff --git a/Octokit/Clients/TeamsClient.cs b/Octokit/Clients/TeamsClient.cs index d2ae08c9..462ea403 100644 --- a/Octokit/Clients/TeamsClient.cs +++ b/Octokit/Clients/TeamsClient.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; #endif using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; namespace Octokit { @@ -23,6 +24,21 @@ namespace Octokit { } + /// + /// Gets a single by identifier. + /// + /// + /// https://developer.github.com/v3/orgs/teams/#get-team + /// + /// The team identifier. + /// The with the given identifier. + public Task Get(int id) + { + var endpoint = ApiUrls.Teams(id); + + return ApiConnection.Get(endpoint); + } + /// /// Returns all s for the current org. /// @@ -60,7 +76,7 @@ namespace Octokit { Ensure.ArgumentNotNull(team, "team"); - var endpoint = ApiUrls.TeamsUpdateOrDelete(id); + var endpoint = ApiUrls.Teams(id); return ApiConnection.Patch(endpoint, team); } @@ -71,8 +87,30 @@ namespace Octokit /// public Task Delete(int id) { - var endpoint = ApiUrls.TeamsUpdateOrDelete(id); + var endpoint = ApiUrls.Teams(id); return ApiConnection.Delete(endpoint); } + + /// + /// Gets whether the user with the given + /// is a member of the team with the given . + /// + /// The team to check. + /// The user to check. + /// if the user is a member of the team; otherwise. + public async Task IsMember(int id, string login) + { + var endpoint = ApiUrls.TeamMember(id, login); + + try + { + var response = await ApiConnection.Connection.GetAsync(endpoint); + return response.StatusCode == System.Net.HttpStatusCode.NoContent; + } + catch (NotFoundException) + { + return false; + } + } } } diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 2e3fe129..5f872867 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -937,15 +937,24 @@ namespace Octokit /// /// returns the for teams - /// use for update or deleting a team /// /// /// - public static Uri TeamsUpdateOrDelete(int id) + public static Uri Teams(int id) { return "teams/{0}".FormatUri(id); } + /// + /// returns the for team members + /// + /// The team id + /// The user login. + public static Uri TeamMember(int id, string login) + { + return "teams/{0}/members/{1}".FormatUri(id, login); + } + /// /// returns the for teams /// use for update or deleting a team