Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,21 +191,14 @@ private List<Triple<D, N, Optional<ProjectIssueIdentifier>>> findOpenSonarqubeCo
return commentsForDiscussion.stream()
.findFirst()
.filter(note -> isNoteFromCurrentUser(note, currentUser))
.filter(note -> !isResolved(client, discussion, commentsForDiscussion, currentUser) || isSummaryComment(client, commentsForDiscussion.stream().findFirst().orElse(null)))
.filter(note -> !isResolved(client, discussion, commentsForDiscussion, currentUser))
.map(note -> new ImmutableTriple<>(discussion, note, parseIssueDetails(client, note)));
})
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}

private boolean isSummaryComment(C client, N note) {
return Optional.of(note)
.flatMap(message -> parseIssueDetails(client, message))
.filter(projectIssueIdentifier -> DECORATOR_SUMMARY_COMMENT.equals(projectIssueIdentifier.getIssueKey()))
.isPresent();
}

private List<String> closeOldDiscussionsAndExtractRemainingKeys(C client, U currentUser,
List<Triple<D, N, Optional<ProjectIssueIdentifier>>> openSonarqubeComments,
List<PostAnalysisIssueVisitor.ComponentIssue> openIssues,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,84 @@ void shouldAddNoteToSummaryCommentThreadIfOtherCommentsInDiscussionAndInlineComm
verify(azureDevopsClient).retrieveThreads(azureProject, azureRepository, pullRequestId);
}

@Test
void shouldNotAddNoteToSummaryCommentThreadIfOtherCommentsInDiscussionAndInlineCommentsEnabledButCloseNoteAlreadyPresent() throws IOException {
String azureProject = "azure-project";
String azureRepository = "azure-repo";
int pullRequestId = 321;

AnalysisSummary analysisSummary = mock();
when(reportGenerator.createAnalysisSummary(any())).thenReturn(analysisSummary);

when(analysisDetails.getPullRequestId()).thenReturn(Integer.toString(pullRequestId));
when(projectAlmSettingDto.getAlmSlug()).thenReturn(azureProject);
when(projectAlmSettingDto.getAlmRepo()).thenReturn(azureRepository);
when(projectAlmSettingDto.getInlineAnnotationsEnabled()).thenReturn(true);

when(analysisDetails.getQualityGateStatus()).thenReturn(QualityGate.Status.OK);

AzureDevopsClient azureDevopsClient = mock();
when(azureDevopsClientFactory.createClient(any(), any())).thenReturn(azureDevopsClient);

PullRequest pullRequest = mock();
when(pullRequest.getId()).thenReturn(pullRequestId);
when(azureDevopsClient.retrievePullRequest(any(), any(), anyInt())).thenReturn(pullRequest);
Repository repository = mock();
Project project = mock();
when(pullRequest.getRepository()).thenReturn(repository);
when(repository.getProject()).thenReturn(project);
when(project.getName()).thenReturn(azureProject);
when(repository.getRemoteUrl()).thenReturn("https://remote.url/path/to/repo");
when(repository.getName()).thenReturn(azureRepository);

AzureDevOpsPullRequestDecorator underTest = new AzureDevOpsPullRequestDecorator(scmInfoRepository, azureDevopsClientFactory, reportGenerator, markdownFormatterFactory);

IdentityRef sonarqubeUser = mock();
when(sonarqubeUser.getId()).thenReturn("sonarqube");

ConnectionData connectionData = mock();
ConnectionData.Identity authenticatedUser = mock();
when(authenticatedUser.getId()).thenReturn("sonarqube");
when(connectionData.getAuthenticatedUser()).thenReturn(authenticatedUser);
when(azureDevopsClient.getConnectionData()).thenReturn(connectionData);

Comment comment1 = mock();
when(comment1.getId()).thenReturn(101);
when(comment1.getAuthor()).thenReturn(sonarqubeUser);
when(comment1.getContent()).thenReturn("Summary comment" + System.lineSeparator() + "[View in SonarQube](http://host.domain/dashboard?id=projectKey&pullRequest=123)");
when(comment1.getCommentType()).thenReturn(CommentType.TEXT);

IdentityRef otherUser = mock();
when(otherUser.getId()).thenReturn("username");
Comment comment2 = mock();
when(comment2.getId()).thenReturn(102);
when(comment2.getAuthor()).thenReturn(otherUser);
when(comment2.getContent()).thenReturn("Another comment");
when(comment2.getCommentType()).thenReturn(CommentType.TEXT);

Comment comment3 = mock();
when(comment3.getId()).thenReturn(101);
when(comment3.getAuthor()).thenReturn(sonarqubeUser);
when(comment3.getContent()).thenReturn("This summary note is outdated, but due to other comments being present in this discussion, the discussion is not being removed. Please manually resolve this discussion once the other comments have been reviewed.");
when(comment3.getCommentType()).thenReturn(CommentType.TEXT);


CommentThread discussion = mock();
when(discussion.getId()).thenReturn(101);
when(discussion.getComments()).thenReturn(List.of(comment1, comment2, comment3));

CommentThread newSummaryThread = mock();
when(azureDevopsClient.createThread(any(), any(), anyInt(), any())).thenReturn(newSummaryThread);

when(azureDevopsClient.retrieveThreads(any(), any(), anyInt())).thenReturn(List.of(discussion));

underTest.decorateQualityGateStatus(analysisDetails, almSettingDto, projectAlmSettingDto);

verify(azureDevopsClient, never()).addCommentToThread(any(), any(), anyInt(), anyInt(), any());
verify(azureDevopsClient, never()).deletePullRequestThreadComment(any(), any(), anyInt(), anyInt(), anyInt());
verify(azureDevopsClient).retrieveThreads(azureProject, azureRepository, pullRequestId);
}

