]> source.dussan.org Git - sonarqube.git/commitdiff
Improve coverage
authorJulien HENRY <julien.henry@sonarsource.com>
Fri, 20 Oct 2017 14:51:23 +0000 (16:51 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Fri, 20 Oct 2017 15:24:40 +0000 (01:24 +1000)
server/sonar-db-dao/src/main/java/org/sonar/core/issue/ShortBranchIssue.java [deleted file]
server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssue.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMerger.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMergerTest.java

diff --git a/server/sonar-db-dao/src/main/java/org/sonar/core/issue/ShortBranchIssue.java b/server/sonar-db-dao/src/main/java/org/sonar/core/issue/ShortBranchIssue.java
deleted file mode 100644 (file)
index e9058d6..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.core.issue;
-
-import java.util.Date;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.core.issue.tracking.Trackable;
-
-@Immutable
-public class ShortBranchIssue implements Trackable {
-  private final String key;
-  private final Integer line;
-  private final String message;
-  private final String lineHash;
-  private final RuleKey ruleKey;
-  private final String status;
-  private final String branchName;
-  private final Date creationDate;
-
-  public ShortBranchIssue(String key, @Nullable Integer line, String message, @Nullable String lineHash, RuleKey ruleKey, String status, String branchName, Date creationDate) {
-    this.key = key;
-    this.line = line;
-    this.message = message;
-    this.lineHash = lineHash;
-    this.ruleKey = ruleKey;
-    this.status = status;
-    this.branchName = branchName;
-    this.creationDate = creationDate;
-  }
-
-  public String getKey() {
-    return key;
-  }
-
-  @CheckForNull
-  @Override
-  public Integer getLine() {
-    return line;
-  }
-
-  @Override
-  public String getMessage() {
-    return message;
-  }
-
-  @CheckForNull
-  @Override
-  public String getLineHash() {
-    return lineHash;
-  }
-
-  @Override
-  public RuleKey getRuleKey() {
-    return ruleKey;
-  }
-
-  @Override
-  public String getStatus() {
-    return status;
-  }
-
-  public String getBranchName() {
-    return branchName;
-  }
-
-  @Override
-  public Date getCreationDate() {
-    return creationDate;
-  }
-
-  @Override
-  public int hashCode() {
-    return key.hashCode();
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (this == obj) {
-      return true;
-    }
-    if (obj == null) {
-      return false;
-    }
-    if (getClass() != obj.getClass()) {
-      return false;
-    }
-    ShortBranchIssue other = (ShortBranchIssue) obj;
-    return key.equals(other.key);
-  }
-
-}
index 79208ddd20da09badf8f59a956d93279e9786169..cf072a5d141a16fd3b773312991b415bda4f2729 100644 (file)
@@ -25,9 +25,6 @@ import javax.annotation.Nullable;
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.apache.commons.lang.builder.ToStringStyle;
 import org.sonar.api.rule.RuleKey;
-import org.sonar.core.issue.ShortBranchIssue;
-
-import static org.sonar.api.utils.DateUtils.longToDate;
 
 public final class ShortBranchIssueDto implements Serializable {
 
@@ -112,12 +109,18 @@ public final class ShortBranchIssueDto implements Serializable {
     return RuleKey.of(ruleRepo, ruleKey);
   }
 
+  public Long getIssueCreationDate() {
+    return issueCreationDate;
+  }
+
+  public ShortBranchIssueDto setIssueCreationDate(Long issueCreationDate) {
+    this.issueCreationDate = issueCreationDate;
+    return this;
+  }
+
   @Override
   public String toString() {
     return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
   }
 
-  public ShortBranchIssue toShortBranchIssue() {
-    return new ShortBranchIssue(getKey(), getLine(), getMessage(), getChecksum(), getRuleKey(), getStatus(), getBranchName(), longToDate(issueCreationDate));
-  }
 }
index cc31fa3d9c7a4af8b8c76f848fa5cec55a0a212a..cd0dc6c4fc1b5c59e6fe3a9863f0b6d3bec62844 100644 (file)
@@ -230,6 +230,8 @@ public class IssueDaoTest {
     assertThat(fp.getChecksum()).isNotEmpty();
     assertThat(fp.getRuleKey()).isNotNull();
     assertThat(fp.getStatus()).isNotNull();
+    assertThat(fp.getBranchName()).isEqualTo("feature/foo");
+    assertThat(fp.getIssueCreationDate()).isNotNull();
   }
 
   private static IssueDto newIssueDto(String key) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssue.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssue.java
new file mode 100644 (file)
index 0000000..79ec1e9
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.server.computation.task.projectanalysis.issue;
+
+import java.util.Date;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.core.issue.tracking.Trackable;
+
+@Immutable
+public class ShortBranchIssue implements Trackable {
+  private final String key;
+  private final Integer line;
+  private final String message;
+  private final String lineHash;
+  private final RuleKey ruleKey;
+  private final String status;
+  private final String branchName;
+  private final Date creationDate;
+
+  public ShortBranchIssue(String key, @Nullable Integer line, String message, @Nullable String lineHash, RuleKey ruleKey, String status, String branchName, Date creationDate) {
+    this.key = key;
+    this.line = line;
+    this.message = message;
+    this.lineHash = lineHash;
+    this.ruleKey = ruleKey;
+    this.status = status;
+    this.branchName = branchName;
+    this.creationDate = creationDate;
+  }
+
+  public String getKey() {
+    return key;
+  }
+
+  @CheckForNull
+  @Override
+  public Integer getLine() {
+    return line;
+  }
+
+  @Override
+  public String getMessage() {
+    return message;
+  }
+
+  @CheckForNull
+  @Override
+  public String getLineHash() {
+    return lineHash;
+  }
+
+  @Override
+  public RuleKey getRuleKey() {
+    return ruleKey;
+  }
+
+  @Override
+  public String getStatus() {
+    return status;
+  }
+
+  public String getBranchName() {
+    return branchName;
+  }
+
+  @Override
+  public Date getCreationDate() {
+    return creationDate;
+  }
+
+  @Override
+  public int hashCode() {
+    return key.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    ShortBranchIssue other = (ShortBranchIssue) obj;
+    return key.equals(other.key);
+  }
+
+}
index 41d55e8570417b0789e633309384dfaff79af7a7..bdbedd9b5b41366dfbec73165b94621ac1cac88a 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.server.computation.task.projectanalysis.issue;
 import java.util.Collection;
 import java.util.Map;
 import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.ShortBranchIssue;
 import org.sonar.core.issue.tracking.SimpleTracker;
 import org.sonar.core.issue.tracking.Tracking;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
index cf91a53b07cc5e5cd7c86b09161cb336ee7b7291..c12b610ef3edbad242cb0ddca138a278b59d9c04 100644 (file)
@@ -26,7 +26,6 @@ import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.ShortBranchIssue;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
@@ -38,6 +37,7 @@ import org.sonar.server.computation.task.projectanalysis.component.ShortBranchCo
 
 import static java.util.stream.Collectors.groupingBy;
 import static java.util.stream.Collectors.toMap;
+import static org.sonar.api.utils.DateUtils.longToDate;
 
 public class ShortBranchIssuesLoader {
 
@@ -58,11 +58,16 @@ public class ShortBranchIssuesLoader {
     try (DbSession session = dbClient.openSession(false)) {
       return dbClient.issueDao().selectOpenByComponentUuids(session, uuids)
         .stream()
-        .map(ShortBranchIssueDto::toShortBranchIssue)
+        .map(ShortBranchIssuesLoader::toShortBranchIssue)
         .collect(Collectors.toList());
     }
   }
 
+  private static ShortBranchIssue toShortBranchIssue(ShortBranchIssueDto dto) {
+    return new ShortBranchIssue(dto.getKey(), dto.getLine(), dto.getMessage(), dto.getChecksum(), dto.getRuleKey(), dto.getStatus(), dto.getBranchName(),
+      longToDate(dto.getIssueCreationDate()));
+  }
+
   public Map<ShortBranchIssue, DefaultIssue> loadDefaultIssuesWithChanges(Collection<ShortBranchIssue> lightIssues) {
     if (lightIssues.isEmpty()) {
       return Collections.emptyMap();
index c3b720e872cea877a59740d9324d1e699257fbcb..2cbb7a20af6443b831647d30adadad6568b01c67 100644 (file)
  */
 package org.sonar.server.computation.task.projectanalysis.issue;
 
-import com.google.common.collect.ImmutableMap;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import javax.annotation.Nullable;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -35,124 +33,194 @@ import org.mockito.MockitoAnnotations;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.ShortBranchIssue;
+import org.sonar.core.issue.FieldDiffs;
 import org.sonar.core.issue.tracking.SimpleTracker;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.issue.IssueDto;
+import org.sonar.db.issue.IssueTesting;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.server.computation.task.projectanalysis.component.ShortBranchComponentsWithIssues;
+import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
 
 public class ShortBranchIssueMergerTest {
-  @Mock
-  private ShortBranchIssuesLoader resolvedShortBranchIssuesLoader;
   @Mock
   private IssueLifecycle issueLifecycle;
-  @Mock
-  private Component component;
+
+  @Rule
+  public DbTester db = DbTester.create();
+
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule()
+    .setRoot(builder(org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT, PROJECT_REF).setKey(PROJECT_KEY).setUuid(PROJECT_UUID)
+      .addChildren(FILE_1)
+      .build());
+
+  private static final String PROJECT_KEY = "project";
+  private static final int PROJECT_REF = 1;
+  private static final String PROJECT_UUID = "projectUuid";
+  private static final int FILE_1_REF = 12341;
+  private static final String FILE_1_KEY = "fileKey";
+  private static final String FILE_1_UUID = "fileUuid";
+
+  private static final org.sonar.server.computation.task.projectanalysis.component.Component FILE_1 = builder(
+    org.sonar.server.computation.task.projectanalysis.component.Component.Type.FILE, FILE_1_REF)
+      .setKey(FILE_1_KEY)
+      .setUuid(FILE_1_UUID)
+      .build();
 
   private SimpleTracker<DefaultIssue, ShortBranchIssue> tracker = new SimpleTracker<>();
   private ShortBranchIssueMerger copier;
+  private ComponentDto fileOnBranch1Dto;
+  private ComponentDto fileOnBranch2Dto;
+  private ComponentDto fileOnBranch3Dto;
+  private ComponentDto projectDto;
+  private ComponentDto branch1Dto;
+  private ComponentDto branch2Dto;
+  private ComponentDto branch3Dto;
+  private RuleDefinitionDto rule;
 
   @Before
   public void setUp() {
     MockitoAnnotations.initMocks(this);
-    copier = new ShortBranchIssueMerger(resolvedShortBranchIssuesLoader, tracker, issueLifecycle);
+    copier = new ShortBranchIssueMerger(new ShortBranchIssuesLoader(new ShortBranchComponentsWithIssues(treeRootHolder, db.getDbClient()), db.getDbClient()), tracker,
+      issueLifecycle);
+    projectDto = db.components().insertMainBranch(p -> p.setDbKey(PROJECT_KEY).setUuid(PROJECT_UUID));
+    branch1Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch1")
+      .setBranchType(BranchType.SHORT)
+      .setMergeBranchUuid(projectDto.uuid()));
+    branch2Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch2")
+      .setBranchType(BranchType.SHORT)
+      .setMergeBranchUuid(projectDto.uuid()));
+    branch3Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch3")
+      .setBranchType(BranchType.SHORT)
+      .setMergeBranchUuid(projectDto.uuid()));
+    fileOnBranch1Dto = db.components().insertComponent(newFileDto(branch1Dto).setDbKey(FILE_1_KEY + ":BRANCH:myBranch1"));
+    fileOnBranch2Dto = db.components().insertComponent(newFileDto(branch2Dto).setDbKey(FILE_1_KEY + ":BRANCH:myBranch2"));
+    fileOnBranch3Dto = db.components().insertComponent(newFileDto(branch3Dto).setDbKey(FILE_1_KEY + ":BRANCH:myBranch3"));
+    rule = db.rules().insert();
   }
 
   @Test
   public void do_nothing_if_no_match() {
-    when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.emptyList());
-    DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null, new Date());
-    copier.tryMerge(component, Collections.singleton(i));
+    DefaultIssue i = createIssue("issue1", rule.getKey(), Issue.STATUS_CONFIRMED, null, new Date());
+    copier.tryMerge(FILE_1, Collections.singleton(i));
 
-    verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component);
     verifyZeroInteractions(issueLifecycle);
   }
 
   @Test
   public void do_nothing_if_no_new_issue() {
-    DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null, new Date());
-    when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(newShortBranchIssue(i, "myBranch")));
-    copier.tryMerge(component, Collections.emptyList());
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
+    copier.tryMerge(FILE_1, Collections.emptyList());
 
