diff --git a/README.md b/README.md index 7758f650..db1aa5a0 100755 --- a/README.md +++ b/README.md @@ -38,18 +38,19 @@ Go to Stash general settings screen on SonarQube server to fill: **Stash base URL** (sonar.stash.url): To define Stash instance. -**Stash base user** (sonar.stash.login): To define user to push violations on Stash pull-request. User must have **REPO_READ permission** for the repository. -**Please notice Stash password needs to be provided to sonar-runner through sonar.stash.password in commandline**. +**Stash base user** (sonar.stash.login): To define user to push violations on Stash pull-request. User must have **REPO_READ permission** for the repository. **Please notice Stash password needs to be provided to sonar-runner through sonar.stash.password in commandline**. **Stash issue threshold** (sonar.stash.issue.threshold): To limit the number of issue pushed to Stash. **Stash timeout** (sonar.stash.timeout): To timeout when Stash Rest api does not replied with expected. -**Stash reviewer approval** (sonar.stash.reviewer.approval): SonarQube is able to approve the pull-request if there is no new issue introduced by the change. -By default, this feature is deactivated: if activated, **Stash base user must have REPO_WRITE permission for the repositories.** +**Stash reviewer approval** (sonar.stash.reviewer.approval): SonarQube is able to approve the pull-request if there is no new issue introduced by the change. By default, this feature is deactivated: if activated, **Stash base user must have REPO_WRITE permission for the repositories.** ![Screenshot SonarQube plugin](resources/Sonar-plugin-approver.PNG) +**Stash tasks severity threshold** (sonar.stash.task.issue.severity.threshold): SonarQube is able to create tasks for all issues with a severity higher to the threshold. By default, this feature is deactivated (threshold: NONE). + +![Screenshot SonarQube plugin](resources/Stash-plugin-task.PNG) ## How to run the plugin? @@ -65,4 +66,4 @@ sonar-runner -Dsonar.analysis.mode=incremental -Dsonar.stash.notification -Dsona If needed, you can reset comments published during the previous SonarQube analysis of your pull-request. Please add **sonar.stash.comments.reset** option to your SonarQube analysis. Please notice only comments linked to the **sonar.stash.login** user will be deleted. This reset will be the first action performed by the plugin. ``` sonar-runner -Dsonar.analysis.mode=incremental -Dsonar.stash.notification -Dsonar.stash.comments.reset -Dsonar.stash.project= -Dsonar.stash.repository= -Dsonar.stash.pullrequest.id= -Dsonar.stash.password=... -``` +``` \ No newline at end of file diff --git a/resources/Sonar-plugin-configuration.PNG b/resources/Sonar-plugin-configuration.PNG index 6a964f17..6f253939 100755 Binary files a/resources/Sonar-plugin-configuration.PNG and b/resources/Sonar-plugin-configuration.PNG differ diff --git a/resources/Stash-plugin-task.PNG b/resources/Stash-plugin-task.PNG new file mode 100755 index 00000000..9c16a448 Binary files /dev/null and b/resources/Stash-plugin-task.PNG differ diff --git a/src/main/java/org/sonar/plugins/stash/StashPlugin.java b/src/main/java/org/sonar/plugins/stash/StashPlugin.java index 409d1a7e..a683bbd7 100755 --- a/src/main/java/org/sonar/plugins/stash/StashPlugin.java +++ b/src/main/java/org/sonar/plugins/stash/StashPlugin.java @@ -3,12 +3,14 @@ import java.util.Arrays; import java.util.List; +import org.apache.commons.collections.ListUtils; import org.sonar.api.Properties; import org.sonar.api.Property; import org.sonar.api.PropertyType; import org.sonar.api.SonarPlugin; import org.sonar.api.config.PropertyDefinition; import org.sonar.api.resources.Qualifiers; +import org.sonar.api.rule.Severity; @Properties({ @Property(key = StashPlugin.STASH_NOTIFICATION, name = "Stash Notification", defaultValue = "false", description = "Analysis result will be issued in Stash pull request", global = false), @@ -21,10 +23,13 @@ public class StashPlugin extends SonarPlugin { private static final String DEFAULT_STASH_TIMEOUT_VALUE = "10000"; private static final String DEFAULT_STASH_THRESHOLD_VALUE = "100"; - private static final String CONFIG_PAGE_SUB_CATEGORY_GENERAL = "General"; + private static final String CONFIG_PAGE_SUB_CATEGORY_STASH = "Stash"; + + public static final String SEVERITY_NONE = "NONE"; + + // INFO, MINOR, MAJOR, CRITICAL, BLOCKER + protected static final List SEVERITY_LIST = Severity.ALL; - public static final String INCREMENTAL_MODE = "incremental"; - public static final String CONTEXT_ISSUE_TYPE = "CONTEXT"; public static final String REMOVED_ISSUE_TYPE = "REMOVED"; public static final String ADDED_ISSUE_TYPE = "ADDED"; @@ -41,6 +46,7 @@ public class StashPlugin extends SonarPlugin { public static final String STASH_ISSUE_THRESHOLD = "sonar.stash.issue.threshold"; public static final String STASH_TIMEOUT = "sonar.stash.timeout"; public static final String SONARQUBE_URL = "sonar.host.url"; + public static final String STASH_TASK_SEVERITY_THRESHOLD = "sonar.stash.task.issue.severity.threshold"; @Override public List getExtensions() { @@ -54,32 +60,40 @@ public List getExtensions() { PropertyDefinition.builder(STASH_URL) .name("Stash base URL") .description("HTTP URL of Stash instance, such as http://yourhost.yourdomain/stash") - .subCategory(CONFIG_PAGE_SUB_CATEGORY_GENERAL) + .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) .onQualifiers(Qualifiers.PROJECT).build(), PropertyDefinition.builder(STASH_LOGIN) .name("Stash base User") .description("User to push data on Stash instance") - .subCategory(CONFIG_PAGE_SUB_CATEGORY_GENERAL) + .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) .onQualifiers(Qualifiers.PROJECT).build(), PropertyDefinition.builder(STASH_TIMEOUT) .name("Stash issue Timeout") .description("Timeout when pushing a new issue to Stash (in ms)") - .subCategory(CONFIG_PAGE_SUB_CATEGORY_GENERAL) + .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) .onQualifiers(Qualifiers.PROJECT) .defaultValue(DEFAULT_STASH_TIMEOUT_VALUE).build(), PropertyDefinition.builder(STASH_REVIEWER_APPROVAL) .name("Stash reviewer approval") .description("Does SonarQube approve the pull-request if there is no new issues?") - .subCategory(CONFIG_PAGE_SUB_CATEGORY_GENERAL) + .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) .onQualifiers(Qualifiers.PROJECT) .type(PropertyType.BOOLEAN) .defaultValue("false").build(), PropertyDefinition.builder(STASH_ISSUE_THRESHOLD) .name("Stash issue Threshold") .description("Threshold to limit the number of issues pushed to Stash server") - .subCategory(CONFIG_PAGE_SUB_CATEGORY_GENERAL) + .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) .onQualifiers(Qualifiers.PROJECT) - .defaultValue(DEFAULT_STASH_THRESHOLD_VALUE).build()); + .defaultValue(DEFAULT_STASH_THRESHOLD_VALUE).build(), + PropertyDefinition.builder(STASH_TASK_SEVERITY_THRESHOLD) + .name("Stash tasks severity threshold") + .description("Only create tasks for issues with the same or higher severity") + .type(PropertyType.SINGLE_SELECT_LIST) + .subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH) + .onQualifiers(Qualifiers.PROJECT) + .defaultValue(SEVERITY_NONE) + .options(ListUtils.sum(Arrays.asList(SEVERITY_NONE), SEVERITY_LIST)).build()); } - } + diff --git a/src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java b/src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java index 436ce105..c2e251ce 100755 --- a/src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java +++ b/src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java @@ -60,4 +60,8 @@ public boolean canApprovePullRequest() { public boolean resetComments() { return settings.getBoolean(StashPlugin.STASH_RESET_COMMENTS); } + + public String getTaskIssueSeverityThreshold() { + return settings.getString(StashPlugin.STASH_TASK_SEVERITY_THRESHOLD); + } } \ No newline at end of file diff --git a/src/main/java/org/sonar/plugins/stash/StashRequestFacade.java b/src/main/java/org/sonar/plugins/stash/StashRequestFacade.java index 4d0497af..a6f83766 100755 --- a/src/main/java/org/sonar/plugins/stash/StashRequestFacade.java +++ b/src/main/java/org/sonar/plugins/stash/StashRequestFacade.java @@ -3,8 +3,10 @@ import java.io.File; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; @@ -21,6 +23,7 @@ import org.sonar.plugins.stash.issue.StashCommentReport; import org.sonar.plugins.stash.issue.StashDiffReport; import org.sonar.plugins.stash.issue.StashPullRequest; +import org.sonar.plugins.stash.issue.StashTask; import org.sonar.plugins.stash.issue.StashUser; import org.sonar.plugins.stash.issue.collector.SonarQubeCollector; @@ -134,6 +137,9 @@ public void postCommentPerIssue(String project, String repository, String pullRe } } + // Severity available to create a task + List taskSeverities = getReportedSeverities(); + for (SonarQubeIssue issue : issueReport.getIssues()) { StashCommentReport comments = commentsByFile.get(issue.getPath()); @@ -151,15 +157,22 @@ public void postCommentPerIssue(String project, String repository, String pullRe long line = diffReport.getLine(issue.getPath(), issue.getLine()); - stashClient.postCommentLineOnPullRequest(project, - repository, - pullRequestId, - MarkdownPrinter.printIssueMarkdown(issue, sonarQubeURL), - issue.getPath(), - line, - type); + StashComment comment = stashClient.postCommentLineOnPullRequest(project, + repository, + pullRequestId, + MarkdownPrinter.printIssueMarkdown(issue, sonarQubeURL), + issue.getPath(), + line, + type); LOGGER.debug("Comment \"{}\" has been created ({}) on file {} ({})", issue.getRule(), type, issue.getPath(), line); + + // Create task linked to the comment if configured + if (taskSeverities.contains(issue.getSeverity())) { + stashClient.postTaskOnComment(issue.getMessage(), comment.getId()); + + LOGGER.debug("Comment \"{}\" has been linked to a Stash task", comment.getId()); + } } } } @@ -289,7 +302,19 @@ public void resetComments(String project, String repository, String pullRequestI // delete comment if published by the current SQ user if (sonarUser.getId() == comment.getAuthor().getId()) { - stashClient.deletePullRequestComment(project, repository, pullRequestId, comment); + + // comment contains tasks which cannot be deleted => do nothing + if (comment.containsPermanentTasks()) { + LOGGER.debug("Comment \"{}\" (path:\"{}\", line:\"{}\") CANNOT be deleted because one of its tasks is not deletable.", comment.getId(), comment.getPath(), comment.getLine()); + } else { + + // delete tasks linked to the current comment + for (StashTask task : comment.getTasks()) { + stashClient.deleteTaskOnComment(task); + } + + stashClient.deletePullRequestComment(project, repository, pullRequestId, comment); + } } } @@ -300,4 +325,28 @@ public void resetComments(String project, String repository, String pullRequestI LOGGER.debug("Exception stack trace", e); } } + + /** + * Get reported severities to create a task. + */ + public List getReportedSeverities() { + List result = new ArrayList<>(); + String threshold = config.getTaskIssueSeverityThreshold(); + + // threshold == NONE, no severities reported + if (! StringUtils.equals(threshold, StashPlugin.SEVERITY_NONE)) { + + // INFO, MINOR, MAJOR, CRITICAL, BLOCKER + boolean hit = false; + for (String severity : StashPlugin.SEVERITY_LIST) { + + if (hit || StringUtils.equals(severity, threshold)) { + result.add(severity); + hit = true; + } + } + } + + return result; + } } diff --git a/src/main/java/org/sonar/plugins/stash/client/StashClient.java b/src/main/java/org/sonar/plugins/stash/client/StashClient.java index 5adba90c..fea10bc7 100755 --- a/src/main/java/org/sonar/plugins/stash/client/StashClient.java +++ b/src/main/java/org/sonar/plugins/stash/client/StashClient.java @@ -16,8 +16,9 @@ import org.sonar.plugins.stash.exceptions.StashReportExtractionException; import org.sonar.plugins.stash.issue.StashComment; import org.sonar.plugins.stash.issue.StashCommentReport; -import org.sonar.plugins.stash.issue.StashDiffReport; -import org.sonar.plugins.stash.issue.StashPullRequest; +import org.sonar.plugins.stash.issue.StashDiffReport; +import org.sonar.plugins.stash.issue.StashPullRequest; +import org.sonar.plugins.stash.issue.StashTask; import org.sonar.plugins.stash.issue.StashUser; import org.sonar.plugins.stash.issue.collector.StashCollector; @@ -43,6 +44,7 @@ public class StashClient { private static final String COMMENT_PULL_REQUEST_API = COMMENTS_PULL_REQUEST_API + "/{4}?version={5}"; private static final String DIFF_PULL_REQUEST_API = PULL_REQUEST_API + "/diff"; private static final String APPROVAL_PULL_REQUEST_API = PULL_REQUEST_API + "/approve"; + private static final String TASKS_API = REST_API + "tasks"; private static final String PULL_REQUEST_APPROVAL_POST_ERROR_MESSAGE = "Unable to change status of pull-request {0} #{1}. Received {2} with message {3}."; private static final String PULL_REQUEST_GET_ERROR_MESSAGE = "Unable to retrieve pull-request {0} #{1}. Received {2} with message {3}."; @@ -51,6 +53,8 @@ public class StashClient { private static final String COMMENT_POST_ERROR_MESSAGE = "Unable to post a comment to {0} #{1}. Received {2} with message {3}."; private static final String COMMENT_GET_ERROR_MESSAGE = "Unable to get comment linked to {0} #{1}. Received {2} with message {3}."; private static final String COMMENT_DELETION_ERROR_MESSAGE = "Unable to delete comment {0} from pull-request {1} #{2}. Received {3} with message {4}."; + private static final String TASK_POST_ERROR_MESSAGE = "Unable to post a task on comment {0}. Received {1} with message {2}."; + private static final String TASK_DELETION_ERROR_MESSAGE = "Unable to delete task {0}. Received {1} with message {2}."; public StashClient(String url, StashCredentials credentials, int stashTimeout) { this.baseUrl = url; @@ -171,9 +175,10 @@ public StashDiffReport getPullRequestDiffs(String project, String repository, St return result; } - public void postCommentLineOnPullRequest(String project, String repository, String pullRequestId, String message, String path, long line, String type) + public StashComment postCommentLineOnPullRequest(String project, String repository, String pullRequestId, String message, String path, long line, String type) throws StashClientException { - + StashComment result = null; + String request = MessageFormat.format(COMMENTS_PULL_REQUEST_API, baseUrl + REST_API, project, repository, pullRequestId); @@ -204,11 +209,17 @@ public void postCommentLineOnPullRequest(String project, String repository, Stri String responseMessage = response.getStatusText(); throw new StashClientException(MessageFormat.format(COMMENT_POST_ERROR_MESSAGE, repository, pullRequestId, responseCode, responseMessage)); } - } catch (ExecutionException | TimeoutException | IOException | InterruptedException e) { + + // get generated comment + result = StashCollector.extractComment(response.getResponseBody(), path, line); + + } catch (ExecutionException | TimeoutException | IOException | InterruptedException | StashReportExtractionException e) { throw new StashClientException(e); } finally{ httpClient.close(); } + + return result; } public StashUser getUser(String userSlug) @@ -339,7 +350,53 @@ public void resetPullRequestApproval(String project, String repository, String p httpClient.close(); } } + + public void postTaskOnComment(String message, Long commentId) throws StashClientException { + String request = baseUrl + TASKS_API; + + JSONObject anchor = new JSONObject(); + anchor.put("id", commentId); + anchor.put("type", "COMMENT"); + + JSONObject json = new JSONObject(); + json.put("anchor", anchor); + json.put("text", message); + + try (AsyncHttpClient httpClient = createHttpClient()) { + + BoundRequestBuilder requestBuilder = httpClient.preparePost(request); + requestBuilder.setBody(json.toString()); + + Response response = executeRequest(requestBuilder); + int responseCode = response.getStatusCode(); + if (responseCode != HttpURLConnection.HTTP_CREATED) { + String responseMessage = response.getStatusText(); + throw new StashClientException(MessageFormat.format(TASK_POST_ERROR_MESSAGE, commentId, responseCode, responseMessage)); + } + } catch (ExecutionException | TimeoutException | IOException | InterruptedException e) { + throw new StashClientException(e); + } + } + public void deleteTaskOnComment(StashTask task) throws StashClientException { + String request = baseUrl + TASKS_API + "/" + task.getId(); + + AsyncHttpClient httpClient = createHttpClient(); + BoundRequestBuilder requestBuilder = httpClient.prepareDelete(request); + + try { + Response response = executeRequest(requestBuilder); + int responseCode = response.getStatusCode(); + if (responseCode != HttpURLConnection.HTTP_NO_CONTENT) { + String responseMessage = response.getStatusText(); + throw new StashClientException(MessageFormat.format(TASK_DELETION_ERROR_MESSAGE, task.getId(), responseCode, responseMessage)); + } + } catch (ExecutionException | TimeoutException | InterruptedException | IOException e) { + throw new StashClientException(e); + } finally{ + httpClient.close(); + } + } Response executeRequest(final BoundRequestBuilder requestBuilder) throws InterruptedException, IOException, ExecutionException, TimeoutException { diff --git a/src/main/java/org/sonar/plugins/stash/issue/StashComment.java b/src/main/java/org/sonar/plugins/stash/issue/StashComment.java index 1fe8e07c..fda51d00 100755 --- a/src/main/java/org/sonar/plugins/stash/issue/StashComment.java +++ b/src/main/java/org/sonar/plugins/stash/issue/StashComment.java @@ -1,14 +1,18 @@ package org.sonar.plugins.stash.issue; +import java.util.ArrayList; +import java.util.List; + public class StashComment { private final long id; private final String message; - private final String path; private final StashUser author; private final long version; private long line; + private String path; + private List tasks; public StashComment(long id, String message, String path, Long line, StashUser author, long version) { this.id = id; @@ -23,6 +27,8 @@ public StashComment(long id, String message, String path, Long line, StashUser a } else{ this.line = line.longValue(); } + + tasks = new ArrayList<>(); } public long getId(){ @@ -33,6 +39,10 @@ public void setLine(long line){ this.line = line; } + public void setPath(String path){ + this.path = path; + } + public String getMessage() { return message; } @@ -53,6 +63,27 @@ public long getVersion() { return version; } + public List getTasks() { + return tasks; + } + + public void addTask(StashTask task){ + tasks.add(task); + } + + public boolean containsPermanentTasks() { + boolean result = false; + + for (StashTask task: tasks){ + if (! task.isDeletable()){ + result = true; + break; + } + } + + return result; + } + @Override public boolean equals(Object object) { boolean result = false; diff --git a/src/main/java/org/sonar/plugins/stash/issue/StashTask.java b/src/main/java/org/sonar/plugins/stash/issue/StashTask.java new file mode 100755 index 00000000..77031b2f --- /dev/null +++ b/src/main/java/org/sonar/plugins/stash/issue/StashTask.java @@ -0,0 +1,32 @@ +package org.sonar.plugins.stash.issue; + +public class StashTask { + + private final Long id; + private final String text; + private final String state; + private final boolean deletable; + + public StashTask(Long id, String text, String state, boolean deletable) { + this.id = id; + this.text = text; + this.state = state; + this.deletable = deletable; + } + + public Long getId() { + return id; + } + + public String getText() { + return text; + } + + public String getState() { + return state; + } + + public boolean isDeletable() { + return deletable; + } +} diff --git a/src/main/java/org/sonar/plugins/stash/issue/collector/StashCollector.java b/src/main/java/org/sonar/plugins/stash/issue/collector/StashCollector.java index 4698be4c..a3f862fc 100755 --- a/src/main/java/org/sonar/plugins/stash/issue/collector/StashCollector.java +++ b/src/main/java/org/sonar/plugins/stash/issue/collector/StashCollector.java @@ -12,6 +12,7 @@ import org.sonar.plugins.stash.issue.StashDiff; import org.sonar.plugins.stash.issue.StashDiffReport; import org.sonar.plugins.stash.issue.StashPullRequest; +import org.sonar.plugins.stash.issue.StashTask; import org.sonar.plugins.stash.issue.StashUser; public final class StashCollector { @@ -32,21 +33,8 @@ public static StashCommentReport extractComments(String jsonBody) throws StashRe for (Object obj : jsonValues.toArray()) { JSONObject jsonComment = (JSONObject) obj; - long id = (long) jsonComment.get("id"); - String message = (String) jsonComment.get("text"); - JSONObject jsonAnchor = (JSONObject) jsonComment.get("anchor"); - String path = (String) jsonAnchor.get("path"); - - // can be null if comment is attached to the global file - Long line = (Long) jsonAnchor.get("line"); - - long version = (long) jsonComment.get("version"); - - JSONObject jsonAuthor = (JSONObject) jsonComment.get("author"); - StashUser stashUser = extractUser(jsonAuthor.toJSONString()); - - StashComment comment = new StashComment(id, message, path, line, stashUser, version); + StashComment comment = extractComment(jsonComment.toJSONString()); result.add(comment); } } @@ -56,7 +44,55 @@ public static StashCommentReport extractComments(String jsonBody) throws StashRe return result; } + + public static StashComment extractComment(String jsonBody, String path, Long line) throws StashReportExtractionException { + StashComment result = null; + + try { + JSONObject jsonComment = (JSONObject) new JSONParser().parse(jsonBody); + + long id = (long) jsonComment.get("id"); + String message = (String) jsonComment.get("text"); + + long version = (long) jsonComment.get("version"); + + JSONObject jsonAuthor = (JSONObject) jsonComment.get("author"); + StashUser stashUser = extractUser(jsonAuthor.toJSONString()); + + result = new StashComment(id, message, path, line, stashUser, version); + + } catch (ParseException e) { + throw new StashReportExtractionException(e); + } + + return result; + } + + public static StashComment extractComment(String jsonBody) throws StashReportExtractionException { + StashComment result = null; + + try { + JSONObject jsonComment = (JSONObject) new JSONParser().parse(jsonBody); + + JSONObject jsonAnchor = (JSONObject) jsonComment.get("anchor"); + if (jsonAnchor == null) { + throw new StashReportExtractionException("JSON Comment does not contain any \"anchor\" tag to describe comment \"line\" and \"path\""); + } + + String path = (String) jsonAnchor.get("path"); + + // can be null if comment is attached to the global file + Long line = (Long) jsonAnchor.get("line"); + result = extractComment(jsonBody, path, line); + + } catch (ParseException e) { + throw new StashReportExtractionException(e); + } + + return result; + } + public static StashPullRequest extractPullRequest(String project, String repository, String pullRequestId, String jsonBody) throws StashReportExtractionException { StashPullRequest result = new StashPullRequest(project, repository, pullRequestId); @@ -169,6 +205,16 @@ public static StashDiffReport extractDiffs(String jsonBody) throws StashReportEx StashComment comment = new StashComment(lineCommentId, lineCommentMessage, path, destination, author, lineCommentVersion); diff.addComment(comment); + + // get the tasks linked to the current comment + JSONArray jsonTasks = (JSONArray) jsonLineComment.get("tasks"); + if (jsonTasks != null) { + for (Object objTask : jsonTasks.toArray()) { + JSONObject jsonTask = (JSONObject) objTask; + + comment.addTask(extractTask(jsonTask.toString())); + } + } } } } @@ -220,6 +266,28 @@ public static StashDiffReport extractDiffs(String jsonBody) throws StashReportEx return result; } + public static StashTask extractTask(String jsonBody) throws StashReportExtractionException { + try { + JSONObject jsonTask = (JSONObject) new JSONParser().parse(jsonBody); + + long taskId = (long) jsonTask.get("id"); + String taskText = (String) jsonTask.get("text"); + String taskState = (String) jsonTask.get("state"); + + boolean deletable = true; + + JSONObject objPermission = (JSONObject) jsonTask.get("permittedOperations"); + if (objPermission != null) { + deletable = (boolean) objPermission.get("deletable"); + } + + return new StashTask(taskId, taskText, taskState, deletable); + + } catch (ParseException e) { + throw new StashReportExtractionException(e); + } + } + public static boolean isLastPage(String jsonBody) throws StashReportExtractionException { boolean result = true; diff --git a/src/test/java/org/sonar/plugins/stash/StashRequestFacadeTest.java b/src/test/java/org/sonar/plugins/stash/StashRequestFacadeTest.java index 14dc89c3..ba98bc84 100755 --- a/src/test/java/org/sonar/plugins/stash/StashRequestFacadeTest.java +++ b/src/test/java/org/sonar/plugins/stash/StashRequestFacadeTest.java @@ -13,6 +13,7 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; +import java.util.List; import org.junit.Before; import org.junit.Rule; @@ -20,6 +21,7 @@ import org.junit.rules.ExpectedException; import org.mockito.Mock; import org.mockito.Mockito; +import org.sonar.api.rule.Severity; import org.sonar.plugins.stash.client.StashClient; import org.sonar.plugins.stash.client.StashCredentials; import org.sonar.plugins.stash.exceptions.StashClientException; @@ -31,6 +33,7 @@ import org.sonar.plugins.stash.issue.StashCommentReport; import org.sonar.plugins.stash.issue.StashDiffReport; import org.sonar.plugins.stash.issue.StashPullRequest; +import org.sonar.plugins.stash.issue.StashTask; import org.sonar.plugins.stash.issue.StashUser; public class StashRequestFacadeTest { @@ -55,6 +58,15 @@ public class StashRequestFacadeTest { @Mock StashCommentReport stashCommentsReport2; + @Mock + StashComment comment1; + + @Mock + StashComment comment2; + + @Mock + StashComment comment3; + @Mock StashUser stashUser; @@ -78,6 +90,8 @@ public class StashRequestFacadeTest { @Before public void setUp() throws Exception { config = mock(StashPluginConfiguration.class); + when(config.getTaskIssueSeverityThreshold()).thenReturn(StashPlugin.SEVERITY_NONE); + myFacade = new StashRequestFacade(config); stashClient = mock(StashClient.class); @@ -94,26 +108,64 @@ public void setUp() throws Exception { issueReport = new SonarQubeIssuesReport(); - SonarQubeIssue issue1 = new SonarQubeIssue("key1", "severity1", "message1", "rule1", FILE_PATH_1, 1); + SonarQubeIssue issue1 = new SonarQubeIssue("key1", Severity.CRITICAL, "message1", "rule1", FILE_PATH_1, 1); stashCommentMessage1 = MarkdownPrinter.printIssueMarkdown(issue1, SONARQUBE_URL); issueReport.add(issue1); - SonarQubeIssue issue2 = new SonarQubeIssue("key2", "severity2", "message2", "rule2", FILE_PATH_1, 2); + SonarQubeIssue issue2 = new SonarQubeIssue("key2", Severity.MAJOR, "message2", "rule2", FILE_PATH_1, 2); stashCommentMessage2 = MarkdownPrinter.printIssueMarkdown(issue2, SONARQUBE_URL); issueReport.add(issue2); - SonarQubeIssue issue3 = new SonarQubeIssue("key3", "severity3", "message3", "rule3", FILE_PATH_2, 1); + SonarQubeIssue issue3 = new SonarQubeIssue("key3", Severity.INFO, "message3", "rule3", FILE_PATH_2, 1); stashCommentMessage3 = MarkdownPrinter.printIssueMarkdown(issue3, SONARQUBE_URL); issueReport.add(issue3); - StashComment comment = mock(StashComment.class); - when(comment.getAuthor()).thenReturn(stashUser); + StashTask task1 = mock(StashTask.class); + when(task1.getId()).thenReturn((long) 1111); + + List taskList1 = new ArrayList<>(); + taskList1.add(task1); + + comment1 = mock(StashComment.class); + when(comment1.getId()).thenReturn((long) 1111); + when(comment1.getAuthor()).thenReturn(stashUser); + when(stashClient.postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage1, FILE_PATH_1, 1, STASH_DIFF_TYPE)).thenReturn(comment1); + when(comment1.getTasks()).thenReturn(taskList1); + when(comment1.containsPermanentTasks()).thenReturn(false); + + StashTask task2 = mock(StashTask.class); + when(task1.getId()).thenReturn((long) 2222); + + List taskList2 = new ArrayList<>(); + taskList2.add(task2); + + comment2 = mock(StashComment.class); + when(comment2.getId()).thenReturn((long) 2222); + when(comment2.getAuthor()).thenReturn(stashUser); + when(stashClient.postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage2, FILE_PATH_1, 2, STASH_DIFF_TYPE)).thenReturn(comment2); + when(comment2.getTasks()).thenReturn(taskList2); + when(comment2.containsPermanentTasks()).thenReturn(false); + + StashTask task3 = mock(StashTask.class); + when(task3.getId()).thenReturn((long) 3333); + + List taskList3 = new ArrayList<>(); + taskList3.add(task3); + + comment3 = mock(StashComment.class); + when(comment3.getId()).thenReturn((long) 3333); + when(comment3.getAuthor()).thenReturn(stashUser); + when(stashClient.postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage3, FILE_PATH_2, 1, STASH_DIFF_TYPE)).thenReturn(comment3); + when(comment3.getTasks()).thenReturn(taskList3); + when(comment3.containsPermanentTasks()).thenReturn(false); ArrayList comments = new ArrayList<>(); - comments.add(comment); + comments.add(comment1); + comments.add(comment2); + comments.add(comment3); when(diffReport.getComments()).thenReturn(comments); - + stashCommentsReport1 = mock(StashCommentReport.class); when(stashCommentsReport1.getComments()).thenReturn(comments); when(stashCommentsReport1.applyDiffReport(diffReport)).thenReturn(stashCommentsReport1); @@ -123,11 +175,9 @@ public void setUp() throws Exception { when(stashCommentsReport1.getComments()).thenReturn(comments); when(stashCommentsReport2.applyDiffReport(diffReport)).thenReturn(stashCommentsReport2); when(stashClient.getPullRequestComments(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, FILE_PATH_2)).thenReturn(stashCommentsReport2); - - doNothing().when(stashClient).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage1, FILE_PATH_1, 1, STASH_DIFF_TYPE); - doNothing().when(stashClient).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage2, FILE_PATH_1, 2, STASH_DIFF_TYPE); - doNothing().when(stashClient).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage3, FILE_PATH_2, 1, STASH_DIFF_TYPE); + doNothing().when(stashClient).deletePullRequestComment(Mockito.eq(STASH_PROJECT), Mockito.eq(STASH_REPOSITORY), Mockito.eq(STASH_PULLREQUEST_ID), (StashComment) Mockito.anyObject()); + doNothing().when(stashClient).deleteTaskOnComment((StashTask) Mockito.anyObject()); } @Test @@ -230,11 +280,71 @@ public void testPostCommentPerIssueWithNoStashCommentAlreadyPushed() throws Exce when(stashCommentsReport2.contains(stashCommentMessage3, FILE_PATH_2, 1)).thenReturn(true); myFacade.postCommentPerIssue(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, SONARQUBE_URL, issueReport, diffReport, stashClient); - + verify(stashClient, times(0)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage1, FILE_PATH_1, 1, STASH_DIFF_TYPE); verify(stashClient, times(0)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage2, FILE_PATH_1, 2, STASH_DIFF_TYPE); verify(stashClient, times(0)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage3, FILE_PATH_2, 1, STASH_DIFF_TYPE); } + + @Test + public void testPostTaskOnComment() throws Exception { + when(config.getTaskIssueSeverityThreshold()).thenReturn(Severity.INFO); + + myFacade.postCommentPerIssue(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, SONARQUBE_URL, issueReport, diffReport, stashClient); + + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage1, FILE_PATH_1, 1, STASH_DIFF_TYPE); + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage2, FILE_PATH_1, 2, STASH_DIFF_TYPE); + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage3, FILE_PATH_2, 1, STASH_DIFF_TYPE); + + verify(stashClient, times(1)).postTaskOnComment("message3", (long) 3333); + verify(stashClient, times(1)).postTaskOnComment("message2", (long) 2222); + verify(stashClient, times(1)).postTaskOnComment("message1", (long) 1111); + } + + @Test + public void testPostTaskOnCommentWithRestrictedLevel() throws Exception { + when(config.getTaskIssueSeverityThreshold()).thenReturn(Severity.MAJOR); + + myFacade.postCommentPerIssue(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, SONARQUBE_URL, issueReport, diffReport, stashClient); + + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage1, FILE_PATH_1, 1, STASH_DIFF_TYPE); + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage2, FILE_PATH_1, 2, STASH_DIFF_TYPE); + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage3, FILE_PATH_2, 1, STASH_DIFF_TYPE); + + verify(stashClient, times(0)).postTaskOnComment("message3", (long) 3333); + verify(stashClient, times(1)).postTaskOnComment("message2", (long) 2222); + verify(stashClient, times(1)).postTaskOnComment("message1", (long) 1111); + } + + @Test + public void testPostTaskOnCommentWithNotPresentLevel() throws Exception { + when(config.getTaskIssueSeverityThreshold()).thenReturn(Severity.BLOCKER); + + myFacade.postCommentPerIssue(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, SONARQUBE_URL, issueReport, diffReport, stashClient); + + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage1, FILE_PATH_1, 1, STASH_DIFF_TYPE); + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage2, FILE_PATH_1, 2, STASH_DIFF_TYPE); + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage3, FILE_PATH_2, 1, STASH_DIFF_TYPE); + + verify(stashClient, times(0)).postTaskOnComment("message3", (long) 3333); + verify(stashClient, times(0)).postTaskOnComment("message2", (long) 2222); + verify(stashClient, times(0)).postTaskOnComment("message1", (long) 1111); + } + + @Test + public void testPostTaskOnCommentWithSeverityNone() throws Exception { + when(config.getTaskIssueSeverityThreshold()).thenReturn(StashPlugin.SEVERITY_NONE); + + myFacade.postCommentPerIssue(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, SONARQUBE_URL, issueReport, diffReport, stashClient); + + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage1, FILE_PATH_1, 1, STASH_DIFF_TYPE); + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage2, FILE_PATH_1, 2, STASH_DIFF_TYPE); + verify(stashClient, times(1)).postCommentLineOnPullRequest(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, stashCommentMessage3, FILE_PATH_2, 1, STASH_DIFF_TYPE); + + verify(stashClient, times(0)).postTaskOnComment("message3", (long) 3333); + verify(stashClient, times(0)).postTaskOnComment("message2", (long) 2222); + verify(stashClient, times(0)).postTaskOnComment("message1", (long) 1111); + } @Test public void testPostCommentPerIssueWithNoType() throws Exception{ @@ -323,7 +433,28 @@ public void testGetPullRequestDiffReportWithException() throws Exception { public void testResetComments() throws Exception { myFacade.resetComments(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, diffReport, stashUser, stashClient); - verify(stashClient, times(1)).deletePullRequestComment(Mockito.eq(STASH_PROJECT), Mockito.eq(STASH_REPOSITORY), Mockito.eq(STASH_PULLREQUEST_ID), (StashComment) Mockito.anyObject()); + verify(stashClient, times(3)).deleteTaskOnComment((StashTask) Mockito.anyObject()); + verify(stashClient, times(3)).deletePullRequestComment(Mockito.eq(STASH_PROJECT), Mockito.eq(STASH_REPOSITORY), Mockito.eq(STASH_PULLREQUEST_ID), (StashComment) Mockito.anyObject()); + } + + @Test + public void testResetCommentsWithNotDeletableTasks() throws Exception { + when(comment1.containsPermanentTasks()).thenReturn(true); + + myFacade.resetComments(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, diffReport, stashUser, stashClient); + + verify(stashClient, times(2)).deleteTaskOnComment((StashTask) Mockito.anyObject()); + verify(stashClient, times(2)).deletePullRequestComment(Mockito.eq(STASH_PROJECT), Mockito.eq(STASH_REPOSITORY), Mockito.eq(STASH_PULLREQUEST_ID), (StashComment) Mockito.anyObject()); + } + + @Test + public void testResetCommentsWithNoTasks() throws Exception { + when(comment1.getTasks()).thenReturn(new ArrayList()); + + myFacade.resetComments(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, diffReport, stashUser, stashClient); + + verify(stashClient, times(2)).deleteTaskOnComment((StashTask) Mockito.anyObject()); + verify(stashClient, times(3)).deletePullRequestComment(Mockito.eq(STASH_PROJECT), Mockito.eq(STASH_REPOSITORY), Mockito.eq(STASH_PULLREQUEST_ID), (StashComment) Mockito.anyObject()); } @Test @@ -400,4 +531,39 @@ public void testAddPullRequestReviewerWithReviewerAlreadyAdded() throws Exceptio verify(stashClient, times(0)).addPullRequestReviewer(STASH_PROJECT, STASH_REPOSITORY, STASH_PULLREQUEST_ID, (long) 1, reviewers); } + + @Test + public void testGetReportedSeverities() { + when(config.getTaskIssueSeverityThreshold()).thenReturn(Severity.INFO); + + List severities = myFacade.getReportedSeverities(); + + assertEquals(5, severities.size()); + assertEquals(StashPlugin.SEVERITY_LIST.get(0), severities.get(0)); + assertEquals(StashPlugin.SEVERITY_LIST.get(1), severities.get(1)); + assertEquals(StashPlugin.SEVERITY_LIST.get(2), severities.get(2)); + assertEquals(StashPlugin.SEVERITY_LIST.get(3), severities.get(3)); + assertEquals(StashPlugin.SEVERITY_LIST.get(4), severities.get(4)); + } + + @Test + public void testGetReportedSeveritiesWithRestriction() { + when(config.getTaskIssueSeverityThreshold()).thenReturn(Severity.MAJOR); + + List severities = myFacade.getReportedSeverities(); + + assertEquals(3, severities.size()); + assertEquals(StashPlugin.SEVERITY_LIST.get(2), severities.get(0)); + assertEquals(StashPlugin.SEVERITY_LIST.get(3), severities.get(1)); + assertEquals(StashPlugin.SEVERITY_LIST.get(4), severities.get(2)); + } + + @Test + public void testGetReportedSeveritiesWithNoSeverity() { + when(config.getTaskIssueSeverityThreshold()).thenReturn(StashPlugin.SEVERITY_NONE); + + List severities = myFacade.getReportedSeverities(); + + assertEquals(0, severities.size()); + } } diff --git a/src/test/java/org/sonar/plugins/stash/client/StashClientTest.java b/src/test/java/org/sonar/plugins/stash/client/StashClientTest.java index 487b41a1..e78fa571 100755 --- a/src/test/java/org/sonar/plugins/stash/client/StashClientTest.java +++ b/src/test/java/org/sonar/plugins/stash/client/StashClientTest.java @@ -26,8 +26,9 @@ import org.sonar.plugins.stash.exceptions.StashClientException; import org.sonar.plugins.stash.issue.StashComment; import org.sonar.plugins.stash.issue.StashCommentReport; -import org.sonar.plugins.stash.issue.StashDiffReport; -import org.sonar.plugins.stash.issue.StashPullRequest; +import org.sonar.plugins.stash.issue.StashDiffReport; +import org.sonar.plugins.stash.issue.StashPullRequest; +import org.sonar.plugins.stash.issue.StashTask; import org.sonar.plugins.stash.issue.StashUser; import org.sonar.plugins.stash.issue.collector.DiffReportSample; @@ -246,9 +247,15 @@ public void testGetPullRequestDiffsWithException() throws Exception { @Test public void testPostCommentLineOnPullRequest() throws Exception { + String stashJsonComment = "{\"id\":1234, \"text\":\"message\", \"anchor\": {\"path\":\"path\", \"line\":5}," + + "\"author\": {\"id\":1, \"name\":\"SonarQube\", \"slug\":\"sonarqube\", \"email\":\"sq@email.com\"}, \"version\": 0}"; + when(response.getStatusCode()).thenReturn(HttpURLConnection.HTTP_CREATED); + when(response.getResponseBody()).thenReturn(stashJsonComment); + + StashComment comment = spyClient.postCommentLineOnPullRequest("Project", "Repository", "1", "message", "path", 5, "type"); - spyClient.postCommentLineOnPullRequest("Project", "Repository", "1", "message", "path", 5, "type"); + assertEquals((long) 1234, comment.getId()); verify(requestBuilder, times(1)).execute(); verify(httpClient, times(1)).close(); } @@ -581,5 +588,98 @@ public void testAddPullRequestReviewerWithException() throws Exception { verify(response, times(0)).getStatusText(); verify(httpClient, times(1)).close(); } - } + } + + @Test + public void testPostTaskOnComment() throws Exception { + when(response.getStatusCode()).thenReturn(HttpURLConnection.HTTP_CREATED); + + spyClient.postTaskOnComment("message", (long) 1111); + + verify(requestBuilder, times(1)).execute(); + verify(httpClient, times(1)).close(); + } + + @Test + public void testPostTaskOnCommentWithWrongHTTPResult() throws Exception { + when(response.getStatusCode()).thenReturn(HttpURLConnection.HTTP_FORBIDDEN); + + try { + spyClient.postTaskOnComment("message", (long) 1111); + + assertFalse("Wrong HTTP result should raised StashClientException", true); + + } catch (StashClientException e) { + verify(response, times(1)).getStatusText(); + verify(httpClient, times(1)).close(); + } + } + + @Test + public void testPostTaskOnCommentWithException() throws Exception { + when(response.getStatusCode()).thenReturn(HttpURLConnection.HTTP_CREATED); + when(listenableFurture.get(anyLong(), eq(TimeUnit.MILLISECONDS))).thenThrow(new TimeoutException("TimeoutException for Test")); + + try { + spyClient.postTaskOnComment("message", (long) 1111); + + assertFalse("Exception failure should be catched and convert to StashClientException", true); + + } catch (StashClientException e) { + verify(response, times(0)).getStatusText(); + verify(httpClient, times(1)).close(); + } + } + + @Test + public void testDeleteTaskOnComment() throws Exception { + StashTask task = mock(StashTask.class); + when(task.getId()).thenReturn((long) 1111); + + when(response.getStatusCode()).thenReturn(HttpURLConnection.HTTP_NO_CONTENT); + + spyClient.deleteTaskOnComment(task); + + verify(requestBuilder, times(1)).execute(); + verify(httpClient, times(1)).close(); + } + + @Test + public void testDeleteTaskOnCommentWithWrongHTTPResult() throws Exception { + StashTask task = mock(StashTask.class); + when(task.getId()).thenReturn((long) 1111); + + when(response.getStatusCode()).thenReturn(HttpURLConnection.HTTP_FORBIDDEN); + + try { + spyClient.deleteTaskOnComment(task); + + assertFalse("Wrong HTTP result should raised StashClientException", true); + + } catch (StashClientException e) { + verify(response, times(1)).getStatusText(); + verify(httpClient, times(1)).close(); + } + } + + @Test + public void testDeleteTaskOnCommentWithException() throws Exception { + StashTask task = mock(StashTask.class); + when(task.getId()).thenReturn((long) 1111); + + when(response.getStatusCode()).thenReturn(HttpURLConnection.HTTP_CREATED); + when(listenableFurture.get(anyLong(), eq(TimeUnit.MILLISECONDS))).thenThrow(new TimeoutException("TimeoutException for Test")); + + try { + spyClient.deleteTaskOnComment(task); + + assertFalse("Exception failure should be catched and convert to StashClientException", true); + + } catch (StashClientException e) { + verify(response, times(0)).getStatusText(); + verify(httpClient, times(1)).close(); + } + } + + } diff --git a/src/test/java/org/sonar/plugins/stash/issue/StashCommentTest.java b/src/test/java/org/sonar/plugins/stash/issue/StashCommentTest.java index 0de12533..bb6feabf 100755 --- a/src/test/java/org/sonar/plugins/stash/issue/StashCommentTest.java +++ b/src/test/java/org/sonar/plugins/stash/issue/StashCommentTest.java @@ -4,6 +4,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import org.junit.Test; import org.mockito.Mock; @@ -35,4 +36,53 @@ public void testEquals(){ StashComment comment3 = new StashComment(1, "message", "path", (long) 1, stashUser, 0); assertTrue(comment1.equals(comment3)); } + + @Test + public void testAddTask() { + StashTask task1 = mock(StashTask.class); + when(task1.getId()).thenReturn((long) 1111); + + StashTask task2 = mock(StashTask.class); + when(task2.getId()).thenReturn((long) 2222); + + StashComment comment = new StashComment(1, "message", "path", (long) 1, stashUser, 0); + assertEquals(0, comment.getTasks().size()); + + comment.addTask(task1); + assertEquals(1, comment.getTasks().size()); + assertTrue(comment.getTasks().get(0).getId() == 1111); + + comment.addTask(task2); + assertEquals(2, comment.getTasks().size()); + assertTrue(comment.getTasks().get(0).getId() == 1111); + assertTrue(comment.getTasks().get(1).getId() == 2222); + } + + @Test + public void testContainsNotDeletableTasks() { + StashComment comment = new StashComment(1, "message", "path", (long) 1, stashUser, 0); + + StashTask task1 = mock(StashTask.class); + when(task1.isDeletable()).thenReturn(true); + comment.addTask(task1); + + StashTask task2 = mock(StashTask.class); + when(task2.isDeletable()).thenReturn(true); + comment.addTask(task2); + + assertFalse(comment.containsPermanentTasks()); + + StashTask task3 = mock(StashTask.class); + when(task3.isDeletable()).thenReturn(false); + comment.addTask(task3); + + assertTrue(comment.containsPermanentTasks()); + } + + @Test + public void testContainsNotDeletableTasksWithoutTasks() { + StashComment comment = new StashComment(1, "message", "path", (long) 1, stashUser, 0); + assertFalse(comment.containsPermanentTasks()); + } + } diff --git a/src/test/java/org/sonar/plugins/stash/issue/StashTaskTest.java b/src/test/java/org/sonar/plugins/stash/issue/StashTaskTest.java new file mode 100755 index 00000000..8f480b76 --- /dev/null +++ b/src/test/java/org/sonar/plugins/stash/issue/StashTaskTest.java @@ -0,0 +1,38 @@ +package org.sonar.plugins.stash.issue; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + +public class StashTaskTest { + + StashTask myTask; + + @Before + public void setUp(){ + myTask = new StashTask((long) 1111, "Text", "State", true); + } + + @Test + public void testGetId() { + assertEquals(1111, (long) myTask.getId()); + } + + @Test + public void testGetState() { + assertEquals("State", myTask.getState()); + } + + @Test + public void testGetText() { + assertEquals("Text", myTask.getText()); + } + + @Test + public void testIsDeletable() { + assertTrue(myTask.isDeletable()); + } +} diff --git a/src/test/java/org/sonar/plugins/stash/issue/collector/DiffReportSample.java b/src/test/java/org/sonar/plugins/stash/issue/collector/DiffReportSample.java index 6869a050..8fb4399e 100755 --- a/src/test/java/org/sonar/plugins/stash/issue/collector/DiffReportSample.java +++ b/src/test/java/org/sonar/plugins/stash/issue/collector/DiffReportSample.java @@ -99,6 +99,23 @@ public class DiffReportSample { + " \"slug\": \"sonarqube\"," + " \"email\": \"sq@email.com\"," + " }," + + " \"tasks\": [" + + " {" + + " \"id\": 12345," + + " \"text\": \"Complete the task associated to this TODO comment.\"," + + " \"state\": \"OPENED\"," + + " \"permittedOperations\": {" + + " \"deletable\": true" + + " }" + + " }," + + " {" + + " \"id\": 54321," + + " \"text\": \"Complete the task associated to this TODO comment.\"," + + " \"state\": \"OPENED\"," + + " \"permittedOperations\": {" + + " \"deletable\": true" + + " }" + + " }]" + " }," + " {" + " \"id\": 54321," diff --git a/src/test/java/org/sonar/plugins/stash/issue/collector/StashCollectorTest.java b/src/test/java/org/sonar/plugins/stash/issue/collector/StashCollectorTest.java index c0a6890b..7b7005e8 100755 --- a/src/test/java/org/sonar/plugins/stash/issue/collector/StashCollectorTest.java +++ b/src/test/java/org/sonar/plugins/stash/issue/collector/StashCollectorTest.java @@ -5,11 +5,13 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; +import org.sonar.plugins.stash.exceptions.StashReportExtractionException; import org.sonar.plugins.stash.issue.StashComment; import org.sonar.plugins.stash.issue.StashCommentReport; import org.sonar.plugins.stash.issue.StashDiff; -import org.sonar.plugins.stash.issue.StashDiffReport; -import org.sonar.plugins.stash.issue.StashPullRequest; +import org.sonar.plugins.stash.issue.StashDiffReport; +import org.sonar.plugins.stash.issue.StashPullRequest; +import org.sonar.plugins.stash.issue.StashTask; import org.sonar.plugins.stash.issue.StashUser; public class StashCollectorTest { @@ -17,7 +19,7 @@ public class StashCollectorTest { private static final long STASH_USER_ID = 1; @Test - public void testExtractSimpleComment() throws Exception { + public void testExtractCommentReport() throws Exception { String commentString = "{\"values\": [{\"id\":1234, \"text\":\"message\", \"anchor\": {\"path\":\"path\", \"line\":5}," + "\"author\": {\"id\":1, \"name\":\"SonarQube\", \"slug\":\"sonarqube\", \"email\":\"sq@email.com\"}, \"version\":0}]}"; StashCommentReport commentReport = StashCollector.extractComments(commentString); @@ -34,7 +36,7 @@ public void testExtractSimpleComment() throws Exception { } @Test - public void testExtractCommentList() throws Exception { + public void testExtractCommentReportWithSeveralComment() throws Exception { String commentString = "{\"values\": [" + "{\"id\":1234, \"text\":\"message1\", \"anchor\": {\"path\":\"path1\", \"line\":1}," + "\"author\": {\"id\":1, \"name\":\"SonarQube\", \"slug\":\"sonarqube\", \"email\":\"sq@email.com\"}, \"version\":1}, " @@ -63,13 +65,58 @@ public void testExtractCommentList() throws Exception { } @Test - public void testExtractEmptyComment() throws Exception { + public void testExtractEmptyCommentReport() throws Exception { String commentString = "{\"values\": []}"; StashCommentReport commentReport = StashCollector.extractComments(commentString); assertEquals(commentReport.size(), 0); } + @Test + public void testExtractComment() throws Exception { + String commentString = "{\"id\":1234, \"text\":\"message\", \"anchor\": {\"path\":\"path\", \"line\":5}," + + "\"author\": {\"id\":1, \"name\":\"SonarQube\", \"slug\":\"sonarqube\", \"email\":\"sq@email.com\"}, \"version\":0}"; + + StashComment comment = StashCollector.extractComment(commentString); + + assertEquals(comment.getId(), 1234); + assertEquals(comment.getMessage(), "message"); + assertEquals(comment.getPath(), "path"); + assertEquals(comment.getVersion(), 0); + assertEquals(comment.getAuthor().getId(), STASH_USER_ID); + assertEquals(comment.getLine(), 5); + } + + @Test + public void testExtractEmptyCommentWithNoAnchor() { + String commentString = "{\"id\":1234, \"text\":\"message\", " + + "\"author\": {\"id\":1, \"name\":\"SonarQube\", \"slug\":\"sonarqube\", \"email\":\"sq@email.com\"}, \"version\":0}"; + + try { + StashCollector.extractComment(commentString); + + assertFalse("No anchor tag: extraction should raised StashReportExtractionException exception", true); + + } catch (StashReportExtractionException e) { + assertTrue("No anchor tag: extraction has raised StashReportExtractionException exception as expected", true); + } + } + + @Test + public void testExtractCommentWithPathAndLineAsParameters() throws Exception { + String commentString = "{\"id\":1234, \"text\":\"message\", \"anchor\": {\"path\":\"path\", \"line\":5}," + + "\"author\": {\"id\":1, \"name\":\"SonarQube\", \"slug\":\"sonarqube\", \"email\":\"sq@email.com\"}, \"version\":0}"; + + StashComment comment = StashCollector.extractComment(commentString, "pathAsParameter", (long) 1111); + + assertEquals(comment.getId(), 1234); + assertEquals(comment.getMessage(), "message"); + assertEquals(comment.getPath(), "pathAsParameter"); + assertEquals(comment.getVersion(), 0); + assertEquals(comment.getAuthor().getId(), STASH_USER_ID); + assertEquals(comment.getLine(), 1111); + } + @Test public void testIsLastPage() throws Exception { String jsonBody = "{\"isLastPage\": true}"; @@ -110,6 +157,16 @@ public void testExtractDiffsWithBaseReport() throws Exception { assertEquals(comment1.getMessage(), "Test comment"); assertEquals(comment1.getVersion(), 1); + StashTask task1 = comment1.getTasks().get(0); + assertEquals(12345, (long) task1.getId()); + assertEquals("Complete the task associated to this TODO comment.", task1.getText()); + assertEquals("OPENED", task1.getState()); + + StashTask task2 = comment1.getTasks().get(1); + assertEquals(54321, (long) task2.getId()); + assertEquals("Complete the task associated to this TODO comment.", task2.getText()); + assertEquals("OPENED", task2.getState()); + StashUser author1 = comment1.getAuthor(); assertEquals(author1.getId(), 12345); assertEquals(author1.getName(), "SonarQube"); @@ -470,4 +527,39 @@ public void testExtractUser() throws Exception { assertEquals(user.getSlug(), userSlug); assertEquals(user.getEmail(), userEmail); } + + @Test + public void testExtractTask() throws Exception { + long id = 1111; + String text = "Text"; + String state = "State"; + boolean deletable = true; + + String jsonTask = "{ \"id\":" + id + ", \"text\":\"" + text + "\", \"state\":\"" + state + "\"," + + "\"permittedOperations\": { \"deletable\":" + deletable + "}}"; + + StashTask task = StashCollector.extractTask(jsonTask); + + assertEquals(id, (long) task.getId()); + assertEquals(text, task.getText()); + assertEquals(state, task.getState()); + assertEquals(deletable, task.isDeletable()); + } + + @Test + public void testExtractTaskWithoutPermittedOperation() throws Exception { + long id = 1111; + String text = "Text"; + String state = "State"; + + String jsonTask = "{ \"id\":" + id + ", \"text\":\"" + text + "\", \"state\": \"" + state + "\"}"; + + StashTask task = StashCollector.extractTask(jsonTask); + + assertEquals(id, (long) task.getId()); + assertEquals(text, task.getText()); + assertEquals(state, task.getState()); + assertEquals(true, task.isDeletable()); + } + }