diff --git a/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs b/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs
index 3888b188..639acde1 100644
--- a/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs
+++ b/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs
@@ -116,6 +116,14 @@ namespace Octokit.Reactive
///
IObservableStatisticsClient Statistics { get; }
+ ///
+ /// Client for GitHub's Repository Comments API.
+ ///
+ ///
+ /// See the Repository Comments API documentation for more information.
+ ///
+ IObservableRepositoryCommentsClient RepositoryComments { get; }
+
///
/// Gets all the branches for the specified repository.
///
diff --git a/Octokit.Reactive/Clients/IObservableRepositoryCommentsClient.cs b/Octokit.Reactive/Clients/IObservableRepositoryCommentsClient.cs
new file mode 100644
index 00000000..2a1faef4
--- /dev/null
+++ b/Octokit.Reactive/Clients/IObservableRepositoryCommentsClient.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Reactive;
+
+namespace Octokit.Reactive
+{
+ public interface IObservableRepositoryCommentsClient
+ {
+ ///
+ /// Gets a single Repository Comment by number.
+ ///
+ /// http://developer.github.com/v3/repos/comments/#get-a-single-commit-comment
+ /// The owner of the repository
+ /// The name of the repository
+ /// The comment id
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get",
+ Justification = "Method makes a network request")]
+ IObservable Get(string owner, string name, int number);
+
+ ///
+ /// Gets Commit Comments for a repository.
+ ///
+ /// http://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository
+ /// The owner of the repository
+ /// The name of the repository
+ ///
+ IObservable GetForRepository(string owner, string name);
+
+ ///
+ /// Gets Commit Comments for a specified Commit.
+ ///
+ /// http://developer.github.com/v3/repos/comments/#list-comments-for-a-single-commit
+ /// The owner of the repository
+ /// The name of the repository
+ /// The sha of the commit
+ ///
+ IObservable GetForCommit(string owner, string name, string sha);
+
+ ///
+ /// Creates a new Commit Comment for a specified Commit.
+ ///
+ /// http://developer.github.com/v3/repos/comments/#create-a-commit-comment
+ /// The owner of the repository
+ /// The name of the repository
+ /// The sha reference of commit
+ /// The new comment to add to the commit
+ ///
+ IObservable Create(string owner, string name, string sha, NewCommitComment newCommitComment);
+
+ ///
+ /// Updates a specified Commit Comment.
+ ///
+ /// http://developer.github.com/v3/repos/comments/#update-a-commit-comment
+ /// The owner of the repository
+ /// The name of the repository
+ /// The comment number
+ /// The modified comment
+ ///
+ IObservable Update(string owner, string name, int number, string commentUpdate);
+
+ ///
+ /// Deletes the specified Commit Comment
+ ///
+ /// http://developer.github.com/v3/repos/comments/#delete-a-commit-comment
+ /// The owner of the repository
+ /// The name of the repository
+ /// The comment id
+ ///
+ IObservable Delete(string owner, string name, int number);
+ }
+}
diff --git a/Octokit.Reactive/Clients/IObservableSearchClient.cs b/Octokit.Reactive/Clients/IObservableSearchClient.cs
index b4273750..d3eb6135 100644
--- a/Octokit.Reactive/Clients/IObservableSearchClient.cs
+++ b/Octokit.Reactive/Clients/IObservableSearchClient.cs
@@ -1,6 +1,4 @@
using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Reactive;
namespace Octokit.Reactive
{
@@ -15,7 +13,7 @@ namespace Octokit.Reactive
///
///
/// List of repositories
- IObservable SearchRepo(SearchRepositoriesRequest search);
+ IObservable SearchRepo(SearchRepositoriesRequest search);
///
/// search users
@@ -23,7 +21,7 @@ namespace Octokit.Reactive
///
///
/// List of users
- IObservable SearchUsers(SearchUsersRequest search);
+ IObservable SearchUsers(SearchUsersRequest search);
///
/// search issues
@@ -31,7 +29,7 @@ namespace Octokit.Reactive
///
///
/// List of issues
- IObservable SearchIssues(SearchIssuesRequest search);
+ IObservable SearchIssues(SearchIssuesRequest search);
///
/// search code
@@ -39,6 +37,6 @@ namespace Octokit.Reactive
///
///
/// List of files
- IObservable SearchCode(SearchCodeRequest search);
+ IObservable SearchCode(SearchCodeRequest search);
}
}
\ No newline at end of file
diff --git a/Octokit.Reactive/Clients/IObservableStatisticsClient.cs b/Octokit.Reactive/Clients/IObservableStatisticsClient.cs
index 551189a2..5bb9a8bc 100644
--- a/Octokit.Reactive/Clients/IObservableStatisticsClient.cs
+++ b/Octokit.Reactive/Clients/IObservableStatisticsClient.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using Octokit.Response;
namespace Octokit.Reactive
{
diff --git a/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs b/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs
index 63a5b254..13749739 100644
--- a/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs
+++ b/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs
@@ -24,6 +24,7 @@ namespace Octokit.Reactive
Deployment = new ObservableDeploymentsClient(client);
Statistics = new ObservableStatisticsClient(client);
PullRequest = new ObservablePullRequestsClient(client);
+ RepositoryComments = new ObservableRepositoryCommentsClient(client);
}
///
@@ -180,6 +181,14 @@ namespace Octokit.Reactive
///
public IObservableStatisticsClient Statistics { get; private set; }
+ ///
+ /// Client for GitHub's Repository Comments API.
+ ///
+ ///
+ /// See the Repository Comments API documentation for more information.
+ ///
+ public IObservableRepositoryCommentsClient RepositoryComments { get; private set; }
+
///
/// Gets all the branches for the specified repository.
///
diff --git a/Octokit.Reactive/Clients/ObservableRepositoryCommentsClient.cs b/Octokit.Reactive/Clients/ObservableRepositoryCommentsClient.cs
new file mode 100644
index 00000000..492792e8
--- /dev/null
+++ b/Octokit.Reactive/Clients/ObservableRepositoryCommentsClient.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Reactive;
+using System.Reactive.Threading.Tasks;
+using Octokit.Reactive.Internal;
+
+namespace Octokit.Reactive
+{
+ public class ObservableRepositoryCommentsClient : IObservableRepositoryCommentsClient
+ {
+ readonly IRepositoryCommentsClient _client;
+ readonly IConnection _connection;
+
+ public ObservableRepositoryCommentsClient(IGitHubClient client)
+ {
+ Ensure.ArgumentNotNull(client, "client");
+
+ _client = client.Repository.RepositoryComments;
+ _connection = client.Connection;
+ }
+
+ ///
+ /// Gets a single Repository Comment by number.
+ ///
+ /// http://developer.github.com/v3/repos/comments/#get-a-single-commit-comment
+ /// The owner of the repository
+ /// The name of the repository
+ /// The comment id
+ ///
+ public IObservable Get(string owner, string name, int number)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
+ Ensure.ArgumentNotNullOrEmptyString(name, "name");
+
+ return _client.Get(owner, name, number).ToObservable();
+ }
+
+ ///
+ /// Gets Commit Comments for a repository.
+ ///
+ /// http://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository
+ /// The owner of the repository
+ /// The name of the repository
+ ///
+ public IObservable GetForRepository(string owner, string name)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
+ Ensure.ArgumentNotNullOrEmptyString(name, "name");
+
+ return _connection.GetAndFlattenAllPages(ApiUrls.CommitComments(owner, name));
+ }
+
+ ///
+ /// Gets Commit Comments for a specified Commit.
+ ///
+ /// http://developer.github.com/v3/repos/comments/#list-comments-for-a-single-commit
+ /// The owner of the repository
+ /// The name of the repository
+ /// The sha of the commit
+ ///
+ public IObservable GetForCommit(string owner, string name, string sha)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
+ Ensure.ArgumentNotNullOrEmptyString(name, "name");
+ Ensure.ArgumentNotNullOrEmptyString(sha, "sha");
+
+ return _connection.GetAndFlattenAllPages(ApiUrls.CommitComments(owner, name, sha));
+ }
+
+ ///
+ /// Creates a new Commit Comment for a specified Commit.
+ ///
+ /// http://developer.github.com/v3/repos/comments/#create-a-commit-comment
+ /// The owner of the repository
+ /// The name of the repository
+ /// The sha reference of commit
+ /// The new comment to add to the commit
+ ///
+ public IObservable Create(string owner, string name, string sha, NewCommitComment newCommitComment)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
+ Ensure.ArgumentNotNullOrEmptyString(name, "name");
+ Ensure.ArgumentNotNullOrEmptyString(sha, "sha");
+ Ensure.ArgumentNotNull(newCommitComment, "newCommitComment");
+
+ return _client.Create(owner, name, sha, newCommitComment).ToObservable();
+ }
+
+ ///
+ /// Updates a specified Commit Comment.
+ ///
+ /// http://developer.github.com/v3/repos/comments/#update-a-commit-comment
+ /// The owner of the repository
+ /// The name of the repository
+ /// The comment number
+ /// The modified comment
+ ///
+ public IObservable Update(string owner, string name, int number, string commentUpdate)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
+ Ensure.ArgumentNotNullOrEmptyString(name, "name");
+ Ensure.ArgumentNotNull(commentUpdate, "commentUpdate");
+
+ return _client.Update(owner, name, number, commentUpdate).ToObservable();
+ }
+
+ ///
+ /// Deletes the specified Commit Comment
+ ///
+ /// http://developer.github.com/v3/repos/comments/#delete-a-commit-comment
+ /// The owner of the repository
+ /// The name of the repository
+ /// The comment id
+ ///
+ public IObservable Delete(string owner, string name, int number)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
+ Ensure.ArgumentNotNullOrEmptyString(name, "name");
+
+ return _client.Delete(owner, name, number).ToObservable();
+ }
+ }
+}
diff --git a/Octokit.Reactive/Clients/ObservableSearchClient.cs b/Octokit.Reactive/Clients/ObservableSearchClient.cs
index c34e4525..1bdf0045 100644
--- a/Octokit.Reactive/Clients/ObservableSearchClient.cs
+++ b/Octokit.Reactive/Clients/ObservableSearchClient.cs
@@ -1,7 +1,5 @@
using System;
-using System.Reactive;
using System.Reactive.Threading.Tasks;
-using Octokit.Reactive.Internal;
namespace Octokit.Reactive
{
@@ -10,13 +8,13 @@ namespace Octokit.Reactive
///
public class ObservableSearchClient : IObservableSearchClient
{
- readonly IConnection _connection;
+ readonly ISearchClient _client;
public ObservableSearchClient(IGitHubClient client)
{
Ensure.ArgumentNotNull(client, "client");
- _connection = client.Connection;
+ _client = client.Search;
}
///
@@ -25,10 +23,10 @@ namespace Octokit.Reactive
///
///
/// List of repositories
- public IObservable SearchRepo(SearchRepositoriesRequest search)
+ public IObservable SearchRepo(SearchRepositoriesRequest search)
{
Ensure.ArgumentNotNull(search, "search");
- return _connection.GetAndFlattenAllPages(ApiUrls.SearchRepositories(), search.Parameters);
+ return _client.SearchRepo(search).ToObservable();
}
///
@@ -37,10 +35,10 @@ namespace Octokit.Reactive
///
///
/// List of users
- public IObservable SearchUsers(SearchUsersRequest search)
+ public IObservable SearchUsers(SearchUsersRequest search)
{
Ensure.ArgumentNotNull(search, "search");
- return _connection.GetAndFlattenAllPages(ApiUrls.SearchUsers(), search.Parameters);
+ return _client.SearchUsers(search).ToObservable();
}
///
@@ -49,10 +47,10 @@ namespace Octokit.Reactive
///
///
/// List of issues
- public IObservable SearchIssues(SearchIssuesRequest search)
+ public IObservable SearchIssues(SearchIssuesRequest search)
{
Ensure.ArgumentNotNull(search, "search");
- return _connection.GetAndFlattenAllPages(ApiUrls.SearchIssues(), search.Parameters);
+ return _client.SearchIssues(search).ToObservable();
}
///
@@ -61,10 +59,10 @@ namespace Octokit.Reactive
///
///
/// List of files
- public IObservable SearchCode(SearchCodeRequest search)
+ public IObservable SearchCode(SearchCodeRequest search)
{
Ensure.ArgumentNotNull(search, "search");
- return _connection.GetAndFlattenAllPages(ApiUrls.SearchCode(), search.Parameters);
+ return _client.SearchCode(search).ToObservable();
}
}
}
\ No newline at end of file
diff --git a/Octokit.Reactive/Clients/ObservableStatisticsClient.cs b/Octokit.Reactive/Clients/ObservableStatisticsClient.cs
index 9905de1f..74acaa64 100644
--- a/Octokit.Reactive/Clients/ObservableStatisticsClient.cs
+++ b/Octokit.Reactive/Clients/ObservableStatisticsClient.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Reactive.Threading.Tasks;
-using Octokit.Response;
namespace Octokit.Reactive
{
diff --git a/Octokit.Reactive/Octokit.Reactive-Mono.csproj b/Octokit.Reactive/Octokit.Reactive-Mono.csproj
index d3c9a130..5dc511fd 100644
--- a/Octokit.Reactive/Octokit.Reactive-Mono.csproj
+++ b/Octokit.Reactive/Octokit.Reactive-Mono.csproj
@@ -139,6 +139,8 @@
+
+
diff --git a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj
index 885d6aa1..cd53c5a2 100644
--- a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj
+++ b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj
@@ -148,6 +148,8 @@
+
+
diff --git a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj
index f571279c..c8657d7f 100644
--- a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj
+++ b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj
@@ -143,6 +143,8 @@
+
+
diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj
index 5a9d0d03..f08540c7 100644
--- a/Octokit.Reactive/Octokit.Reactive.csproj
+++ b/Octokit.Reactive/Octokit.Reactive.csproj
@@ -19,7 +19,7 @@
false
obj\Debug\Net40
bin\Debug\Net45\
- DEBUG;TRACE;CODE_ANALYSIS;NET_45
+ TRACE;DEBUG;CODE_ANALYSIS;NET_45;SIMPLE_JSON_READONLY_COLLECTIONS
prompt
4
true
@@ -32,7 +32,7 @@
true
obj\Release\Net40
bin\Release\Net45\
- TRACE;NET_45
+ TRACE;CODE_ANALYSIS;NET_45;SIMPLE_JSON_READONLY_COLLECTIONS
prompt
4
false
@@ -73,6 +73,8 @@
Properties\SolutionInfo.cs
+
+
diff --git a/Octokit.Tests.Conventions/Exception/InterfaceHasAdditionalMethodsException.cs b/Octokit.Tests.Conventions/Exception/InterfaceHasAdditionalMethodsException.cs
new file mode 100644
index 00000000..78ea2daa
--- /dev/null
+++ b/Octokit.Tests.Conventions/Exception/InterfaceHasAdditionalMethodsException.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+
+namespace Octokit.Tests.Conventions
+{
+ public class InterfaceHasAdditionalMethodsException : Exception
+ {
+ public InterfaceHasAdditionalMethodsException(Type type, IEnumerable methodsMissingOnReactiveClient)
+ : base(CreateMessage(type, methodsMissingOnReactiveClient)) { }
+
+ public InterfaceHasAdditionalMethodsException(Type type, IEnumerable methodsMissingOnReactiveClient, Exception innerException)
+ : base(CreateMessage(type, methodsMissingOnReactiveClient), innerException) { }
+
+ protected InterfaceHasAdditionalMethodsException(SerializationInfo info, StreamingContext context)
+ : base(info, context) { }
+
+ static string CreateMessage(Type type, IEnumerable methods)
+ {
+ var methodsFormatted = String.Join("\r\n", methods.Select(m => String.Format(" - {0}", m)));
+ return "Methods found on type {0} which should be removed:\r\n{1}"
+ .FormatWithNewLine(
+ type.Name,
+ methodsFormatted);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Octokit.Tests.Conventions/Exception/InterfaceMethodsMismatchException.cs b/Octokit.Tests.Conventions/Exception/InterfaceMethodsMismatchException.cs
new file mode 100644
index 00000000..7a422d49
--- /dev/null
+++ b/Octokit.Tests.Conventions/Exception/InterfaceMethodsMismatchException.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization;
+
+namespace Octokit.Tests.Conventions
+{
+ public class InterfaceMethodsMismatchException : Exception
+ {
+ public InterfaceMethodsMismatchException(Type observableType, Type clientInterface)
+ : base(CreateMessage(observableType, clientInterface)) { }
+
+ public InterfaceMethodsMismatchException(Type type,Type clientInterface, Exception innerException)
+ : base(CreateMessage(type, clientInterface), innerException) { }
+
+ protected InterfaceMethodsMismatchException(SerializationInfo info, StreamingContext context)
+ : base(info, context) { }
+
+ static string Format(ParameterInfo parameterInfo)
+ {
+ return String.Format("{0} {1}", parameterInfo.ParameterType.Name, parameterInfo.Name);
+ }
+
+ static string Format(MethodInfo methodInfo)
+ {
+ var parameters = methodInfo.GetParameters().Select(Format);
+
+ return String.Format("{0} {1}({2})", methodInfo.ReturnType, methodInfo.Name, String.Join(", ", parameters));
+ }
+
+ static string CreateMessage(Type observableInterface, Type clientInterface)
+ {
+ var mainMethods = clientInterface.GetMethodsOrdered();
+ var observableMethods = observableInterface.GetMethodsOrdered();
+
+ var formattedMainMethods = String.Join("\r\n", mainMethods.Select(Format).Select(m => String.Format(" - {0}", m)));
+ var formattedObservableMethods = String.Join("\r\n", observableMethods.Select(Format).Select(m => String.Format(" - {0}", m)));
+
+ return
+ "There are some overloads which are confusing the convention tests. Check everything is okay in these types:\r\n{0}\r\n{1}\r\n{2}\r\n{3}"
+ .FormatWithNewLine(
+ clientInterface.Name,
+ formattedMainMethods,
+ observableInterface.Name,
+ formattedObservableMethods);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Octokit.Tests.Conventions/Exception/InterfaceMissingMethodsException.cs b/Octokit.Tests.Conventions/Exception/InterfaceMissingMethodsException.cs
new file mode 100644
index 00000000..4d16288a
--- /dev/null
+++ b/Octokit.Tests.Conventions/Exception/InterfaceMissingMethodsException.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+
+namespace Octokit.Tests.Conventions
+{
+ public class InterfaceMissingMethodsException : Exception
+ {
+ public InterfaceMissingMethodsException(Type type, IEnumerable methodsMissingOnReactiveClient)
+ : base(CreateMessage(type, methodsMissingOnReactiveClient)) { }
+
+ public InterfaceMissingMethodsException(Type type, IEnumerable methodsMissingOnReactiveClient, Exception innerException)
+ : base(CreateMessage(type, methodsMissingOnReactiveClient), innerException) { }
+
+ protected InterfaceMissingMethodsException(SerializationInfo info, StreamingContext context)
+ : base(info, context) { }
+
+ static string CreateMessage(Type type, IEnumerable methods)
+ {
+ var methodsFormatted = String.Join("\r\n", methods.Select(m => String.Format(" - {0}", m)));
+ return "Methods not found on interface {0} which are required:\r\n{1}"
+ .FormatWithNewLine(type.Name, methodsFormatted);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Octokit.Tests.Conventions/Exception/InterfaceNotFoundException.cs b/Octokit.Tests.Conventions/Exception/InterfaceNotFoundException.cs
new file mode 100644
index 00000000..3a428331
--- /dev/null
+++ b/Octokit.Tests.Conventions/Exception/InterfaceNotFoundException.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace Octokit.Tests.Conventions
+{
+ public class InterfaceNotFoundException : Exception
+ {
+ public InterfaceNotFoundException() { }
+
+ public InterfaceNotFoundException(string type)
+ : base(CreateMessage(type)) { }
+
+ public InterfaceNotFoundException(string type, Exception innerException)
+ : base(CreateMessage(type), innerException) { }
+
+ protected InterfaceNotFoundException(SerializationInfo info, StreamingContext context)
+ : base(info, context) { }
+
+ static string CreateMessage(string type)
+ {
+ return String.Format("Could not find the interface {0}. Add this to the Octokit.Reactive project", type);
+ }
+
+ }
+}
diff --git a/Octokit.Tests.Conventions/Exception/ParameterCountMismatchException.cs b/Octokit.Tests.Conventions/Exception/ParameterCountMismatchException.cs
new file mode 100644
index 00000000..24b165f3
--- /dev/null
+++ b/Octokit.Tests.Conventions/Exception/ParameterCountMismatchException.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization;
+
+namespace Octokit.Tests.Conventions
+{
+ public class ParameterCountMismatchException : Exception
+ {
+ public ParameterCountMismatchException(MethodInfo method, IEnumerable expected, IEnumerable actual)
+ : base(CreateMessage(method, expected, actual)) { }
+
+ public ParameterCountMismatchException(MethodInfo method, IEnumerable expected, IEnumerable actual, Exception innerException)
+ : base(CreateMessage(method, expected, actual), innerException) { }
+
+ protected ParameterCountMismatchException(SerializationInfo info, StreamingContext context)
+ : base(info, context) { }
+
+ static string CreateMethodSignature(IEnumerable parameters)
+ {
+ return String.Join(",", parameters.Select(p => String.Format("{0} {1}", p.ParameterType.Name, p.Name)));
+ }
+
+ static string CreateMessage(MethodInfo method, IEnumerable expected, IEnumerable actual)
+ {
+ var expectedMethodSignature = CreateMethodSignature(expected);
+ var actualMethodSignature = CreateMethodSignature(actual);
+
+ return String.Format("Method signature for {0}.{1} must be \"({2})\" but is \"({3})\"", method.DeclaringType.Name, method.Name, expectedMethodSignature, actualMethodSignature);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Octokit.Tests.Conventions/Exception/ParameterMismatchException.cs b/Octokit.Tests.Conventions/Exception/ParameterMismatchException.cs
new file mode 100644
index 00000000..88009960
--- /dev/null
+++ b/Octokit.Tests.Conventions/Exception/ParameterMismatchException.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Reflection;
+using System.Runtime.Serialization;
+
+namespace Octokit.Tests.Conventions
+{
+ public class ParameterMismatchException : Exception
+ {
+ public ParameterMismatchException(MethodInfo method, int position, ParameterInfo expected, ParameterInfo actual)
+ : base(CreateMessage(method, position, expected, actual)) { }
+
+ public ParameterMismatchException(MethodInfo method, int position, ParameterInfo expected, ParameterInfo actual, Exception innerException)
+ : base(CreateMessage(method, position, expected, actual), innerException) { }
+
+ protected ParameterMismatchException(SerializationInfo info, StreamingContext context)
+ : base(info, context) { }
+
+ static string CreateParameterSignature(ParameterInfo parameter)
+ {
+ return String.Format("{0} {1}", parameter.ParameterType.Name, parameter.Name);
+ }
+
+ static string CreateMessage(MethodInfo method, int position, ParameterInfo expected, ParameterInfo actual)
+ {
+ var expectedMethodSignature = CreateParameterSignature(expected);
+ var actualMethodSignature = CreateParameterSignature(actual);
+
+ return String.Format("Parameter {0} for method {1}.{2} must be \"{3}\" but is \"{4}\"", position, method.DeclaringType.Name, method.Name, expectedMethodSignature, actualMethodSignature);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Octokit.Tests.Conventions/Exception/ReturnValueMismatchException.cs b/Octokit.Tests.Conventions/Exception/ReturnValueMismatchException.cs
new file mode 100644
index 00000000..3bc97e4e
--- /dev/null
+++ b/Octokit.Tests.Conventions/Exception/ReturnValueMismatchException.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Reflection;
+using System.Runtime.Serialization;
+
+namespace Octokit.Tests.Conventions
+{
+ public class ReturnValueMismatchException : Exception
+ {
+ public ReturnValueMismatchException(MethodInfo method, Type expected, Type actual)
+ : base(CreateMessage(method, expected, actual)) { }
+
+ public ReturnValueMismatchException(MethodInfo method, Type expected, Type actual, Exception innerException)
+ : base(CreateMessage(method, expected, actual), innerException) { }
+
+ protected ReturnValueMismatchException(SerializationInfo info, StreamingContext context)
+ : base(info, context) { }
+
+ static string CreateMessage(MethodInfo method, Type expected, Type actual)
+ {
+ return String.Format("Return value for {0}.{1} must be \"{2}\" but is \"{3}\"", method.DeclaringType.Name, method.Name, expected, actual);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj b/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj
index 7b378297..dd3aa159 100644
--- a/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj
+++ b/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj
@@ -50,7 +50,15 @@
+
+
+
+
+
+
+
+
diff --git a/Octokit.Tests.Conventions/StringExtensions.cs b/Octokit.Tests.Conventions/StringExtensions.cs
new file mode 100644
index 00000000..0fdc64a3
--- /dev/null
+++ b/Octokit.Tests.Conventions/StringExtensions.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Octokit.Tests.Conventions
+{
+ internal static class StringExtensions
+ {
+ public static string FormatWithNewLine(this string s, params object[] args)
+ {
+ var template = Environment.NewLine == "\r\n"
+ ? s
+ : s.Replace("\r\n", Environment.NewLine);
+
+ return String.Format(template, args);
+ }
+ }
+}
diff --git a/Octokit.Tests.Conventions/SyncObservableClients.cs b/Octokit.Tests.Conventions/SyncObservableClients.cs
index 376750ac..2302f0f5 100644
--- a/Octokit.Tests.Conventions/SyncObservableClients.cs
+++ b/Octokit.Tests.Conventions/SyncObservableClients.cs
@@ -7,29 +7,38 @@ using System.Reflection;
using Octokit.Tests.Helpers;
using Xunit;
using Xunit.Extensions;
-using Xunit.Sdk;
namespace Octokit.Tests.Conventions
{
public class SyncObservableClients
{
- [Fact]
- private void CheckObservableClientExample()
- {
- CheckObservableClients(typeof(ISearchClient));
- }
-
[Theory]
[ClassData(typeof(ClientInterfaces))]
- private void CheckObservableClients(Type clientInterface)
+ public void CheckObservableClients(Type clientInterface)
{
var observableClient = clientInterface.GetObservableClientInterface();
var mainMethods = clientInterface.GetMethodsOrdered();
var observableMethods = observableClient.GetMethodsOrdered();
var mainNames = Array.ConvertAll(mainMethods, m => m.Name);
var observableNames = Array.ConvertAll(observableMethods, m => m.Name);
- AssertEx.Empty(observableNames.Except(mainNames), "Extra observable methods");
- AssertEx.Empty(mainNames.Except(observableNames), "Missing observable methods");
+
+ var methodsMissingOnReactiveClient = mainNames.Except(observableNames);
+ if (methodsMissingOnReactiveClient.Any())
+ {
+ throw new InterfaceMissingMethodsException(observableClient, methodsMissingOnReactiveClient);
+ }
+
+ var additionalMethodsOnReactiveClient = observableNames.Except(mainNames);
+ if (additionalMethodsOnReactiveClient.Any())
+ {
+ throw new InterfaceHasAdditionalMethodsException(observableClient, additionalMethodsOnReactiveClient);
+ }
+
+ if (mainNames.Count() != observableNames.Count())
+ {
+ throw new InterfaceMethodsMismatchException(observableClient, clientInterface);
+ }
+
int index = 0;
foreach(var mainMethod in mainMethods)
{
@@ -52,7 +61,11 @@ namespace Octokit.Tests.Conventions
var mainReturnType = mainMethod.ReturnType;
var observableReturnType = observableMethod.ReturnType;
var expectedType = GetObservableExpectedType(mainReturnType);
- Assert.Equal(expectedType, observableReturnType);
+
+ if (expectedType != observableReturnType)
+ {
+ throw new ReturnValueMismatchException(observableMethod, expectedType, observableReturnType);
+ }
}
private static Type GetObservableExpectedType(Type mainType)
@@ -82,16 +95,27 @@ namespace Octokit.Tests.Conventions
{
var mainParameters = mainMethod.GetParametersOrdered();
var observableParameters = observableMethod.GetParametersOrdered();
- Assert.Equal(mainParameters.Length, observableParameters.Length);
+
+ if (mainParameters.Length != observableParameters.Length)
+ {
+ throw new ParameterCountMismatchException(observableMethod, mainParameters, observableParameters);
+ }
+
int index = 0;
foreach(var mainParameter in mainParameters)
{
var observableParameter = observableParameters[index];
- Assert.Equal(mainParameter.Name, observableParameter.Name);
- var mainType = mainParameter.ParameterType;
- var typeInfo = mainType.GetTypeInfo();
+ if (mainParameter.Name != observableParameter.Name)
+ {
+ throw new ParameterMismatchException(observableMethod, index, mainParameter, observableParameter);
+ }
+
+ var mainType = mainParameter.ParameterType;
var expectedType = GetObservableExpectedType(mainType);
- Assert.Equal(expectedType, observableParameter.ParameterType);
+ if (expectedType != observableParameter.ParameterType)
+ {
+ throw new ParameterMismatchException(observableMethod, index, mainParameter, observableParameter);
+ }
index++;
}
}
diff --git a/Octokit.Tests.Conventions/TypeExtensions.cs b/Octokit.Tests.Conventions/TypeExtensions.cs
index ea7900a9..4028b26e 100644
--- a/Octokit.Tests.Conventions/TypeExtensions.cs
+++ b/Octokit.Tests.Conventions/TypeExtensions.cs
@@ -80,7 +80,7 @@ namespace Octokit.Tests.Conventions
var observableInterface = observableClient.Assembly.GetType(observableClientName);
if(observableInterface == null)
{
- throw new Exception("Cannot find observable interface "+observableClientName);
+ throw new InterfaceNotFoundException(observableClientName);
}
return observableInterface;
}
diff --git a/Octokit.Tests.Integration/Clients/GistsClientTests.cs b/Octokit.Tests.Integration/Clients/GistsClientTests.cs
index 53c96392..3df84b68 100644
--- a/Octokit.Tests.Integration/Clients/GistsClientTests.cs
+++ b/Octokit.Tests.Integration/Clients/GistsClientTests.cs
@@ -62,7 +62,7 @@ public class GistsClientTests
Assert.DoesNotThrow(async () => { await _fixture.Delete(createdGist.Id); });
}
- [IntegrationTest]
+ [IntegrationTest(Skip = "See https://github.com/octokit/octokit.net/issues/424 for an explanation of the issue")]
public async Task CanStarAndUnstarAGist()
{
Assert.DoesNotThrow(async () => { await _fixture.Star(testGistId); });
diff --git a/Octokit.Tests.Integration/Clients/IssuesClientTests.cs b/Octokit.Tests.Integration/Clients/IssuesClientTests.cs
index 2a224262..41e2016d 100644
--- a/Octokit.Tests.Integration/Clients/IssuesClientTests.cs
+++ b/Octokit.Tests.Integration/Clients/IssuesClientTests.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Octokit;
+using Octokit.Tests.Helpers;
using Octokit.Tests.Integration;
using Xunit;
@@ -159,6 +160,97 @@ public class IssuesClientTests : IDisposable
Assert.True(retrieved.Any(i => i.Number == issue2.Number));
}
+ [IntegrationTest]
+ public async Task CanFilterByAssigned()
+ {
+ var owner = _repository.Owner.Login;
+ var newIssue1 = new NewIssue("An assigned issue") { Body = "Assigning this to myself", Assignee = owner };
+ var newIssue2 = new NewIssue("An unassigned issue") { Body = "A new unassigned issue" };
+ await _issuesClient.Create(owner, _repository.Name, newIssue1);
+ await _issuesClient.Create(owner, _repository.Name, newIssue2);
+
+ var allIssues = await _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest());
+
+ Assert.Equal(2, allIssues.Count);
+
+ var assignedIssues = await _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest { Assignee = owner });
+
+ Assert.Equal(1, assignedIssues.Count);
+ Assert.Equal("An assigned issue", assignedIssues[0].Title);
+
+ var unassignedIssues = await _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest { Assignee = "none" });
+
+ Assert.Equal(1, unassignedIssues.Count);
+ Assert.Equal("An unassigned issue", unassignedIssues[0].Title);
+ }
+
+ [IntegrationTest]
+ public async Task CanFilterByCreator()
+ {
+ var owner = _repository.Owner.Login;
+ var newIssue1 = new NewIssue("An issue") { Body = "words words words" };
+ var newIssue2 = new NewIssue("Another issue") { Body = "some other words" };
+ await _issuesClient.Create(owner, _repository.Name, newIssue1);
+ await _issuesClient.Create(owner, _repository.Name, newIssue2);
+
+ var allIssues = await _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest());
+
+ Assert.Equal(2, allIssues.Count);
+
+ var issuesCreatedByOwner = await _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest { Creator = owner });
+
+ Assert.Equal(2, issuesCreatedByOwner.Count);
+
+ var issuesCreatedByExternalUser = await _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest { Creator = "shiftkey" });
+
+ Assert.Equal(0, issuesCreatedByExternalUser.Count);
+ }
+
+ [IntegrationTest]
+ public async Task CanFilterByMentioned()
+ {
+ var owner = _repository.Owner.Login;
+ var newIssue1 = new NewIssue("An issue") { Body = "words words words hello there @shiftkey" };
+ var newIssue2 = new NewIssue("Another issue") { Body = "some other words" };
+ await _issuesClient.Create(owner, _repository.Name, newIssue1);
+ await _issuesClient.Create(owner, _repository.Name, newIssue2);
+
+ var allIssues = await _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest());
+
+ Assert.Equal(2, allIssues.Count);
+
+ var mentionsWithShiftkey = await _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest { Mentioned = "shiftkey" });
+
+ Assert.Equal(1, mentionsWithShiftkey.Count);
+
+ var mentionsWithHaacked = await _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest { Mentioned = "haacked" });
+
+ Assert.Equal(0, mentionsWithHaacked.Count);
+ }
+
+ [IntegrationTest]
+ public async Task FilteringByInvalidAccountThrowsError()
+ {
+ var owner = _repository.Owner.Login;
+
+ AssertEx.Throws(
+ () => _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest { Creator = "some-random-account" }));
+
+ AssertEx.Throws(
+ () => _issuesClient.GetForRepository(owner, _repository.Name,
+ new RepositoryIssueRequest { Assignee = "some-random-account" }));
+ }
+
public void Dispose()
{
Helper.DeleteRepo(_repository);
diff --git a/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs
index cceb0cca..e80ca018 100644
--- a/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs
+++ b/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs
@@ -245,7 +245,7 @@ public class RepositoriesClientTests
{
Name = repoName,
AutoInit = true,
- GitignoreTemplate = "visualstudio"
+ GitignoreTemplate = "VisualStudio"
});
try
diff --git a/Octokit.Tests.Integration/Clients/SearchClientTests.cs b/Octokit.Tests.Integration/Clients/SearchClientTests.cs
new file mode 100644
index 00000000..8a4363b5
--- /dev/null
+++ b/Octokit.Tests.Integration/Clients/SearchClientTests.cs
@@ -0,0 +1,57 @@
+using System.Threading.Tasks;
+using Octokit;
+using Octokit.Tests.Integration;
+using Xunit;
+
+public class SearchClientTests
+{
+ readonly IGitHubClient _gitHubClient;
+
+ public SearchClientTests()
+ {
+ _gitHubClient = new GitHubClient(new ProductHeaderValue("OctokitTests"))
+ {
+ Credentials = Helper.Credentials
+ };
+ }
+
+ [Fact]
+ public async Task SearchForCSharpRepositories()
+ {
+ var request = new SearchRepositoriesRequest("csharp");
+ var repos = await _gitHubClient.Search.SearchRepo(request);
+
+ Assert.NotEmpty(repos.Items);
+ }
+
+ [Fact]
+ public async Task SearchForGitHub()
+ {
+ var request = new SearchUsersRequest("github");
+ var repos = await _gitHubClient.Search.SearchUsers(request);
+
+ Assert.NotEmpty(repos.Items);
+ }
+
+ [Fact]
+ public async Task SearchForFunctionInCode()
+ {
+ var request = new SearchCodeRequest("addClass");
+ request.Repo = "jquery/jquery";
+ var repos = await _gitHubClient.Search.SearchCode(request);
+
+ Assert.NotEmpty(repos.Items);
+ }
+
+ [Fact]
+ public async Task SearchForWordInCode()
+ {
+ var request = new SearchIssuesRequest("windows");
+ request.SortField = IssueSearchSort.Created;
+ request.Order = SortDirection.Descending;
+
+ var repos = await _gitHubClient.Search.SearchIssues(request);
+
+ Assert.NotEmpty(repos.Items);
+ }
+}
diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj
index 274abe0e..24eb70c1 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/RepositoryCommentsClientTests.cs b/Octokit.Tests/Clients/RepositoryCommentsClientTests.cs
new file mode 100644
index 00000000..a073da85
--- /dev/null
+++ b/Octokit.Tests/Clients/RepositoryCommentsClientTests.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Threading.Tasks;
+using NSubstitute;
+using Octokit;
+using Octokit.Internal;
+using Octokit.Tests.Helpers;
+using Xunit;
+
+public class RepositoryCommentsClientTests
+{
+ public class TheGetMethod
+ {
+ [Fact]
+ public void RequestsCorrectUrl()
+ {
+ var connection = Substitute.For();
+ var client = new RepositoryCommentsClient(connection);
+
+ client.Get("fake", "repo", 42);
+
+ connection.Received().Get(Arg.Is(u => u.ToString() == "repos/fake/repo/comments/42"),
+ null);
+ }
+
+ [Fact]
+ public async Task EnsuresNonNullArguments()
+ {
+ var client = new RepositoryCommentsClient(Substitute.For());
+
+ await AssertEx.Throws(async () => await client.Get(null, "name", 1));
+ await AssertEx.Throws(async () => await client.Get("", "name", 1));
+ await AssertEx.Throws(async () => await client.Get("owner", null, 1));
+ await AssertEx.Throws(async () => await client.Get("owner", "", 1));
+ }
+
+ }
+
+ public class TheGetForRepositoryMethod
+ {
+ [Fact]
+ public void RequestsCorrectUrl()
+ {
+ var connection = Substitute.For();
+ var client = new RepositoryCommentsClient(connection);
+
+ client.GetForRepository("fake", "repo");
+
+ connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/comments"));
+ }
+
+ [Fact]
+ public async Task EnsuresArgumentsNotNull()
+ {
+ var connection = Substitute.For();
+ var client = new RepositoryCommentsClient(connection);
+
+ await AssertEx.Throws(async () => await client.GetForRepository(null, "name"));
+ await AssertEx.Throws(async () => await client.GetForRepository("", "name"));
+ await AssertEx.Throws(async () => await client.GetForRepository("owner", null));
+ await AssertEx.Throws(async () => await client.GetForRepository("owner", ""));
+ }
+ }
+
+ public class TheGetForCommitMethod
+ {
+ [Fact]
+ public void RequestsCorrectUrl()
+ {
+ var connection = Substitute.For();
+ var client = new RepositoryCommentsClient(connection);
+
+ client.GetForCommit("fake", "repo", "sha");
+
+ connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/commits/sha/comments"));
+ }
+
+ [Fact]
+ public async Task EnsuresArgumentsNotNull()
+ {
+ var connection = Substitute.For();
+ var client = new RepositoryCommentsClient(connection);
+
+ await AssertEx.Throws(async () => await client.GetForCommit(null, "name", "sha"));
+ await AssertEx.Throws(async () => await client.GetForCommit("", "name", "sha"));
+ await AssertEx.Throws(async () => await client.GetForCommit("owner", null, "sha"));
+ await AssertEx.Throws(async () => await client.GetForCommit("owner", "", "sha"));
+ await AssertEx.Throws(async () => await client.GetForCommit("owner", "name", null));
+ await AssertEx.Throws(async () => await client.GetForCommit("owner", "name", ""));
+ }
+ }
+
+ public class TheCreateMethod
+ {
+ [Fact]
+ public void PostsToCorrectUrl()
+ {
+ NewCommitComment newComment = new NewCommitComment("body");
+
+ var connection = Substitute.For();
+ var client = new RepositoryCommentsClient(connection);
+
+ client.Create("fake", "repo", "sha", newComment);
+
+ connection.Received().Post(Arg.Is(u => u.ToString() == "repos/fake/repo/commits/sha/comments"), Arg.Any