-    verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component);
     verifyZeroInteractions(issueLifecycle);
   }
 
   @Test
-  public void update_status_on_matches() {
-    DefaultIssue issue1 = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null, new Date());
-    ShortBranchIssue shortBranchIssue = newShortBranchIssue(issue1, "myBranch");
-    DefaultIssue newIssue = createIssue("issue2", "rule1", Issue.STATUS_OPEN, null, new Date());
-
-    when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(shortBranchIssue));
-    when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue, issue1));
-    copier.tryMerge(component, Collections.singleton(newIssue));
-    ArgumentCaptor<Collection> captor = ArgumentCaptor.forClass(Collection.class);
-    verify(resolvedShortBranchIssuesLoader).loadDefaultIssuesWithChanges(captor.capture());
-    assertThat(captor.getValue()).containsOnly(shortBranchIssue);
-    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(newIssue, issue1, "myBranch");
+  public void merge_confirmed_issues() {
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
+    DefaultIssue newIssue = createIssue("issue2", rule.getKey(), Issue.STATUS_OPEN, null, new Date());
+
+    copier.tryMerge(FILE_1, Collections.singleton(newIssue));
+
+    ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
+    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(eq(newIssue), issueToMerge.capture(), eq("myBranch1"));
+
+    assertThat(issueToMerge.getValue().key()).isEqualTo("issue1");
   }
 
   @Test
   public void prefer_resolved_issues() {
-    ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_REOPENED, null, new Date()), "myBranch1");
-    ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_CONFIRMED, null, new Date()), "myBranch2");
-    DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE, new Date());
-    ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3, "myBranch3");
-    DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null, new Date());
-
-    when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3));
-    when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3));
-    copier.tryMerge(component, Collections.singleton(newIssue));
-    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(newIssue, issue3, "myBranch3");
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_REOPENED).setLine(1).setChecksum("checksum"));
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch2Dto, fileOnBranch2Dto).setKee("issue2").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch3Dto, fileOnBranch3Dto).setKee("issue3").setStatus(Issue.STATUS_RESOLVED)
+      .setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setLine(1).setChecksum("checksum"));
+    DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, null, new Date());
+
+    copier.tryMerge(FILE_1, Collections.singleton(newIssue));
+
+    ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
+    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(eq(newIssue), issueToMerge.capture(), eq("myBranch3"));
+
+    assertThat(issueToMerge.getValue().key()).isEqualTo("issue3");
   }
 
   @Test
