diff --git a/Octokit.Reactive/Clients/IObservableActivitiesClient.cs b/Octokit.Reactive/Clients/IObservableActivitiesClient.cs
index b3698f33..7be9ecd5 100644
--- a/Octokit.Reactive/Clients/IObservableActivitiesClient.cs
+++ b/Octokit.Reactive/Clients/IObservableActivitiesClient.cs
@@ -3,5 +3,7 @@
public interface IObservableActivitiesClient
{
IObservableEventsClient Events { get; }
+ IObservableWatchedClient Watched { get; }
+ IObservableStarredClient Starred { get; }
}
}
\ No newline at end of file
diff --git a/Octokit.Reactive/Clients/IObservableFollowersClient.cs b/Octokit.Reactive/Clients/IObservableFollowersClient.cs
new file mode 100644
index 00000000..f959b833
--- /dev/null
+++ b/Octokit.Reactive/Clients/IObservableFollowersClient.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Reactive;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Octokit.Reactive
+{
+ public interface IObservableFollowersClient
+ {
+ ///
+ /// List the authenticated user’s followers
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A of s that follow the authenticated user.
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ IObservable GetAllForCurrent();
+
+ ///
+ /// List a user’s followers
+ ///
+ /// The login name for the user
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A of s that follow the passed user.
+ IObservable GetAll(string login);
+
+ ///
+ /// List who the authenticated user is following
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A of s that the authenticated user follows.
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ IObservable GetFollowingForCurrent();
+
+ ///
+ /// List who a user is following
+ ///
+ /// The login name of the user
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A of s that the passed user follows.
+ IObservable GetFollowing(string login);
+
+ ///
+ /// Check if the authenticated user follows another user
+ ///
+ /// The login name of the other user
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A bool representing the success of the operation.
+ IObservable IsFollowingForCurrent(string following);
+
+ ///
+ /// Check if one user follows another user
+ ///
+ /// The login name of the user
+ /// The login name of the other user
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A bool representing the success of the operation.
+ IObservable IsFollowing(string login, string following);
+
+ ///
+ /// Follow a user
+ ///
+ /// The login name of the user to follow
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A bool representing the success of the operation.
+ IObservable Follow(string login);
+
+ ///
+ /// Unfollow a user
+ ///
+ /// The login name of the user to unfollow
+ ///
+ /// See the API documentation for more information.
+ ///
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Unfollow",
+ Justification = "Unfollow is consistent with the GitHub website")]
+ IObservable Unfollow(string login);
+ }
+}
diff --git a/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs b/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs
index ecf58209..e06e3033 100644
--- a/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs
+++ b/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Octokit.Reactive
@@ -8,7 +7,7 @@ namespace Octokit.Reactive
{
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "Makes a network request")]
- IObservable> GetEmojis();
+ IObservable GetEmojis();
IObservable RenderRawMarkdown(string markdown);
}
diff --git a/Octokit.Reactive/Clients/IObservableWatchedClient.cs b/Octokit.Reactive/Clients/IObservableWatchedClient.cs
new file mode 100644
index 00000000..7a2c9684
--- /dev/null
+++ b/Octokit.Reactive/Clients/IObservableWatchedClient.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Octokit.Reactive
+{
+ public interface IObservableWatchedClient
+ {
+ ///
+ /// Retrieves all of the watchers for the passed repository
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// Thrown if the client is not authenticated
+ /// A of s watching the passed repository
+ IObservable GetAllWatchers(string owner, string name);
+
+ ///
+ /// Retrieves all of the watched (ies) for the current user
+ ///
+ /// Thrown if the client is not authenticated
+ /// A of
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ IObservable GetAllForCurrent();
+
+ ///
+ /// Retrieves all of the (ies) watched by the specified user
+ ///
+ /// The login of the user
+ /// Thrown if the client is not authenticated
+ /// A watched by the specified user
+ IObservable GetAllForUser(string user);
+
+ ///
+ /// Check if a repository is watched by the current authenticated user
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// Thrown if the client is not authenticated
+ /// A bool representing the success of the operation
+ IObservable CheckStarred(string owner, string name);
+
+ ///
+ /// Stars a repository for the authenticated user.
+ ///
+ /// The owner of the repository to star
+ /// The name of the repository to star
+ /// A instance describing the new subscription to create
+ /// A bool representing the success of starring
+ IObservable WatchRepo(string owner, string name, NewSubscription newSubscription);
+
+ ///
+ /// Unstars a repository for the authenticated user.
+ ///
+ /// The owner of the repository to unstar
+ /// The name of the repository to unstar
+ /// A bool representing the success of the operation
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Unwatch",
+ Justification = "Unwatch is consistent with the GitHub website")]
+ IObservable UnwatchRepo(string owner, string name);
+ }
+}
\ No newline at end of file
diff --git a/Octokit.Reactive/Clients/ObservableActivitiesClient.cs b/Octokit.Reactive/Clients/ObservableActivitiesClient.cs
index c2150c59..4391c2d3 100644
--- a/Octokit.Reactive/Clients/ObservableActivitiesClient.cs
+++ b/Octokit.Reactive/Clients/ObservableActivitiesClient.cs
@@ -7,7 +7,13 @@
Ensure.ArgumentNotNull(client, "client");
Events = new ObservableEventsClient(client);
+ Watched = new ObservableWatchedClient(client);
+ Starred = new ObservableStarredClient(client);
}
public IObservableEventsClient Events { get; private set; }
+
+ public IObservableWatchedClient Watched { get; private set; }
+
+ public IObservableStarredClient Starred { get; private set; }
}
}
\ No newline at end of file
diff --git a/Octokit.Reactive/Clients/ObservableFollowersClient.cs b/Octokit.Reactive/Clients/ObservableFollowersClient.cs
new file mode 100644
index 00000000..e9dadc2e
--- /dev/null
+++ b/Octokit.Reactive/Clients/ObservableFollowersClient.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Reactive;
+using System.Reactive.Threading.Tasks;
+using Octokit.Reactive.Internal;
+
+namespace Octokit.Reactive
+{
+ public class ObservableFollowersClient : IObservableFollowersClient
+ {
+ readonly IFollowersClient _client;
+ readonly IConnection _connection;
+
+ ///
+ /// Initializes a new User Followers API client.
+ ///
+ /// An used to make the requests
+ public ObservableFollowersClient(IGitHubClient client)
+ {
+ Ensure.ArgumentNotNull(client, "client");
+
+ _client = client.User.Followers;
+ _connection = client.Connection;
+ }
+
+ ///
+ /// List the authenticated user’s followers
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A of s that follow the authenticated user.
+ public IObservable GetAllForCurrent()
+ {
+ return _connection.GetAndFlattenAllPages(ApiUrls.Followers());
+ }
+
+ ///
+ /// List a user’s followers
+ ///
+ /// The login name for the user
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A of s that follow the passed user.
+ public IObservable GetAll(string login)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(login, "login");
+
+ return _connection.GetAndFlattenAllPages(ApiUrls.Followers(login));
+ }
+
+ ///
+ /// List who the authenticated user is following
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A of s that the authenticated user follows.
+ public IObservable GetFollowingForCurrent()
+ {
+ return _connection.GetAndFlattenAllPages(ApiUrls.Following());
+ }
+
+ ///
+ /// List who a user is following
+ ///
+ /// The login name of the user
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A of s that the passed user follows.
+ public IObservable GetFollowing(string login)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(login, "login");
+
+ return _connection.GetAndFlattenAllPages(ApiUrls.Following(login));
+ }
+
+ ///
+ /// Check if the authenticated user follows another user
+ ///
+ /// The login name of the other user
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A bool representing the success of the operation.
+ public IObservable IsFollowingForCurrent(string following)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(following, "following");
+
+ return _client.IsFollowingForCurrent(following).ToObservable();
+ }
+
+ ///
+ /// Check if one user follows another user
+ ///
+ /// The login name of the user
+ /// The login name of the other user
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A bool representing the success of the operation.
+ public IObservable IsFollowing(string login, string following)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(login, "login");
+ Ensure.ArgumentNotNullOrEmptyString(following, "following");
+
+ return _client.IsFollowing(login, following).ToObservable();
+ }
+
+ ///
+ /// Follow a user
+ ///
+ /// The login name of the user to follow
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// A bool representing the success of the operation.
+ public IObservable Follow(string login)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(login, "login");
+
+ return _client.Follow(login).ToObservable();
+ }
+
+ ///
+ /// Unfollow a user
+ ///
+ /// The login name of the user to unfollow
+ ///
+ /// See the API documentation for more information.
+ ///
+ ///
+ public IObservable Unfollow(string login)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(login, "login");
+
+ return _client.Unfollow(login).ToObservable();
+ }
+ }
+}
diff --git a/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs b/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs
index 702e7ee1..33ce4891 100644
--- a/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs
+++ b/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs
@@ -1,5 +1,5 @@
using System;
-using System.Collections.Generic;
+using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
namespace Octokit.Reactive
@@ -15,9 +15,9 @@ namespace Octokit.Reactive
_client = client;
}
- public IObservable> GetEmojis()
+ public IObservable GetEmojis()
{
- return _client.GetEmojis().ToObservable();
+ return _client.GetEmojis().ToObservable().SelectMany(e => e);
}
public IObservable RenderRawMarkdown(string markdown)
diff --git a/Octokit.Reactive/Clients/ObservableSearchClient.cs b/Octokit.Reactive/Clients/ObservableSearchClient.cs
index c417285e..71b1c038 100644
--- a/Octokit.Reactive/Clients/ObservableSearchClient.cs
+++ b/Octokit.Reactive/Clients/ObservableSearchClient.cs
@@ -61,7 +61,7 @@ namespace Octokit.Reactive
public IObservable SearchCode(SearchCodeRequest request)
{
Ensure.ArgumentNotNull(request, "request");
- return _connection.GetAndFlattenAllPages(ApiUrls.SearchCode(), request.ToParametersDictionary());
+ return _connection.GetAndFlattenAllPages(ApiUrls.SearchCode(), request.Parameters);
}
}
}
\ No newline at end of file
diff --git a/Octokit.Reactive/Clients/ObservableStarredClient.cs b/Octokit.Reactive/Clients/ObservableStarredClient.cs
index 7439c111..d94747c6 100644
--- a/Octokit.Reactive/Clients/ObservableStarredClient.cs
+++ b/Octokit.Reactive/Clients/ObservableStarredClient.cs
@@ -5,7 +5,7 @@ using Octokit.Reactive.Internal;
namespace Octokit.Reactive
{
- public class ObservableStarredClient
+ public class ObservableStarredClient : IObservableStarredClient
{
private IStarredClient _client;
private IConnection _connection;
diff --git a/Octokit.Reactive/Clients/ObservableTreesClient.cs b/Octokit.Reactive/Clients/ObservableTreesClient.cs
index 2e3ba031..47d24578 100644
--- a/Octokit.Reactive/Clients/ObservableTreesClient.cs
+++ b/Octokit.Reactive/Clients/ObservableTreesClient.cs
@@ -12,7 +12,7 @@ namespace Octokit.Reactive
{
Ensure.ArgumentNotNull(client, "client");
- _client = client.Tree;
+ _client = client.GitDatabase.Tree;
}
///
diff --git a/Octokit.Reactive/Clients/ObservableWatchedClient.cs b/Octokit.Reactive/Clients/ObservableWatchedClient.cs
new file mode 100644
index 00000000..5a97c83d
--- /dev/null
+++ b/Octokit.Reactive/Clients/ObservableWatchedClient.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Reactive.Threading.Tasks;
+
+using Octokit.Reactive.Internal;
+
+namespace Octokit.Reactive
+{
+ public class ObservableWatchedClient : IObservableWatchedClient
+ {
+ private IWatchedClient _client;
+ private IConnection _connection;
+
+ public ObservableWatchedClient(IGitHubClient client)
+ {
+ Ensure.ArgumentNotNull(client, "client");
+
+ _client = client.Activity.Watching;
+ _connection = client.Connection;
+ }
+
+ ///
+ /// Retrieves all of the watchers for the passed repository
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// Thrown if the client is not authenticated
+ /// A of s watching the passed repository
+ public IObservable GetAllWatchers(string owner, string name)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
+ Ensure.ArgumentNotNullOrEmptyString(name, "name");
+
+ return _connection.GetAndFlattenAllPages(ApiUrls.Watchers(owner, name));
+ }
+
+ ///
+ /// Retrieves all of the watched (ies) for the current user
+ ///
+ /// Thrown if the client is not authenticated
+ /// A of
+ public IObservable GetAllForCurrent()
+ {
+ return _connection.GetAndFlattenAllPages(ApiUrls.Watched());
+ }
+
+ ///
+ /// Retrieves all of the (ies) watched by the specified user
+ ///
+ /// The login of the user
+ /// Thrown if the client is not authenticated
+ /// A watched by the specified user
+ public IObservable GetAllForUser(string user)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(user, "user");
+
+ return _connection.GetAndFlattenAllPages(ApiUrls.WatchedByUser(user));
+ }
+
+ ///
+ /// Check if a repository is watched by the current authenticated user
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// Thrown if the client is not authenticated
+ /// A bool representing the success of the operation
+ public IObservable CheckStarred(string owner, string name)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
+ Ensure.ArgumentNotNullOrEmptyString(name, "name");
+
+ return _client.CheckWatched(owner, name).ToObservable();
+ }
+
+ ///
+ /// Stars a repository for the authenticated user.
+ ///
+ /// The owner of the repository to star
+ /// The name of the repository to star
+ /// A instance describing the new subscription to create
+ /// A bool representing the success of starring
+ public IObservable WatchRepo(string owner, string name, NewSubscription newSubscription)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
+ Ensure.ArgumentNotNullOrEmptyString(name, "name");
+
+ return _client.WatchRepo(owner, name, newSubscription).ToObservable();
+ }
+
+ ///
+ /// Unstars a repository for the authenticated user.
+ ///
+ /// The owner of the repository to unstar
+ /// The name of the repository to unstar
+ /// A bool representing the success of the operation
+ public IObservable UnwatchRepo(string owner, string name)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
+ Ensure.ArgumentNotNullOrEmptyString(name, "name");
+
+ return _client.UnwatchRepo(owner, name).ToObservable();
+ }
+ }
+}
diff --git a/Octokit.Reactive/Octokit.Reactive-Mono.csproj b/Octokit.Reactive/Octokit.Reactive-Mono.csproj
index aea65088..0f9a5321 100644
--- a/Octokit.Reactive/Octokit.Reactive-Mono.csproj
+++ b/Octokit.Reactive/Octokit.Reactive-Mono.csproj
@@ -122,6 +122,10 @@
+
+
+
+
diff --git a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj
index 6dfc2a78..5c093854 100644
--- a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj
+++ b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj
@@ -131,6 +131,10 @@
+
+
+
+
diff --git a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj
index ef06fde1..52331941 100644
--- a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj
+++ b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj
@@ -126,6 +126,10 @@
+
+
+
+
diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj
index 876530db..6bc21ae3 100644
--- a/Octokit.Reactive/Octokit.Reactive.csproj
+++ b/Octokit.Reactive/Octokit.Reactive.csproj
@@ -77,6 +77,8 @@
+
+
@@ -123,9 +125,11 @@
+
+
diff --git a/Octokit.Tests.Integration/Clients/FollowersClientTests.cs b/Octokit.Tests.Integration/Clients/FollowersClientTests.cs
new file mode 100644
index 00000000..4d7f1e0f
--- /dev/null
+++ b/Octokit.Tests.Integration/Clients/FollowersClientTests.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+using Octokit;
+using Octokit.Tests.Integration;
+using Xunit;
+
+public class FollowersClientTests : IDisposable
+{
+ readonly GitHubClient _github;
+ readonly User _currentUser;
+
+ public FollowersClientTests()
+ {
+ _github = new GitHubClient(new ProductHeaderValue("OctokitTests"))
+ {
+ Credentials = Helper.Credentials
+ };
+ _currentUser = _github.User.Current().Result;
+ }
+
+ [IntegrationTest]
+ public async Task ReturnsUsersTheCurrentUserIsFollowing()
+ {
+ await _github.User.Followers.Follow("alfhenrik");
+
+ var following = await _github.User.Followers.GetFollowingForCurrent();
+
+ Assert.NotNull(following);
+ Assert.True(following.Any(f => f.Login == "alfhenrik"));
+ }
+
+ [IntegrationTest]
+ public async Task ReturnsUsersTheUserIsFollowing()
+ {
+ var following = await _github.User.Followers.GetFollowing("alfhenrik");
+
+ Assert.NotNull(following);
+ Assert.NotEmpty(following);
+ }
+
+ [IntegrationTest]
+ public async Task ReturnsUsersFollowingTheUser()
+ {
+ await _github.User.Followers.Follow("alfhenrik");
+
+ var followers = await _github.User.Followers.GetAll("alfhenrik");
+
+ Assert.NotEmpty(followers);
+ Assert.True(followers.Any(f => f.Login == _currentUser.Login));
+ }
+
+ [IntegrationTest]
+ public async Task ChecksIfIsFollowingUserWhenFollowingUser()
+ {
+ await _github.User.Followers.Follow("alfhenrik");
+
+ var isFollowing = await _github.User.Followers.IsFollowingForCurrent("alfhenrik");
+
+ Assert.True(isFollowing);
+ }
+
+ [IntegrationTest]
+ public async Task ChecksIfIsFollowingUserWhenNotFollowingUser()
+ {
+ var isFollowing = await _github.User.Followers.IsFollowingForCurrent("alfhenrik");
+
+ Assert.False(isFollowing);
+ }
+
+ [IntegrationTest]
+ public async Task FollowUserNotBeingFollowedByTheUser()
+ {
+ var result = await _github.User.Followers.Follow("alfhenrik");
+ var following = await _github.User.Followers.GetFollowingForCurrent();
+
+ Assert.True(result);
+ Assert.NotEmpty(following);
+ Assert.True(following.Any(f => f.Login == "alfhenrik"));
+ }
+
+ [IntegrationTest]
+ public async Task UnfollowUserBeingFollowedByTheUser()
+ {
+ await _github.User.Followers.Follow("alfhenrik");
+ var followers = await _github.User.Followers.GetAll("alfhenrik");
+ Assert.True(followers.Any(f => f.Login == _currentUser.Login));
+
+ await _github.User.Followers.Unfollow("alfhenrik");
+ followers = await _github.User.Followers.GetAll("alfhenrik");
+ Assert.False(followers.Any(f => f.Login == _currentUser.Login));
+ }
+
+ [IntegrationTest]
+ public async Task UnfollowUserNotBeingFollowedTheUser()
+ {
+ var followers = await _github.User.Followers.GetAll("alfhenrik");
+ Assert.False(followers.Any(f => f.Login == _currentUser.Login));
+
+ await _github.User.Followers.Unfollow("alfhenrik");
+ }
+
+ public void Dispose()
+ {
+ _github.User.Followers.Unfollow("alfhenrik");
+ }
+}
diff --git a/Octokit.Tests.Integration/Clients/UsersClientTests.cs b/Octokit.Tests.Integration/Clients/UsersClientTests.cs
index 69d7c8ac..23de66ec 100644
--- a/Octokit.Tests.Integration/Clients/UsersClientTests.cs
+++ b/Octokit.Tests.Integration/Clients/UsersClientTests.cs
@@ -102,6 +102,7 @@ public class UsersClientTests
public class TheGetEmailsMethod
{
+ [IntegrationTest]
public async Task RetrievesEmailsForUser()
{
var github = new GitHubClient(new ProductHeaderValue("OctokitTests"))
@@ -111,10 +112,9 @@ public class UsersClientTests
var emails = await github.User.GetEmails();
- Assert.Equal(1, emails.Count());
- Assert.Equal("test-octowin@example.com", emails.First().Email);
- Assert.True(emails.First().Primary);
- Assert.False(emails.First().Verified);
+ Assert.NotEmpty(emails);
+ var email = emails.First();
+ Assert.True(email.Primary);
}
}
}
diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj
index 5cc50133..ed3e147d 100644
--- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj
+++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj
@@ -71,6 +71,7 @@
+
diff --git a/Octokit.Tests/Clients/FollowersClientTests.cs b/Octokit.Tests/Clients/FollowersClientTests.cs
new file mode 100644
index 00000000..9c24b839
--- /dev/null
+++ b/Octokit.Tests/Clients/FollowersClientTests.cs
@@ -0,0 +1,274 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Threading.Tasks;
+using NSubstitute;
+using Octokit.Internal;
+using Octokit.Tests;
+using Octokit.Tests.Helpers;
+using Xunit;
+using Xunit.Extensions;
+
+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 FollowersClientTests
+ {
+ public class TheConstructor
+ {
+ [Fact]
+ public void EnsuresNonNullArguments()
+ {
+ Assert.Throws(() => new FollowersClient(null));
+ }
+ }
+
+ public class TheGetAllForCurrentMethod
+ {
+ [Fact]
+ public void RequestsTheCorrectUrl()
+ {
+ var connection = Substitute.For();
+ var client = new FollowersClient(connection);
+
+ client.GetAllForCurrent();
+
+ connection.Received().GetAll(
+ Arg.Is(u => u.ToString() == "user/followers"));
+ }
+ }
+
+ public class TheGetAllMethod
+ {
+ [Fact]
+ public void RequestsTheCorrectUrl()
+ {
+ var connection = Substitute.For();
+ var client = new FollowersClient(connection);
+
+ client.GetAll("alfhenrik");
+
+ connection.Received().GetAll(
+ Arg.Is(u => u.ToString() == "users/alfhenrik/followers"));
+ }
+
+ [Fact]
+ public void EnsureNonNullArguments()
+ {
+ var connection = Substitute.For();
+ var client = new FollowersClient(connection);
+
+ AssertEx.Throws(async () => await client.GetAll(null));
+ AssertEx.Throws(async () => await client.GetAll(""));
+ }
+ }
+
+ public class TheGetFollowingForCurrentMethod
+ {
+ [Fact]
+ public void RequestsTheCorrectUrl()
+ {
+ var connection = Substitute.For();
+ var client = new FollowersClient(connection);
+
+ client.GetFollowingForCurrent();
+
+ connection.Received().GetAll(Arg.Is(u => u.ToString() == "user/following"));
+ }
+ }
+
+ public class TheGetFollowingMethod
+ {
+ [Fact]
+ public void RequestsTheCorrectUrl()
+ {
+ var connection = Substitute.For();
+ var client = new FollowersClient(connection);
+
+ client.GetFollowing("alfhenrik");
+
+ connection.Received().GetAll(Arg.Is(u => u.ToString() == "users/alfhenrik/following"));
+ }
+
+ [Fact]
+ public void EnsuresNonNullArguments()
+ {
+ var connection = Substitute.For();
+ var client = new FollowersClient(connection);
+
+ AssertEx.Throws(async () => await client.GetFollowing(null));
+ AssertEx.Throws(async () => await client.GetFollowing(""));
+ }
+ }
+
+ public class TheIsFollowingForCurrentMethod
+ {
+ [Theory]
+ [InlineData(HttpStatusCode.NoContent, true)]
+ [InlineData(HttpStatusCode.NotFound, false)]
+ public async Task RequestsCorrectValueForStatusCode(HttpStatusCode status, bool expected)
+ {
+ var response = Task.Factory.StartNew>(() =>
+ new ApiResponse