]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16597 Ensure issues pull action returns issues data only for requested branch
authorKlaudio Sinani <klaudio.sinani@sonarsource.com>
Tue, 5 Jul 2022 11:22:53 +0000 (13:22 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 8 Jul 2022 20:02:47 +0000 (20:02 +0000)
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueQueryParams.java
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueQueryParamsTest.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/PullAction.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/PullActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/pull/PullActionIssuesRetrieverTest.java

index bebf378b07c6c2844160fe3157390d59dcda402e..0ce8683422bcc13a3b669d185185ef3297fdf739 100644 (file)
@@ -25,29 +25,23 @@ import javax.annotation.Nullable;
 
 public class IssueQueryParams {
 
-  private final String projectUuid;
-  private final String branchName;
+  private final String branchUuid;
   private final List<String> languages;
   private final List<String> ruleRepositories;
   private final boolean resolvedOnly;
   private final Long changedSince;
 
-  public IssueQueryParams(String projectUuid, String branchName, @Nullable List<String> languages,
+  public IssueQueryParams(String branchUuid, @Nullable List<String> languages,
     @Nullable List<String> ruleRepositories, boolean resolvedOnly, @Nullable Long changedSince) {
-    this.projectUuid = projectUuid;
-    this.branchName = branchName;
+    this.branchUuid = branchUuid;
     this.languages = languages;
     this.ruleRepositories = ruleRepositories;
     this.resolvedOnly = resolvedOnly;
     this.changedSince = changedSince;
   }
 
-  public String getProjectUuid() {
-    return projectUuid;
-  }
-
-  public String getBranchName() {
-    return branchName;
+  public String getBranchUuid() {
+    return branchUuid;
   }
 
   @CheckForNull
index 0b0690724ed7a75da545a9a99618bdf4b4d29bad..7cd7edc76f6d4d6fa1b8762d6e5c482f63cbab0c 100644 (file)
       <include refid="selectByBranchColumns"/>
     , p.path as filePath
     from issues i
-        inner join project_branches b on i.project_uuid = b.project_uuid
         inner join rules r on r.uuid = i.rule_uuid
         inner join components p on p.uuid=i.component_uuid
     where
-      b.kee = #{queryParams.branchName}
-      AND i.project_uuid = #{queryParams.projectUuid}
+      i.project_uuid = #{queryParams.branchUuid}
      <if test="queryParams.changedSince != null">
       AND i.issue_update_date &gt;= #{queryParams.changedSince,jdbcType=BIGINT}
      </if>
         row_number() over(order by i.kee ASC) as row_number,
           <include refid="selectByBranchColumns"/>
       from issues i
-          inner join project_branches b on i.project_uuid = b.project_uuid
           inner join rules r on r.uuid = i.rule_uuid
       where
-        b.kee = #{queryParams.branchName}
-        AND i.project_uuid = #{queryParams.projectUuid}
+        i.project_uuid = #{queryParams.branchUuid}
           <if test="queryParams.changedSince != null">
         AND i.issue_update_date &gt;= #{queryParams.changedSince,jdbcType=BIGINT}
          </if>
           select
             <include refid="selectByBranchColumns"/>
           from issues i
-              inner join project_branches b on i.project_uuid = b.project_uuid
               inner join rules r on r.uuid = i.rule_uuid
           where
-            b.kee = #{queryParams.branchName}
-            AND i.project_uuid = #{queryParams.projectUuid}
+            i.project_uuid = #{queryParams.branchUuid}
               <if test="queryParams.changedSince != null">
             AND i.issue_update_date &gt;= #{queryParams.changedSince,jdbcType=BIGINT}
              </if>
   <select id="selectRecentlyClosedIssues" resultType="string">
     select i.kee
     from issues i
-        inner join project_branches b on i.project_uuid = b.project_uuid
         inner join rules r on r.uuid = i.rule_uuid
     where
-      i.project_uuid = #{queryParams.projectUuid}
+      i.project_uuid = #{queryParams.branchUuid}
       AND issue_update_date &gt;= #{queryParams.changedSince}
-      AND b.kee = #{queryParams.branchName}
         <if test="queryParams.ruleRepositories != null">
           AND r.plugin_name IN
           <foreach item="ruleRepository" index="index" collection="queryParams.ruleRepositories" open="(" separator="," close=")">
index dfc724861a9f42908ca9e5cf1c0c4ed8ccd10bb4..607df35387a94acd77e0fd821f6d28665ab11168 100644 (file)
@@ -28,7 +28,6 @@ import java.util.stream.IntStream;
 import java.util.stream.Stream;
 import org.junit.Rule;
 import org.junit.Test;
-import org.sonar.api.issue.Issue;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.System2;
@@ -46,7 +45,18 @@ import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
 import static org.apache.commons.lang.math.RandomUtils.nextInt;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.AssertionsForClassTypes.tuple;
 import static org.junit.Assert.assertFalse;
+import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
+import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX;
+import static org.sonar.api.issue.Issue.STATUSES;
+import static org.sonar.api.issue.Issue.STATUS_CLOSED;
+import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
+import static org.sonar.api.issue.Issue.STATUS_OPEN;
+import static org.sonar.api.issue.Issue.STATUS_REOPENED;
+import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
+import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
 import static org.sonar.db.component.ComponentTesting.newDirectory;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newModuleDto;
@@ -69,7 +79,7 @@ public class IssueDaoTest {
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
 
-  private IssueDao underTest = db.getDbClient().issueDao();
+  private final IssueDao underTest = db.getDbClient().issueDao();
 
   @Test
   public void selectByKeyOrFail() {
@@ -154,6 +164,58 @@ public class IssueDaoTest {
     assertThat(issues).containsOnly("I1", "I2");
   }
 
+  @Test
+  public void selectByBranch() {
+    long updatedAt = 1_340_000_000_000L;
+    long changedSince = 1_000_000_000_000L;
+
+    ComponentDto project = db.components().insertPrivateProject();
+    RuleDto rule = db.rules().insert(r -> r.setRepositoryKey("java").setLanguage("java"));
+
+    ComponentDto branchA = db.components().insertProjectBranch(project, b -> b.setKey("branchA"));
+    ComponentDto fileA = db.components().insertComponent(newFileDto(branchA));
+
+    List<String> statusesA = List.of(STATUS_OPEN, STATUS_REVIEWED, STATUS_CLOSED, STATUS_RESOLVED);
+    IntStream.range(0, statusesA.size()).forEach(i -> insertBranchIssue(branchA, fileA, rule, "A" + i, statusesA.get(i), updatedAt));
+
+    ComponentDto branchB = db.components().insertProjectBranch(project, b -> b.setKey("branchB"));
+    ComponentDto fileB = db.components().insertComponent(newFileDto(branchB));
+
+    List<String> statusesB = List.of(STATUS_OPEN, STATUS_RESOLVED);
+    IntStream.range(0, statusesB.size()).forEach(i -> insertBranchIssue(branchB, fileB, rule, "B" + i, statusesB.get(i), updatedAt));
+
+    List<IssueDto> branchAIssuesA1 = underTest.selectByBranch(db.getSession(), buildSelectByBranchQuery(branchA, "java", false, changedSince), 1);
+
+    assertThat(branchAIssuesA1)
+      .extracting(IssueDto::getKey, IssueDto::getStatus)
+      .containsExactlyInAnyOrder(
+        tuple("issueA0", STATUS_OPEN),
+        tuple("issueA1", STATUS_REVIEWED),
+        tuple("issueA3", STATUS_RESOLVED)
+      );
+
+    List<IssueDto> branchAIssuesA2 = underTest.selectByBranch(db.getSession(), buildSelectByBranchQuery(branchA, "java", true, changedSince), 1);
+
+    assertThat(branchAIssuesA2)
+      .extracting(IssueDto::getKey, IssueDto::getStatus)
+      .containsExactly(tuple("issueA3", STATUS_RESOLVED));
+
+    List<IssueDto> branchBIssuesB1 = underTest.selectByBranch(db.getSession(), buildSelectByBranchQuery(branchB, "java", false, changedSince), 1);
+
+    assertThat(branchBIssuesB1)
+      .extracting(IssueDto::getKey, IssueDto::getStatus)
+      .containsExactlyInAnyOrder(
+        tuple("issueB0", STATUS_OPEN),
+        tuple("issueB1", STATUS_RESOLVED)
+      );
+
+    List<IssueDto> branchBIssuesB2 = underTest.selectByBranch(db.getSession(), buildSelectByBranchQuery(branchB, "java", true, changedSince), 1);
+
+    assertThat(branchBIssuesB2)
+      .extracting(IssueDto::getKey, IssueDto::getStatus)
+      .containsExactly(tuple("issueB1", STATUS_RESOLVED));
+  }
+
   @Test
   public void selectByComponentUuidPaginated() {
     // contains I1 and I2
@@ -234,12 +296,12 @@ public class IssueDaoTest {
 
     ComponentDto file = db.components().insertComponent(newFileDto(projectBranch));
 
-    IssueDto openIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_OPEN).setResolution(null));
-    IssueDto closedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED));
-    IssueDto reopenedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_REOPENED).setResolution(null));
-    IssueDto confirmedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_CONFIRMED).setResolution(null));
-    IssueDto wontfixIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX));
-    IssueDto fpIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE));
+    IssueDto openIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_OPEN).setResolution(null));
+    IssueDto closedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_CLOSED).setResolution(RESOLUTION_FIXED));
+    IssueDto reopenedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_REOPENED).setResolution(null));
+    IssueDto confirmedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_CONFIRMED).setResolution(null));
+    IssueDto wontfixIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX));
+    IssueDto fpIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_FALSE_POSITIVE));
 
     assertThat(underTest.selectOpenByComponentUuids(db.getSession(), Collections.singletonList(file.uuid())))
       .extracting("kee")