-  public void prefer_confirmed_issues() {
-    ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_REOPENED, null, new Date()), "myBranch1");
-    ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_OPEN, null, new Date()), "myBranch2");
-    DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_CONFIRMED, null, new Date());
-    ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3, "myBranch3");
-    DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null, new Date());
-
-    when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3));
-    when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3));
-    copier.tryMerge(component, Collections.singleton(newIssue));
-    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(newIssue, issue3, "myBranch3");
+  public void prefer_confirmed_issues_if_no_resolved() {
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_REOPENED).setLine(1).setChecksum("checksum"));
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch2Dto, fileOnBranch2Dto).setKee("issue2").setStatus(Issue.STATUS_OPEN).setLine(1).setChecksum("checksum"));
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch3Dto, fileOnBranch3Dto).setKee("issue3").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
+    DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, null, new Date());
+
+    copier.tryMerge(FILE_1, Collections.singleton(newIssue));
+
+    ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
+    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(eq(newIssue), issueToMerge.capture(), eq("myBranch3"));
+
+    assertThat(issueToMerge.getValue().key()).isEqualTo("issue3");
   }
 
   @Test
   public void prefer_older_issues() {
     Instant now = Instant.now();
-    ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_REOPENED, null, Date.from(now.plus(2, ChronoUnit.SECONDS))), "myBranch1");
-    ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_OPEN, null, Date.from(now.plus(1, ChronoUnit.SECONDS))), "myBranch2");
-    DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_OPEN, null, Date.from(now));
-    ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3, "myBranch3");
-    DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null, new Date());
-
-    when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3));
-    when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3));
-    copier.tryMerge(component, Collections.singleton(newIssue));
-    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(newIssue, issue3, "myBranch3");
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_REOPENED).setLine(1).setChecksum("checksum")
+      .setIssueCreationDate(Date.from(now.plus(2, ChronoUnit.SECONDS))));
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch2Dto, fileOnBranch2Dto).setKee("issue2").setStatus(Issue.STATUS_OPEN).setLine(1).setChecksum("checksum")
+      .setIssueCreationDate(Date.from(now.plus(1, ChronoUnit.SECONDS))));
+    db.issues().insertIssue(IssueTesting.newIssue(rule, branch3Dto, fileOnBranch3Dto).setKee("issue3").setStatus(Issue.STATUS_OPEN).setLine(1).setChecksum("checksum")
+      .setIssueCreationDate(Date.from(now)));
+    DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, null, new Date());
+
+    copier.tryMerge(FILE_1, Collections.singleton(newIssue));
+
+    ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
+    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(eq(newIssue), issueToMerge.capture(), eq("myBranch3"));
+
+    assertThat(issueToMerge.getValue().key()).isEqualTo("issue3");
   }
 
