diff --git a/src/Cake.Issues.PullRequests.Tfs.Tests/Cake.Issues.PullRequests.Tfs.Tests.csproj b/src/Cake.Issues.PullRequests.Tfs.Tests/Cake.Issues.PullRequests.Tfs.Tests.csproj index c60f64f..c17f6ab 100644 --- a/src/Cake.Issues.PullRequests.Tfs.Tests/Cake.Issues.PullRequests.Tfs.Tests.csproj +++ b/src/Cake.Issues.PullRequests.Tfs.Tests/Cake.Issues.PullRequests.Tfs.Tests.csproj @@ -66,8 +66,12 @@ + + + + diff --git a/src/Cake.Issues.PullRequests.Tfs.Tests/CommentExtensionsTests.cs b/src/Cake.Issues.PullRequests.Tfs.Tests/CommentExtensionsTests.cs new file mode 100644 index 0000000..fcb7748 --- /dev/null +++ b/src/Cake.Issues.PullRequests.Tfs.Tests/CommentExtensionsTests.cs @@ -0,0 +1,63 @@ +namespace Cake.Issues.PullRequests.Tfs.Tests +{ + using Cake.Issues.Testing; + using Microsoft.TeamFoundation.SourceControl.WebApi; + using Shouldly; + using Xunit; + + public sealed class CommentExtensionsTests + { + public sealed class TheToPullRequestDiscussionCommentExtension + { + [Fact] + public void Should_Throw_If_Comment_Is_Null() + { + // Given + Comment comment = null; + + // When + var result = Record.Exception(() => comment.ToPullRequestDiscussionComment()); + + // Then + result.IsArgumentNullException("comment"); + } + + [Fact] + public void Should_Set_Correct_Content() + { + // Given + var content = "foo"; + var comment = + new Comment + { + Content = content + }; + + // When + var result = comment.ToPullRequestDiscussionComment(); + + // Then + result.Content.ShouldBe(content); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Should_Set_Correct_IsDeleted(bool isDeleted) + { + // Given + var comment = + new Comment + { + IsDeleted = isDeleted + }; + + // When + var result = comment.ToPullRequestDiscussionComment(); + + // Then + result.IsDeleted.ShouldBe(isDeleted); + } + } + } +} diff --git a/src/Cake.Issues.PullRequests.Tfs.Tests/CommentThreadStatusExtensionsTests.cs b/src/Cake.Issues.PullRequests.Tfs.Tests/CommentThreadStatusExtensionsTests.cs new file mode 100644 index 0000000..93a8e76 --- /dev/null +++ b/src/Cake.Issues.PullRequests.Tfs.Tests/CommentThreadStatusExtensionsTests.cs @@ -0,0 +1,85 @@ +namespace Cake.Issues.PullRequests.Tfs.Tests +{ + using Microsoft.TeamFoundation.SourceControl.WebApi; + using Shouldly; + using Xunit; + + public sealed class CommentThreadStatusExtensionsTests + { + public sealed class TheToPullRequestDiscussionStatusExtension + { + [Theory] + [InlineData( + CommentThreadStatus.Unknown, + PullRequestDiscussionStatus.Unknown)] + [InlineData( + CommentThreadStatus.Active, + PullRequestDiscussionStatus.Active)] + [InlineData( + CommentThreadStatus.Pending, + PullRequestDiscussionStatus.Active)] + [InlineData( + CommentThreadStatus.Fixed, + PullRequestDiscussionStatus.Resolved)] + [InlineData( + CommentThreadStatus.WontFix, + PullRequestDiscussionStatus.Resolved)] + [InlineData( + CommentThreadStatus.Closed, + PullRequestDiscussionStatus.Resolved)] + [InlineData( + CommentThreadStatus.ByDesign, + PullRequestDiscussionStatus.Resolved)] + public void Should_Return_Correct_Value( + CommentThreadStatus status, + PullRequestDiscussionStatus expectedResult) + { + // Given + + // When + var result = status.ToPullRequestDiscussionStatus(); + + // Then + result.ShouldBe(expectedResult); + } + } + + public sealed class TheToPullRequestDiscussionResolutionExtension + { + [Theory] + [InlineData( + CommentThreadStatus.Unknown, + PullRequestDiscussionResolution.Unknown)] + [InlineData( + CommentThreadStatus.Active, + PullRequestDiscussionResolution.Unknown)] + [InlineData( + CommentThreadStatus.Pending, + PullRequestDiscussionResolution.Unknown)] + [InlineData( + CommentThreadStatus.Fixed, + PullRequestDiscussionResolution.Resolved)] + [InlineData( + CommentThreadStatus.WontFix, + PullRequestDiscussionResolution.WontFix)] + [InlineData( + CommentThreadStatus.Closed, + PullRequestDiscussionResolution.Resolved)] + [InlineData( + CommentThreadStatus.ByDesign, + PullRequestDiscussionResolution.Resolved)] + public void Should_Return_Correct_Value( + CommentThreadStatus status, + PullRequestDiscussionResolution expectedResult) + { + // Given + + // When + var result = status.ToPullRequestDiscussionResolution(); + + // Then + result.ShouldBe(expectedResult); + } + } + } +} diff --git a/src/Cake.Issues.PullRequests.Tfs.Tests/GitPullRequestCommentThreadExtensionsTests.cs b/src/Cake.Issues.PullRequests.Tfs.Tests/GitPullRequestCommentThreadExtensionsTests.cs new file mode 100644 index 0000000..cae9295 --- /dev/null +++ b/src/Cake.Issues.PullRequests.Tfs.Tests/GitPullRequestCommentThreadExtensionsTests.cs @@ -0,0 +1,619 @@ +namespace Cake.Issues.PullRequests.Tfs.Tests +{ + using System.Collections.Generic; + using System.Linq; + using Cake.Issues.Testing; + using Microsoft.TeamFoundation.SourceControl.WebApi; + using Microsoft.VisualStudio.Services.WebApi; + using Shouldly; + using Xunit; + + public sealed class GitPullRequestCommentThreadExtensionsTests + { + public sealed class TheToPullRequestDiscussionThreadExtension + { + [Fact] + public void Should_Throw_If_Thread_Is_Null() + { + // Given + GitPullRequestCommentThread thread = null; + + // When + var result = Record.Exception(() => thread.ToPullRequestDiscussionThread()); + + // Then + result.IsArgumentNullException("thread"); + } + + [Fact] + public void Should_Throw_If_ThreadContext_Is_Null() + { + // Given + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = null, + Comments = new List(), + Properties = new PropertiesCollection() + }; + + // When + var result = Record.Exception(() => thread.ToPullRequestDiscussionThread()); + + // Then + result.IsInvalidOperationException("ThreadContext is not created."); + } + + [Fact] + public void Should_Throw_If_Comments_Are_Null() + { + // Given + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext { FilePath = "/foo.cs" }, + Comments = null, + Properties = new PropertiesCollection() + }; + + // When + var result = Record.Exception(() => thread.ToPullRequestDiscussionThread()); + + // Then + result.IsInvalidOperationException("Comments list is not created."); + } + + [Fact] + public void Should_Throw_If_Properties_Are_Null() + { + // Given + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = null + }; + + // When + var result = Record.Exception(() => thread.ToPullRequestDiscussionThread()); + + // Then + result.IsInvalidOperationException("Properties collection is not created."); + } + + [Fact] + public void Should_Set_Correct_Id() + { + // Given + var id = 123; + var status = CommentThreadStatus.Active; + var filePath = "/foo.cs"; + var thread = + new GitPullRequestCommentThread + { + Id = id, + Status = status, + ThreadContext = new CommentThreadContext() { FilePath = filePath }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + + // When + var result = thread.ToPullRequestDiscussionThread(); + + // Then + result.Id.ShouldBe(id); + } + + [Theory] + [InlineData( + CommentThreadStatus.Unknown, + PullRequestDiscussionStatus.Unknown)] + [InlineData( + CommentThreadStatus.Active, + PullRequestDiscussionStatus.Active)] + [InlineData( + CommentThreadStatus.Pending, + PullRequestDiscussionStatus.Active)] + [InlineData( + CommentThreadStatus.Fixed, + PullRequestDiscussionStatus.Resolved)] + [InlineData( + CommentThreadStatus.WontFix, + PullRequestDiscussionStatus.Resolved)] + [InlineData( + CommentThreadStatus.Closed, + PullRequestDiscussionStatus.Resolved)] + [InlineData( + CommentThreadStatus.ByDesign, + PullRequestDiscussionStatus.Resolved)] + public void Should_Set_Correct_Status( + CommentThreadStatus status, + PullRequestDiscussionStatus expectedResult) + { + // Given + var id = 123; + var filePath = "/foo.cs"; + var thread = + new GitPullRequestCommentThread + { + Id = id, + Status = status, + ThreadContext = new CommentThreadContext() { FilePath = filePath }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + + // When + var result = thread.ToPullRequestDiscussionThread(); + + // Then + result.Status.ShouldBe(expectedResult); + } + + [Theory] + [InlineData("/foo.cs", "foo.cs")] + public void Should_Set_Correct_FilePath(string filePath, string expectedResult) + { + // Given + var id = 123; + var status = CommentThreadStatus.Active; + var thread = + new GitPullRequestCommentThread + { + Id = id, + Status = status, + ThreadContext = new CommentThreadContext() { FilePath = filePath }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + + // When + var result = thread.ToPullRequestDiscussionThread(); + + // Then + result.AffectedFileRelativePath.ToString().ShouldBe(expectedResult); + } + + [Fact] + public void Should_Set_Correct_Comments() + { + // Given + var id = 123; + var status = CommentThreadStatus.Active; + var filePath = "/foo.cs"; + var commentContent = "foo"; + var commentIsDeleted = false; + var thread = + new GitPullRequestCommentThread + { + Id = id, + Status = status, + ThreadContext = new CommentThreadContext() { FilePath = filePath }, + Comments = new List + { + new Comment() + { + Content = commentContent, + IsDeleted = commentIsDeleted + } + }, + Properties = new PropertiesCollection() + }; + + // When + var result = thread.ToPullRequestDiscussionThread(); + + // Then + result.Comments.Count.ShouldBe(1); + result.Comments.Single().Content.ShouldBe(commentContent); + result.Comments.Single().IsDeleted.ShouldBe(commentIsDeleted); + } + + [Fact] + public void Should_Set_Correct_CommentSource() + { + // Given + var id = 123; + var status = CommentThreadStatus.Active; + var filePath = "/foo.cs"; + var commentSource = "foo"; + var thread = + new GitPullRequestCommentThread + { + Id = id, + Status = status, + ThreadContext = new CommentThreadContext() { FilePath = filePath }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + thread.SetCommentSource(commentSource); + + // When + var result = thread.ToPullRequestDiscussionThread(); + + // Then + result.CommentSource.ShouldBe(commentSource); + } + + [Theory] + [InlineData( + CommentThreadStatus.Unknown, + PullRequestDiscussionResolution.Unknown)] + [InlineData( + CommentThreadStatus.Active, + PullRequestDiscussionResolution.Unknown)] + [InlineData( + CommentThreadStatus.Pending, + PullRequestDiscussionResolution.Unknown)] + [InlineData( + CommentThreadStatus.Fixed, + PullRequestDiscussionResolution.Resolved)] + [InlineData( + CommentThreadStatus.WontFix, + PullRequestDiscussionResolution.WontFix)] + [InlineData( + CommentThreadStatus.Closed, + PullRequestDiscussionResolution.Resolved)] + [InlineData( + CommentThreadStatus.ByDesign, + PullRequestDiscussionResolution.Resolved)] + public void Should_Set_Correct_Resolution( + CommentThreadStatus status, + PullRequestDiscussionResolution expectedResult) + { + // Given + var id = 123; + var filePath = "/foo.cs"; + var thread = + new GitPullRequestCommentThread + { + Id = id, + Status = status, + ThreadContext = new CommentThreadContext() { FilePath = filePath }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + + // When + var result = thread.ToPullRequestDiscussionThread(); + + // Then + result.Resolution.ShouldBe(expectedResult); + } + } + + public sealed class TheGetCommentSourceExtension + { + [Fact] + public void Should_Throw_If_Thread_Is_Null() + { + // Given + GitPullRequestCommentThread thread = null; + + // When + var result = Record.Exception(() => thread.GetCommentSource()); + + // Then + result.IsArgumentNullException("thread"); + } + + [Fact] + public void Should_Throw_If_Properties_Are_Null() + { + // Given + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = null + }; + + // When + var result = Record.Exception(() => thread.GetCommentSource()); + + // Then + result.IsInvalidOperationException("Properties collection is not created."); + } + + [Fact] + public void Should_Return_Comment_Source() + { + // Given + var commentSource = "foo"; + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext() { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + thread.SetCommentSource(commentSource); + + // When + var result = thread.GetCommentSource(); + + // Then + result.ShouldBe(commentSource); + } + } + + public sealed class TheSetCommentSourceExtension + { + [Fact] + public void Should_Throw_If_Thread_Is_Null() + { + // Given + GitPullRequestCommentThread thread = null; + var value = "foo"; + + // When + var result = Record.Exception(() => thread.SetCommentSource(value)); + + // Then + result.IsArgumentNullException("thread"); + } + + [Fact] + public void Should_Throw_If_Properties_Are_Null() + { + // Given + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = null + }; + var value = "foo"; + + // When + var result = Record.Exception(() => thread.SetCommentSource(value)); + + // Then + result.IsInvalidOperationException("Properties collection is not created."); + } + + [Fact] + public void Should_Set_Comment_Source() + { + // Given + var commentSource = "foo"; + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext() { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + + // When + thread.SetCommentSource(commentSource); + + // Then + thread.GetCommentSource().ShouldBe(commentSource); + } + } + + public sealed class TheIsCommentSourceExtension + { + [Fact] + public void Should_Throw_If_Thread_Is_Null() + { + // Given + GitPullRequestCommentThread thread = null; + var value = "foo"; + + // When + var result = Record.Exception(() => thread.IsCommentSource(value)); + + // Then + result.IsArgumentNullException("thread"); + } + + [Fact] + public void Should_Throw_If_Properties_Are_Null() + { + // Given + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = null + }; + var value = "foo"; + + // When + var result = Record.Exception(() => thread.IsCommentSource(value)); + + // Then + result.IsInvalidOperationException("Properties collection is not created."); + } + + [Fact] + public void Should_Return_True_For_Existing_Comment_Source() + { + // Given + var commentSource = "foo"; + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext() { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + thread.SetCommentSource(commentSource); + + // When + var result = thread.IsCommentSource(commentSource); + + // Then + result.ShouldBeTrue(); + } + + [Fact] + public void Should_Return_False_For_Non_Existing_Comment_Source() + { + // Given + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext() { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + thread.SetCommentSource("foo"); + + // When + var result = thread.IsCommentSource("bar"); + + // Then + result.ShouldBeFalse(); + } + } + + public sealed class TheGetIssueMessageExtension + { + [Fact] + public void Should_Throw_If_Thread_Is_Null() + { + // Given + GitPullRequestCommentThread thread = null; + + // When + var result = Record.Exception(() => thread.GetIssueMessage()); + + // Then + result.IsArgumentNullException("thread"); + } + + [Fact] + public void Should_Throw_If_Properties_Are_Null() + { + // Given + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = null + }; + + // When + var result = Record.Exception(() => thread.GetIssueMessage()); + + // Then + result.IsInvalidOperationException("Properties collection is not created."); + } + + [Fact] + public void Should_Return_Message() + { + // Given + var message = "foo"; + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext() { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + thread.SetIssueMessage(message); + + // When + var result = thread.GetIssueMessage(); + + // Then + result.ShouldBe(message); + } + } + + public sealed class TheSetIssueMessageExtension + { + [Fact] + public void Should_Throw_If_Thread_Is_Null() + { + // Given + GitPullRequestCommentThread thread = null; + var value = "foo"; + + // When + var result = Record.Exception(() => thread.SetIssueMessage(value)); + + // Then + result.IsArgumentNullException("thread"); + } + + [Fact] + public void Should_Throw_If_Properties_Are_Null() + { + // Given + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = null + }; + var value = "foo"; + + // When + var result = Record.Exception(() => thread.SetIssueMessage(value)); + + // Then + result.IsInvalidOperationException("Properties collection is not created."); + } + + [Fact] + public void Should_Return_Message() + { + // Given + var message = "foo"; + var thread = + new GitPullRequestCommentThread + { + Id = 123, + Status = CommentThreadStatus.Active, + ThreadContext = new CommentThreadContext() { FilePath = "/foo.cs" }, + Comments = new List(), + Properties = new PropertiesCollection() + }; + + // When + thread.SetIssueMessage(message); + + // Then + thread.GetIssueMessage().ShouldBe(message); + } + } + } +} diff --git a/src/Cake.Issues.PullRequests.Tfs.Tests/TfsCredentialsExtensionsTests.cs b/src/Cake.Issues.PullRequests.Tfs.Tests/TfsCredentialsExtensionsTests.cs new file mode 100644 index 0000000..3654a3d --- /dev/null +++ b/src/Cake.Issues.PullRequests.Tfs.Tests/TfsCredentialsExtensionsTests.cs @@ -0,0 +1,81 @@ +namespace Cake.Issues.PullRequests.Tfs.Tests +{ + using Cake.Issues.PullRequests.Tfs.Authentication; + using Cake.Issues.Testing; + using Microsoft.VisualStudio.Services.Client; + using Microsoft.VisualStudio.Services.Common; + using Microsoft.VisualStudio.Services.OAuth; + using Shouldly; + using Xunit; + + public sealed class TfsCredentialsExtensionsTests + { + public sealed class TheToVssCredentialsExtension + { + [Fact] + public void Should_Throw_If_Credentials_Are_Null() + { + // Given + ITfsCredentials credentials = null; + + // When + var result = Record.Exception(() => credentials.ToVssCredentials()); + + // Then + result.IsArgumentNullException("credentials"); + } + + [Fact] + public void Should_Return_Ntlm_Credentials() + { + // Given + var credentials = new TfsNtlmCredentials(); + + // When + var result = credentials.ToVssCredentials(); + + // Then + result.ShouldNotBeNull(); + } + + [Fact] + public void Should_Return_Basic_Credentials() + { + // Given + var credentials = new TfsBasicCredentials("foo", "bar"); + + // When + var result = credentials.ToVssCredentials(); + + // Then + result.ShouldNotBeNull(); + } + + [Fact] + public void Should_Return_OAuth_Credentials() + { + // Given + var credentials = new TfsOAuthCredentials("foo"); + + // When + var result = credentials.ToVssCredentials(); + + // Then + result.ShouldNotBeNull(); + } + + [Fact] + public void Should_Return_Aad_Credentials() + { + // Given + var credentials = new TfsAadCredentials("foo", "bar"); + + // When + var result = credentials.ToVssCredentials(); + + // Then + result.ShouldNotBeNull(); + } + } + } +} diff --git a/src/Cake.Issues.PullRequests.Tfs/GitPullRequestCommentThreadExtensions.cs b/src/Cake.Issues.PullRequests.Tfs/GitPullRequestCommentThreadExtensions.cs index b483452..b8bd5a7 100644 --- a/src/Cake.Issues.PullRequests.Tfs/GitPullRequestCommentThreadExtensions.cs +++ b/src/Cake.Issues.PullRequests.Tfs/GitPullRequestCommentThreadExtensions.cs @@ -22,10 +22,20 @@ public static IPullRequestDiscussionThread ToPullRequestDiscussionThread(this Gi { thread.NotNull(nameof(thread)); + if (thread.ThreadContext == null) + { + throw new InvalidOperationException("ThreadContext is not created."); + } + + if (thread.Comments == null) + { + throw new InvalidOperationException("Comments list is not created."); + } + return new PullRequestDiscussionThread( thread.Id, thread.Status.ToPullRequestDiscussionStatus(), - new FilePath(thread.ThreadContext.FilePath.TrimStart('/')), + thread.ThreadContext.FilePath?.TrimStart('/'), thread.Comments.Select(x => x.ToPullRequestDiscussionComment())) { CommentSource = thread.GetCommentSource(), @@ -42,6 +52,11 @@ public static string GetCommentSource(this GitPullRequestCommentThread thread) { thread.NotNull(nameof(thread)); + if (thread.Properties == null) + { + throw new InvalidOperationException("Properties collection is not created."); + } + return thread.Properties.GetValue(CommentSourcePropertyName, string.Empty); } @@ -81,6 +96,11 @@ public static string GetIssueMessage(this GitPullRequestCommentThread thread) { thread.NotNull(nameof(thread)); + if (thread.Properties == null) + { + throw new InvalidOperationException("Properties collection is not created."); + } + return thread.Properties.GetValue(IssueMessagePropertyName, string.Empty); }