@@ -520,13 +582,13 @@ public class IssueDaoTest {
       .forEach(moduleOrDir -> {
         String projectUuid = moduleOrDir.projectUuid();
         // CLOSED issue => not returned
-        db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(moduleOrDir).setStatus(Issue.STATUS_CLOSED));
+        db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(moduleOrDir).setStatus(STATUS_CLOSED));
         assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
           .isEmpty();
 
         // status != CLOSED => returned
-        Issue.STATUSES.stream()
-          .filter(t -> !Issue.STATUS_CLOSED.equals(t))
+        STATUSES.stream()
+          .filter(t -> !STATUS_CLOSED.equals(t))
           .forEach(status -> {
             IssueDto issue = db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(moduleOrDir).setStatus(status));
             assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
@@ -543,7 +605,7 @@ public class IssueDaoTest {
     Stream.of(project1, file11, application, view, subview, project2, file21)
       .forEach(neitherModuleNorDir -> {
         String projectUuid = neitherModuleNorDir.projectUuid();
-        Issue.STATUSES
+        STATUSES
           .forEach(status -> {
             db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(neitherModuleNorDir).setStatus(status));
             assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
@@ -557,7 +619,7 @@ public class IssueDaoTest {
         String projectUuid = component.projectUuid();
 
         // issues for each status => returned if component is dir or module
-        Issue.STATUSES
+        STATUSES
           .forEach(status -> db.issues().insertIssue(t -> t.setProjectUuid(projectUuid).setComponent(component).setStatus(status)));
         if (allModuleOrDirs.contains(component)) {
           assertThat(underTest.selectModuleAndDirComponentUuidsOfOpenIssuesForProjectUuid(db.getSession(), projectUuid))
@@ -656,4 +718,12 @@ public class IssueDaoTest {
   private static RuleType randomRuleTypeExceptHotspot() {
     return RULE_TYPES_EXCEPT_HOTSPOT[nextInt(RULE_TYPES_EXCEPT_HOTSPOT.length)];
   }
+
+  private void insertBranchIssue(ComponentDto branch, ComponentDto file, RuleDto rule, String id, String status, Long updateAt) {
+    db.issues().insert(rule, branch, file, i -> i.setKee("issue" + id).setStatus(status).setUpdatedAt(updateAt).setType(randomRuleTypeExceptHotspot()));
+  }
+
+  private static IssueQueryParams buildSelectByBranchQuery(ComponentDto branch, String language, boolean resolvedOnly, Long changedSince) {
+    return new IssueQueryParams(branch.uuid(), List.of(language), List.of(language), resolvedOnly, changedSince);
+  }
 }
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueQueryParamsTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueQueryParamsTest.java
new file mode 100644 (file)
index 0000000..b23a2bb
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.db.issue;
+
+import java.util.List;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssueQueryParamsTest {
+  private final List<String> languages = List.of("java");
+  private final List<String> ruleRepositories = List.of("js-security", "java");
+
+  @Test
+  public void validate_issue_query_parameters_structure() {
+    boolean resolvedOnly = false;
+    long changedSince = 1_000_000L;
+    String branchUuid = "master-branch-uuid";
+
+    IssueQueryParams queryParameters = new IssueQueryParams(branchUuid, languages, ruleRepositories, resolvedOnly, changedSince);
+
+    assertThat(queryParameters.getBranchUuid()).isEqualTo(branchUuid);
+    assertThat(queryParameters.getLanguages()).isEqualTo(languages);
+    assertThat(queryParameters.getRuleRepositories()).isEqualTo(ruleRepositories);
+    assertThat(queryParameters.isResolvedOnly()).isFalse();
+    assertThat(queryParameters.getChangedSince()).isEqualTo(changedSince);
+
+  }
+}
index e22016d35575bee622ada7b6c029d6b19059a8b3..24813f197e3b9ca3d9eda7bab9fee3b4a691548a 100644 (file)
@@ -32,14 +32,16 @@ import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.issue.IssueQueryParams;
 import org.sonar.db.project.ProjectDto;
+import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.issue.ws.pull.PullActionIssuesRetriever;
 import org.sonar.server.issue.ws.pull.PullActionResponseWriter;
 import org.sonar.server.user.UserSession;
 
-import static java.util.Optional.*;
+import static java.util.Optional.ofNullable;
 import static org.sonar.api.web.UserRole.USER;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_PULL;
 
@@ -55,11 +57,13 @@ public class PullAction implements IssuesWsAction {
   private final DbClient dbClient;
   private final UserSession userSession;
   private final PullActionResponseWriter pullActionResponseWriter;
+  private final ComponentFinder componentFinder;
 
-  public PullAction(DbClient dbClient, UserSession userSession, PullActionResponseWriter pullActionResponseWriter) {
+  public PullAction(DbClient dbClient, UserSession userSession, PullActionResponseWriter pullActionResponseWriter, ComponentFinder componentFinder) {
     this.dbClient = dbClient;
     this.userSession = userSession;
     this.pullActionResponseWriter = pullActionResponseWriter;
+    this.componentFinder = componentFinder;
   }
 
   @Override
@@ -120,28 +124,21 @@ public class PullAction implements IssuesWsAction {
     throws IOException {
 
     try (DbSession dbSession = dbClient.openSession(false)) {
-      Optional<ProjectDto> projectDto = dbClient.projectDao().selectProjectByKey(dbSession, projectKey);
-      validateProjectPermissions(projectDto);
+      ProjectDto projectDto = componentFinder.getProjectByKey(dbSession, projectKey);
+      userSession.checkProjectPermission(USER, projectDto);
+      BranchDto branchDto = componentFinder.getBranchOrPullRequest(dbSession, projectDto, branchName, null);
       pullActionResponseWriter.appendTimestampToResponse(outputStream);
-      var pullActionQueryParams = new IssueQueryParams(projectDto.get().getUuid(), branchName,
-        languages, ruleRepositories, resolvedOnly, changedSince);
-      retrieveAndSendIssues(dbSession, projectDto.get().getUuid(), pullActionQueryParams, outputStream);
+      IssueQueryParams pullActionQueryParams = new IssueQueryParams(branchDto.getUuid(), languages, ruleRepositories, resolvedOnly, changedSince);
+      retrieveAndSendIssues(dbSession, pullActionQueryParams, outputStream);
     }
   }
 
-  private void validateProjectPermissions(Optional<ProjectDto> projectDto) {
-    if (projectDto.isEmpty()) {
-      throw new IllegalArgumentException("Invalid " + PROJECT_KEY_PARAM + " parameter");
-    }
-    userSession.checkProjectPermission(USER, projectDto.get());
-  }
-
-  private void retrieveAndSendIssues(DbSession dbSession, String componentUuid, IssueQueryParams queryParams, OutputStream outputStream)
+  private void retrieveAndSendIssues(DbSession dbSession, IssueQueryParams queryParams, OutputStream outputStream)
     throws IOException {
 
     var issuesRetriever = new PullActionIssuesRetriever(dbClient, queryParams);
 
-    Set<String> issueKeysSnapshot = new HashSet<>(getIssueKeysSnapshot(componentUuid, queryParams.getChangedSince()));
+    Set<String> issueKeysSnapshot = new HashSet<>(getIssueKeysSnapshot(queryParams.getBranchUuid(), queryParams.getChangedSince()));
     Consumer<List<IssueDto>> listConsumer = issueDtos -> pullActionResponseWriter.appendIssuesToResponse(issueDtos, outputStream);
     issuesRetriever.processIssuesByBatch(dbSession, issueKeysSnapshot, listConsumer);
 
index f9da48c1680a3eb36ba9ff84213b90b4697c6945..0f12633ce0d57484de785f60755add716c85c8b2 100644 (file)
@@ -27,17 +27,21 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.issue.Issue;
+import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDbTester;
 import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ResourceTypesRule;
 import org.sonar.db.issue.IssueDbTester;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.protobuf.DbCommons;
 import org.sonar.db.protobuf.DbIssues;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.db.user.UserDto;
+import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.issue.ws.pull.PullActionProtobufObjectGenerator;
 import org.sonar.server.issue.ws.pull.PullActionResponseWriter;
 import org.sonar.server.tester.UserSessionRule;
@@ -47,6 +51,7 @@ import org.sonar.server.ws.WsActionTester;
 import org.sonarqube.ws.Common;
 import org.sonarqube.ws.Issues;
 
+import static java.lang.String.format;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.Mockito.mock;
@@ -74,11 +79,13 @@ public class PullActionTest {
   private final PullActionProtobufObjectGenerator pullActionProtobufObjectGenerator = new PullActionProtobufObjectGenerator();
 
   private final PullActionResponseWriter pullActionResponseWriter = new PullActionResponseWriter(system2, pullActionProtobufObjectGenerator);
+  private final ResourceTypesRule resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
+  private final ComponentFinder componentFinder = new ComponentFinder(db.getDbClient(), resourceTypes);
 
   private final IssueDbTester issueDbTester = new IssueDbTester(db);
   private final ComponentDbTester componentDbTester = new ComponentDbTester(db);
 
-  private final PullAction underTest = new PullAction(db.getDbClient(), userSession, pullActionResponseWriter);
+  private final PullAction underTest = new PullAction(db.getDbClient(), userSession, pullActionResponseWriter, componentFinder);
   private final WsActionTester tester = new WsActionTester(underTest);
 
   private RuleDto correctRule, incorrectRule;
@@ -112,8 +119,8 @@ public class PullActionTest {
       .setParam("branchName", DEFAULT_BRANCH);
 
     assertThatThrownBy(request::execute)
-      .isInstanceOf(IllegalArgumentException.class)
-      .hasMessage("Invalid projectKey parameter");
+      .isInstanceOf(NotFoundException.class)
+      .hasMessage("Project 'projectKey' not found");
   }
 
   @Test
@@ -129,6 +136,37 @@ public class PullActionTest {
       .hasMessage("Insufficient privileges");
   }
 
+  @Test
+  public void givenNotExistingBranchKey_throwException() {
+    DbCommons.TextRange textRange = DbCommons.TextRange.newBuilder()
+      .setStartLine(1)
+      .setEndLine(2)
+      .setStartOffset(3)
+      .setEndOffset(4)
+      .build();
+    DbIssues.Locations.Builder mainLocation = DbIssues.Locations.newBuilder()
+      .setChecksum("hash")
+      .setTextRange(textRange);
+
+    RuleDto rule = db.rules().insertIssueRule(r -> r.setRepositoryKey("java").setRuleKey("S1000"));
+    IssueDto issueDto = issueDbTester.insertIssue(rule, p -> p.setSeverity("MINOR")
+      .setManualSeverity(true)
+      .setMessage("message")
+      .setCreatedAt(NOW)
+      .setStatus(Issue.STATUS_RESOLVED)
+      .setLocations(mainLocation.build())
+      .setType(Common.RuleType.BUG.getNumber()));
+    loginWithBrowsePermission(issueDto);
+
+    TestRequest request = tester.newRequest()
+      .setParam("projectKey",  issueDto.getProjectKey())
+      .setParam("branchName", "non-existent-branch");
+
+    assertThatThrownBy(request::execute)
+      .isInstanceOf(NotFoundException.class)
+      .hasMessage(format("Branch 'non-existent-branch' in project '%s' not found", issueDto.getProjectKey()));
+  }
+
   @Test
   public void givenValidProjectKeyAndOneIssueOnBranch_returnOneIssue() throws IOException {
     DbCommons.TextRange textRange = DbCommons.TextRange.newBuilder()
index 460f4e572447e1fe4231c32946694f743cbe4dbd..30233c93ec65ce0c0cb7e97e40d74297cb6bedee 100644 (file)
@@ -41,14 +41,12 @@ import static org.mockito.Mockito.when;
 public class PullActionIssuesRetrieverTest {
 
   private final DbClient dbClient = mock(DbClient.class);
-  private final String projectUuid = "default-project-uuid";
-  private final String branchName = "master";
+  private final String branchUuid = "master-branch-uuid";
   private final List<String> languages = List.of("java");
   private final List<String> ruleRepositories = List.of("js-security", "java");
   private final Long defaultChangedSince = 1_000_000L;
 
-  private final IssueQueryParams queryParams = new IssueQueryParams(projectUuid, branchName, languages, ruleRepositories, false,
-    defaultChangedSince);
+  private final IssueQueryParams queryParams = new IssueQueryParams(branchUuid, languages, ruleRepositories, false, defaultChangedSince);
   private final IssueDao issueDao = mock(IssueDao.class);
 
   @Before