public async Task CreateWorkItemAsync_OnNonNullWorkItemDetailsAndWorkItem_CreatesIssue(bool isIssueClosed, bool hasAttachments)
        {
            // Arrange
            var issuesMock = new GitHubIssueMock();
            var ctorArgs   = new CtorArgs {
                Issues = issuesMock.Issues
            };
            GitHubRepoIssueReaderWriter target          = CreateTarget(ctorArgs);
            WorkItemDetails             workItemDetails = CreateSampleWorkItemDetails(hasAttachments: hasAttachments, isClosed: isIssueClosed);

            // Act
            await target.WriteWorkItemAsync(workItemDetails);

            // Assert: Owner + Repo
            Assert.NotNull(issuesMock.CreateIssueArgs.NewIssue);
            Assert.Equal(ctorArgs.RepoOwner, issuesMock.CreateIssueArgs.Owner);
            Assert.Equal(ctorArgs.Repo, issuesMock.CreateIssueArgs.Name);

            // Assert: Title + Body
            Assert.Equal(workItemDetails.WorkItem.Summary, issuesMock.CreateIssueArgs.NewIssue.Title);
            Assert.Contains(TextUtilities.GetFormattedWorkItemBody(workItemDetails.WorkItem, workItemDetails.FileAttachments), issuesMock.CreateIssueArgs.NewIssue.Body);

            // Assert: Labels
            Assert.Single(issuesMock.CreateIssueArgs.NewIssue.Labels);
            Assert.Contains(issuesMock.CreateIssueArgs.NewIssue.Labels, label => label == GitHubLabels.CodePlexMigrationInitiated);
            Assert.Single(issuesMock.UpdateIssueArgs.IssueUpdate.Labels);
            Assert.Contains(issuesMock.UpdateIssueArgs.IssueUpdate.Labels, label => label == GitHubLabels.CodePlexMigrated);

            // Assert: Attachments
            if (hasAttachments)
            {
                Assert.Contains(Resources.Attachments, issuesMock.CreateIssueArgs.NewIssue.Body);
            }
            else
            {
                Assert.DoesNotContain(Resources.Attachments, issuesMock.CreateIssueArgs.NewIssue.Body);
            }

            // Assert: Issue state: closed/open
            Assert.Equal(isIssueClosed ? ItemState.Closed : (ItemState?)null, issuesMock.UpdateIssueArgs.IssueUpdate.State);

            issuesMock.VerifyIssuesCallCount(methodName: nameof(IIssuesClient.Create), callCount: 1);
            issuesMock.VerifyIssuesCallCount(methodName: nameof(IIssuesClient.Update), callCount: 1);
            issuesMock.VerifyCommentCallCount(methodName: nameof(IIssueCommentsClient.Create), callCount: 0);
            issuesMock.VerifyCommentCallCount(methodName: nameof(IIssueCommentsClient.Delete), callCount: 0);
        }
        public async Task UpdateWorkItemAsync_IfWorkItemExists_Succeeds(bool hasAttachments)
        {
            // Arrange
            Issue           issue           = CreateSampleIssue(label: "AnyLabel");
            WorkItemDetails workItemDetails = CreateSampleWorkItemDetails(hasAttachments: hasAttachments);

            var      issuesMock = new GitHubIssueMock();
            var      searchMock = new GitHubSearchInBodyMock(searchResults: new[] { issue });
            CtorArgs ctorArgs   = new CtorArgs {
                Search = searchMock.Search, Issues = issuesMock.Issues
            };

            GitHubRepoIssueReaderWriter target = CreateTarget(ctorArgs);

            // Act
            await target.UpdateWorkItemAsync(workItemDetails);

            // Assert: Search
            Assert.Contains(workItemDetails.WorkItem.Id.ToString(), searchMock.SearchIssuesRequest.Term);
            Assert.Equal(IssueTypeQualifier.Issue, searchMock.SearchIssuesRequest.Type);
            Assert.Single(searchMock.SearchIssuesRequest.Repos);
            Assert.Equal(ctorArgs.FullRepoName, searchMock.SearchIssuesRequest.Repos[0]);
            Assert.Contains(searchMock.SearchIssuesRequest.In, q => q == IssueInQualifier.Body);
            searchMock.VerifySearchCallCount(callCount: 1);

            // Assert: Update -- Owner + Repo + Issue number
            Assert.Equal(ctorArgs.RepoOwner, issuesMock.UpdateIssueArgs.Owner);
            Assert.Equal(ctorArgs.Repo, issuesMock.UpdateIssueArgs.Name);
            Assert.Equal(issue.Number, issuesMock.UpdateIssueArgs.Number);

            // Assert: Update -- Title + Body
            Assert.Equal(workItemDetails.WorkItem.Summary, issuesMock.UpdateIssueArgs.IssueUpdate.Title);
            Assert.Contains(TextUtilities.GetFormattedWorkItemBody(workItemDetails.WorkItem, workItemDetails.FileAttachments), issuesMock.UpdateIssueArgs.IssueUpdate.Body);

            // Assert: Update -- Labels
            Assert.Single(issuesMock.UpdateIssueArgs.IssueUpdate.Labels);
            Assert.Contains(issuesMock.UpdateIssueArgs.IssueUpdate.Labels, label => label == GitHubLabels.CodePlexMigrated);

            // Assert: Update -- Attachments
            if (hasAttachments)
            {
                Assert.Contains(Resources.Attachments, issuesMock.UpdateIssueArgs.IssueUpdate.Body);
            }
            else
            {
                Assert.DoesNotContain(Resources.Attachments, issuesMock.UpdateIssueArgs.IssueUpdate.Body);
            }

            issuesMock.VerifyIssuesCallCount(methodName: nameof(IIssuesClient.Update), callCount: 2);
        }