@Test
void shouldNotAddNoteToSummaryCommentThreadIfOtherCommentsInDiscussionAndInlineCommentsNotEnabled() throws IOException {
String azureProject = "azure-project";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2021-2025 Michael Clarke
* Copyright (C) 2021-2026 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -774,6 +774,41 @@ void shouldAddNoteToSummaryCommentThreadIfOtherCommentsInDiscussion() throws IOE
verify(gitlabClient).getMergeRequestDiscussions(PROJECT_ID, MERGE_REQUEST_IID);
}

@Test
void shouldNotAddNoteToSummaryCommentThreadIfOtherCommentsInDiscussionAndNoteAlreadyPresent() throws IOException {
Note note = mock();
when(note.getId()).thenReturn(101L);
when(note.getAuthor()).thenReturn(sonarqubeUser);
when(note.getBody()).thenReturn("Summary comment" + System.lineSeparator() + "[View in SonarQube](http://host.domain/dashboard?id=projectKey&pullRequest=123)");
when(note.isSystem()).thenReturn(false);

User otherUser = mock();
when(otherUser.getUsername()).thenReturn("username");
Note note2 = mock();
when(note2.getId()).thenReturn(102L);
when(note2.getAuthor()).thenReturn(otherUser);
when(note2.getBody()).thenReturn("Another comment");
when(note2.isSystem()).thenReturn(false);

Note note3 = mock();
when(note3.getId()).thenReturn(102L);
when(note3.getAuthor()).thenReturn(sonarqubeUser);
when(note3.getBody()).thenReturn("This summary note is outdated, but due to other comments being present in this discussion, the discussion is not being removed. Please manually resolve this discussion once the other comments have been reviewed.");
when(note3.isSystem()).thenReturn(false);

Discussion discussion = mock();
when(discussion.getId()).thenReturn("discussionId");
when(discussion.getNotes()).thenReturn(List.of(note, note2, note3));

when(gitlabClient.getMergeRequestDiscussions(anyLong(), anyLong())).thenReturn(Collections.singletonList(discussion));

underTest.decorateQualityGateStatus(analysisDetails, almSettingDto, projectAlmSettingDto);

verify(gitlabClient, never()).addMergeRequestDiscussionNote(anyLong(), anyLong(), any(), any());
verify(gitlabClient, never()).deleteMergeRequestDiscussionNote(anyLong(), anyLong(), any(), anyLong());
verify(gitlabClient).getMergeRequestDiscussions(PROJECT_ID, MERGE_REQUEST_IID);
}

@Test
void shouldNotTryAndCleanupNonSummaryNote() throws IOException {
Note note = mock();
Expand Down
Loading