-  private static DefaultIssue createIssue(String key, String ruleKey, String status, @Nullable String resolution, Date creationDate) {
+  @Test
+  public void lazy_load_changes() {
+    IssueDto issue1 = db.issues()
+      .insertIssue(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_REOPENED).setLine(1).setChecksum("checksum"));
+    db.issues().insertComment(issue1, "user1", "A comment 1");
+    db.issues().insertFieldDiffs(issue1, FieldDiffs.parse("severity=BLOCKER|INFO,assignee=toto|titi"));
+    IssueDto issue2 = db.issues()
+      .insertIssue(IssueTesting.newIssue(rule, branch2Dto, fileOnBranch2Dto).setKee("issue2").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
+    db.issues().insertComment(issue2, "user2", "A comment 2");
+    db.issues().insertFieldDiffs(issue2, FieldDiffs.parse("severity=BLOCKER|MINOR,assignee=foo|bar"));
+    DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, null, new Date());
+
+    copier.tryMerge(FILE_1, Collections.singleton(newIssue));
+
+    ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
+    verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(eq(newIssue), issueToMerge.capture(), eq("myBranch2"));
+
+    assertThat(issueToMerge.getValue().key()).isEqualTo("issue2");
+    assertThat(issueToMerge.getValue().comments()).isNotEmpty();
+    assertThat(issueToMerge.getValue().changes()).isNotEmpty();
+  }
+
+  private static DefaultIssue createIssue(String key, RuleKey ruleKey, String status, @Nullable String resolution, Date creationDate) {
     DefaultIssue issue = new DefaultIssue();
     issue.setKey(key);
-    issue.setRuleKey(RuleKey.of("repo", ruleKey));
+    issue.setRuleKey(ruleKey);
     issue.setMessage("msg");
     issue.setLine(1);
     issue.setStatus(status);
     issue.setResolution(resolution);
     issue.setCreationDate(creationDate);
+    issue.setChecksum("checksum");
     return issue;
   }
 
-  private ShortBranchIssue newShortBranchIssue(DefaultIssue i, String originBranch) {
-    return new ShortBranchIssue(i.key(), i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status(), originBranch, i.creationDate());
-  }
 }