Bläddra i källkod

SONAR-5218 Once a module has been turned into a project, its issues are no more visible in the UI

tags/4.4-RC1
Julien Lancelot 10 år sedan
förälder
incheckning
bfeca720d3
26 ändrade filer med 399 tillägg och 136 borttagningar
  1. 2
    0
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java
  2. 2
    1
      plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java
  3. 5
    2
      sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java
  4. 5
    2
      sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java
  5. 1
    0
      sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
  6. 6
    15
      sonar-batch/src/main/java/org/sonar/batch/issue/ScanIssueStorage.java
  7. 4
    2
      sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java
  8. 4
    2
      sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java
  9. 2
    0
      sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
  10. 22
    24
      sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java
  11. 9
    0
      sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java
  12. 16
    0
      sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java
  13. 2
    1
      sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java
  14. 0
    2
      sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java
  15. 1
    1
      sonar-core/src/main/java/org/sonar/core/issue/db/IssueStorage.java
  16. 1
    1
      sonar-core/src/main/java/org/sonar/core/issue/db/UpdateConflictResolver.java
  17. 6
    2
      sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml
  18. 4
    0
      sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java
  19. 31
    0
      sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java
  20. 27
    1
      sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java
  21. 3
    3
      sonar-core/src/test/java/org/sonar/core/issue/db/IssueMapperTest.java
  22. 127
    0
      sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_non_closed_issues_by_module_on_removed_project.xml
  23. 1
    1
      sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml
  24. 2
    13
      sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
  25. 22
    7
      sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
  26. 94
    56
      sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java

+ 2
- 0
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java Visa fil

@@ -157,6 +157,7 @@ public class IssueTrackingDecorator implements Decorator {
issue.setStatus(ref.getStatus());
issue.setAssignee(ref.getAssignee());
issue.setAuthorLogin(ref.getAuthorLogin());

if (ref.getIssueAttributes() != null) {
issue.setAttributes(KeyValueFormat.parse(ref.getIssueAttributes()));
}
@@ -180,6 +181,7 @@ public class IssueTrackingDecorator implements Decorator {
Long debtInMinutes = ref.getDebt();
Duration previousTechnicalDebt = debtInMinutes != null ? Duration.create(debtInMinutes) : null;
updater.setPastTechnicalDebt(issue, previousTechnicalDebt, changeContext);
updater.setPastProject(issue, ref.getRootComponentKey(), changeContext);
}
}


+ 2
- 1
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java Visa fil

@@ -508,7 +508,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
@Test
public void merge_matched_issue() throws Exception {
IssueDto previousIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle")
.setLine(10).setSeverity("MAJOR").setMessage("Message").setEffortToFix(1.5).setDebt(1L);
.setLine(10).setSeverity("MAJOR").setMessage("Message").setEffortToFix(1.5).setDebt(1L).setRootComponentKey_unit_test_only("sample");
DefaultIssue issue = new DefaultIssue();

IssueTrackingResult trackingResult = mock(IssueTrackingResult.class);
@@ -521,6 +521,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
verify(updater).setPastMessage(eq(issue), eq("Message"), any(IssueChangeContext.class));
verify(updater).setPastEffortToFix(eq(issue), eq(1.5), any(IssueChangeContext.class));
verify(updater).setPastTechnicalDebt(eq(issue), eq(Duration.create(1L)), any(IssueChangeContext.class));
verify(updater).setPastProject(eq(issue), eq("sample"), any(IssueChangeContext.class));
}

@Test

+ 5
- 2
sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java Visa fil

@@ -24,6 +24,7 @@ import org.sonar.api.component.Component;
import org.sonar.api.issue.Issuable;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.resources.Project;
import org.sonar.core.issue.DefaultIssueBuilder;

import java.util.List;
@@ -36,16 +37,18 @@ public class DefaultIssuable implements Issuable {
private final ModuleIssues moduleIssues;
private final IssueCache cache;
private final Component component;
private final Project project;

DefaultIssuable(Component component, ModuleIssues moduleIssues, IssueCache cache) {
DefaultIssuable(Component component, Project project, ModuleIssues moduleIssues, IssueCache cache) {
this.component = component;
this.project = project;
this.moduleIssues = moduleIssues;
this.cache = cache;
}

@Override
public IssueBuilder newIssueBuilder() {
return new DefaultIssueBuilder().componentKey(component.key());
return new DefaultIssueBuilder().componentKey(component.key()).projectKey(project.getKey());
}

@Override

+ 5
- 2
sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java Visa fil

@@ -22,6 +22,7 @@ package org.sonar.batch.issue;
import org.sonar.api.component.Component;
import org.sonar.api.issue.Issuable;
import org.sonar.api.resources.Scopes;
import org.sonar.batch.ProjectTree;
import org.sonar.core.component.PerspectiveBuilder;
import org.sonar.core.component.ResourceComponent;

@@ -35,11 +36,13 @@ public class IssuableFactory extends PerspectiveBuilder<Issuable> {

private final ModuleIssues moduleIssues;
private final IssueCache cache;
private final ProjectTree projectTree;

public IssuableFactory(ModuleIssues moduleIssues, IssueCache cache) {
public IssuableFactory(ModuleIssues moduleIssues, IssueCache cache, ProjectTree projectTree) {
super(Issuable.class);
this.moduleIssues = moduleIssues;
this.cache = cache;
this.projectTree = projectTree;
}

@CheckForNull
@@ -49,6 +52,6 @@ public class IssuableFactory extends PerspectiveBuilder<Issuable> {
if (component instanceof ResourceComponent) {
supported = Scopes.isHigherThanOrEquals(((ResourceComponent) component).scope(), Scopes.FILE);
}
return supported ? new DefaultIssuable(component, moduleIssues, cache) : null;
return supported ? new DefaultIssuable(component, projectTree.getRootProject(), moduleIssues, cache) : null;
}
}

+ 1
- 0
sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java Visa fil

@@ -67,6 +67,7 @@ public class ModuleIssues {
private DefaultIssue newIssue(Violation violation) {
return (DefaultIssue) new DefaultIssueBuilder()
.componentKey(violation.getResource().getEffectiveKey())
.projectKey(project.getRoot().getEffectiveKey())
.ruleKey(RuleKey.of(violation.getRule().getRepositoryKey(), violation.getRule().getKey()))
.effortToFix(violation.getCost())
.line(violation.getLineId())

+ 6
- 15
sonar-batch/src/main/java/org/sonar/batch/issue/ScanIssueStorage.java Visa fil

@@ -23,6 +23,7 @@ import org.sonar.api.BatchComponent;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.rules.RuleFinder;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.index.SnapshotCache;
import org.sonar.core.issue.db.IssueStorage;
import org.sonar.core.persistence.MyBatis;
@@ -34,16 +35,18 @@ public class ScanIssueStorage extends IssueStorage implements BatchComponent {

private final SnapshotCache snapshotCache;
private final ResourceDao resourceDao;
private final ProjectTree projectTree;

public ScanIssueStorage(MyBatis mybatis, RuleFinder ruleFinder, SnapshotCache snapshotCache, ResourceDao resourceDao) {
public ScanIssueStorage(MyBatis mybatis, RuleFinder ruleFinder, SnapshotCache snapshotCache, ResourceDao resourceDao, ProjectTree projectTree) {
super(mybatis, ruleFinder);
this.snapshotCache = snapshotCache;
this.resourceDao = resourceDao;
this.projectTree = projectTree;
}

@Override
protected long componentId(DefaultIssue issue) {
Snapshot snapshot = getSnapshot(issue);
Snapshot snapshot = snapshotCache.get(issue.componentKey());
if (snapshot != null) {
return snapshot.getResourceId();
}
@@ -58,19 +61,7 @@ public class ScanIssueStorage extends IssueStorage implements BatchComponent {

@Override
protected long projectId(DefaultIssue issue) {
Snapshot snapshot = getSnapshot(issue);
if (snapshot != null) {
return snapshot.getRootProjectId();
}
throw new IllegalStateException("Project id not found for: " + issue.componentKey());
}

private Snapshot getSnapshot(DefaultIssue issue) {
Snapshot snapshot = snapshotCache.get(issue.componentKey());
if (snapshot != null) {
return snapshot;
}
return null;
return projectTree.getRootProject().getId();
}

}

+ 4
- 2
sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java Visa fil

@@ -23,6 +23,7 @@ import org.junit.Test;
import org.sonar.api.component.Component;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.resources.Project;

import java.util.Arrays;
import java.util.List;
@@ -35,6 +36,7 @@ public class DefaultIssuableTest {

ModuleIssues moduleIssues = mock(ModuleIssues.class);
IssueCache cache = mock(IssueCache.class);
Project project = mock(Project.class);
Component component = mock(Component.class);

@Test
@@ -44,7 +46,7 @@ public class DefaultIssuableTest {
DefaultIssue unresolved = new DefaultIssue();
when(cache.byComponent("struts:org.apache.Action")).thenReturn(Arrays.asList(resolved, unresolved));

DefaultIssuable perspective = new DefaultIssuable(component, moduleIssues, cache);
DefaultIssuable perspective = new DefaultIssuable(component, project, moduleIssues, cache);

List<Issue> issues = perspective.issues();
assertThat(issues).containsOnly(unresolved);
@@ -57,7 +59,7 @@ public class DefaultIssuableTest {
DefaultIssue unresolved = new DefaultIssue();
when(cache.byComponent("struts:org.apache.Action")).thenReturn(Arrays.asList(resolved, unresolved));

DefaultIssuable perspective = new DefaultIssuable(component, moduleIssues, cache);
DefaultIssuable perspective = new DefaultIssuable(component, project, moduleIssues, cache);

List<Issue> issues = perspective.resolvedIssues();
assertThat(issues).containsOnly(resolved);

+ 4
- 2
sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java Visa fil

@@ -25,6 +25,7 @@ import org.sonar.api.component.Component;
import org.sonar.api.issue.Issuable;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.batch.ProjectTree;
import org.sonar.core.component.ResourceComponent;

import static org.fest.assertions.Assertions.assertThat;
@@ -34,10 +35,11 @@ public class IssuableFactoryTest {

ModuleIssues moduleIssues = mock(ModuleIssues.class);
IssueCache cache = mock(IssueCache.class, Mockito.RETURNS_MOCKS);
ProjectTree projectTree = mock(ProjectTree.class);

@Test
public void file_should_be_issuable() throws Exception {
IssuableFactory factory = new IssuableFactory(moduleIssues, cache);
IssuableFactory factory = new IssuableFactory(moduleIssues, cache, projectTree);
Component component = new ResourceComponent(new File("foo/bar.c").setEffectiveKey("foo/bar.c"));
Issuable issuable = factory.loadPerspective(Issuable.class, component);

@@ -48,7 +50,7 @@ public class IssuableFactoryTest {

@Test
public void project_should_be_issuable() throws Exception {
IssuableFactory factory = new IssuableFactory(moduleIssues, cache);
IssuableFactory factory = new IssuableFactory(moduleIssues, cache, projectTree);
Component component = new ResourceComponent(new Project("Foo").setEffectiveKey("foo"));
Issuable issuable = factory.loadPerspective(Issuable.class, component);


+ 2
- 0
sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java Visa fil

@@ -73,6 +73,7 @@ public class ModuleIssuesTest {
public void setUp() {
when(project.getAnalysisDate()).thenReturn(new Date());
when(project.getEffectiveKey()).thenReturn("org.apache:struts-core");
when(project.getRoot()).thenReturn(project);
}

@Test
@@ -225,6 +226,7 @@ public class ModuleIssuesTest {
assertThat(issue.key()).isNotEmpty();
assertThat(issue.ruleKey().toString()).isEqualTo("squid:AvoidCycle");
assertThat(issue.componentKey()).isEqualTo("struts:src/org/struts/Action.java");
assertThat(issue.projectKey()).isEqualTo("org.apache:struts-core");
}

@Test

+ 22
- 24
sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java Visa fil

@@ -19,13 +19,19 @@
*/
package org.sonar.batch.issue;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.resources.Project;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleFinder;
import org.sonar.api.rules.RuleQuery;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.index.SnapshotCache;
import org.sonar.core.persistence.AbstractDaoTestCase;
import org.sonar.core.resource.ResourceDao;
@@ -34,16 +40,28 @@ import java.util.Collection;

import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class ScanIssueStorageTest extends AbstractDaoTestCase {

@Mock
SnapshotCache snapshotCache;

@Mock
ProjectTree projectTree;

ScanIssueStorage storage;

@Before
public void setUp() throws Exception {
storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis()), projectTree);
}

@Test
public void should_load_component_id_from_cache() throws Exception {
SnapshotCache snapshotCache = mock(SnapshotCache.class);
when(snapshotCache.get("struts:Action.java")).thenReturn(new Snapshot().setResourceId(123));

ScanIssueStorage storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis()));
long componentId = storage.componentId(new DefaultIssue().setComponentKey("struts:Action.java"));

assertThat(componentId).isEqualTo(123);
@@ -52,10 +70,8 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase {
@Test
public void should_load_component_id_from_db() throws Exception {
setupData("should_load_component_id_from_db");
SnapshotCache snapshotCache = mock(SnapshotCache.class);
when(snapshotCache.get("struts:Action.java")).thenReturn(null);

ScanIssueStorage storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis()));
long componentId = storage.componentId(new DefaultIssue().setComponentKey("struts:Action.java"));

assertThat(componentId).isEqualTo(123);
@@ -64,10 +80,8 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase {
@Test
public void should_fail_to_load_component_id_if_unknown_component() throws Exception {
setupData("should_fail_to_load_component_id_if_unknown_component");
SnapshotCache snapshotCache = mock(SnapshotCache.class);
when(snapshotCache.get("struts:Action.java")).thenReturn(null);

ScanIssueStorage storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis()));
try {
storage.componentId(new DefaultIssue().setComponentKey("struts:Action.java"));
fail();
@@ -78,29 +92,13 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase {

@Test
public void should_load_project_id() throws Exception {
SnapshotCache snapshotCache = mock(SnapshotCache.class);
when(snapshotCache.get("struts:Action.java")).thenReturn(new Snapshot().setResourceId(123).setRootProjectId(100));
when(projectTree.getRootProject()).thenReturn((Project) new Project("struts").setId(100));

ScanIssueStorage storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis()));
long projectId = storage.projectId(new DefaultIssue().setComponentKey("struts:Action.java"));

assertThat(projectId).isEqualTo(100);
}

@Test
public void should_fail_to_load_project_id_if_unknown_component() throws Exception {
SnapshotCache snapshotCache = mock(SnapshotCache.class);
when(snapshotCache.get("struts:Action.java")).thenReturn(null);

ScanIssueStorage storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis()));
try {
storage.projectId(new DefaultIssue().setComponentKey("struts:Action.java"));
fail();
} catch (Exception e) {
assertThat(e).hasMessage("Project id not found for: struts:Action.java");
}
}

static class FakeRuleFinder implements RuleFinder {

@Override

+ 9
- 0
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java Visa fil

@@ -35,6 +35,7 @@ import java.util.UUID;
public class DefaultIssueBuilder implements Issuable.IssueBuilder {

private String componentKey;
private String projectKey;
private RuleKey ruleKey;
private Integer line;
private String message;
@@ -52,6 +53,12 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder {
return this;
}


public DefaultIssueBuilder projectKey(String projectKey) {
this.projectKey = projectKey;
return this;
}

@Override
public Issuable.IssueBuilder ruleKey(RuleKey ruleKey) {
this.ruleKey = ruleKey;
@@ -99,6 +106,7 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder {

@Override
public DefaultIssue build() {
Preconditions.checkNotNull(projectKey, "Project key must be set");
Preconditions.checkNotNull(componentKey, "Component key must be set");
Preconditions.checkNotNull(ruleKey, "Rule key must be set");

@@ -107,6 +115,7 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder {
Preconditions.checkState(!Strings.isNullOrEmpty(key), "Fail to generate issue key");
issue.setKey(key);
issue.setComponentKey(componentKey);
issue.setProjectKey(projectKey);
issue.setRuleKey(ruleKey);
issue.setMessage(message);
issue.setSeverity(severity);

+ 16
- 0
sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java Visa fil

@@ -247,4 +247,20 @@ public class IssueUpdater implements BatchComponent, ServerComponent {
return false;
}

public boolean setProject(DefaultIssue issue, String key, IssueChangeContext context) {
if (!Objects.equal(key, issue.projectKey())) {
issue.setProjectKey(key);
issue.setUpdateDate(context.date());
issue.setChanged(true);
return true;
}
return false;
}

public boolean setPastProject(DefaultIssue issue, String previousKey, IssueChangeContext context) {
String currentProjectKey = issue.projectKey();
issue.setProjectKey(previousKey);
return setProject(issue, currentProjectKey, context);
}

}

+ 2
- 1
sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java Visa fil

@@ -390,7 +390,7 @@ public final class IssueDto implements Serializable {
.setUpdatedAt(now);
}

public static IssueDto toDtoForUpdate(DefaultIssue issue, Date now) {
public static IssueDto toDtoForUpdate(DefaultIssue issue, Long rootComponentId, Date now) {
// Invariant fields, like key and rule, can't be updated
return new IssueDto()
.setKee(issue.key())
@@ -408,6 +408,7 @@ public final class IssueDto implements Serializable {
.setActionPlanKey(issue.actionPlanKey())
.setIssueAttributes(KeyValueFormat.format(issue.attributes()))
.setAuthorLogin(issue.authorLogin())
.setRootComponentId(rootComponentId)
.setIssueCreationDate(issue.creationDate())
.setIssueCloseDate(issue.closeDate())
.setIssueUpdateDate(issue.updateDate())

+ 0
- 2
sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java Visa fil

@@ -31,8 +31,6 @@ public interface IssueMapper {

IssueDto selectByKey(String key);

List<IssueDto> selectNonClosedIssuesByModule(int rootComponentId);

/**
* Return a paginated list of authorized issue ids for a user.
* If the role is null, then the authorisation check is disabled.

+ 1
- 1
sonar-core/src/main/java/org/sonar/core/issue/db/IssueStorage.java Visa fil

@@ -119,7 +119,7 @@ public abstract class IssueStorage {
}

private void update(IssueMapper issueMapper, Date now, DefaultIssue issue) {
IssueDto dto = IssueDto.toDtoForUpdate(issue, now);
IssueDto dto = IssueDto.toDtoForUpdate(issue, projectId(issue), now);
if (Issue.STATUS_CLOSED.equals(issue.status()) || issue.selectedAt() == null) {
// Issue is closed by scan or changed by end-user
issueMapper.update(dto);

+ 1
- 1
sonar-core/src/main/java/org/sonar/core/issue/db/UpdateConflictResolver.java Visa fil

@@ -41,7 +41,7 @@ class UpdateConflictResolver {
IssueDto dbIssue = mapper.selectByKey(issue.key());
if (dbIssue != null) {
mergeFields(dbIssue, issue);
mapper.update(IssueDto.toDtoForUpdate(issue, new Date()));
mapper.update(IssueDto.toDtoForUpdate(issue, dbIssue.getRootComponentId(), new Date()));
}
}


+ 6
- 2
sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml Visa fil

@@ -89,6 +89,7 @@
reporter=#{reporter},
assignee=#{assignee},
author_login=#{authorLogin},
root_component_id=#{rootComponentId},
issue_attributes=#{issueAttributes},
issue_creation_date=#{issueCreationDate},
issue_update_date=#{issueUpdateDate},
@@ -115,6 +116,7 @@
reporter=#{reporter},
assignee=#{assignee},
author_login=#{authorLogin},
root_component_id=#{rootComponentId},
issue_attributes=#{issueAttributes},
issue_creation_date=#{issueCreationDate},
issue_update_date=#{issueUpdateDate},
@@ -163,10 +165,12 @@
i.updated_at as updatedAt,
r.plugin_rule_key as ruleKey,
r.plugin_name as ruleRepo,
p.kee as componentKey
p.kee as componentKey,
root.kee as rootComponentKey
from issues i
inner join rules r on r.id=i.rule_id
inner join (select p.id,p.kee from projects p where (p.root_id=#{id} and p.qualifier &lt;&gt; 'BRC') or (p.id=#{id})) p on p.id=i.component_id
inner join rules r on r.id=i.rule_id
left outer join projects root on root.id=i.root_component_id
<where>
and i.status &lt;&gt; 'CLOSED'
</where>

+ 4
- 0
sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java Visa fil

@@ -32,8 +32,10 @@ public class DefaultIssueBuilderTest {
@Test
public void build_new_issue() throws Exception {
String componentKey = "org.apache.struts:struts-core:Action.java";
String projectKey = "org.apache.struts";
DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder()
.componentKey(componentKey)
.projectKey(projectKey)
.message("the message")
.line(123)
.effortToFix(10000.0)
@@ -47,6 +49,7 @@ public class DefaultIssueBuilderTest {
assertThat(issue.key()).isNotNull();
assertThat(issue.effortToFix()).isEqualTo(10000.0);
assertThat(issue.componentKey()).isEqualTo(componentKey);
assertThat(issue.projectKey()).isEqualTo(projectKey);
assertThat(issue.message()).isEqualTo("the message");
assertThat(issue.line()).isEqualTo(123);
assertThat(issue.ruleKey().repository()).isEqualTo("squid");
@@ -65,6 +68,7 @@ public class DefaultIssueBuilderTest {
public void not_set_default_severity() {
DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder()
.componentKey("Action.java")
.projectKey("org.apache.struts")
.ruleKey(RuleKey.of("squid", "NullDereference"))
.build();


+ 31
- 0
sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java Visa fil

@@ -447,4 +447,35 @@ public class IssueUpdaterTest {
assertThat(issue.mustSendNotifications()).isFalse();
}

@Test
public void set_project() throws Exception {
boolean updated = updater.setProject(issue, "sample", context);
assertThat(updated).isTrue();
assertThat(issue.projectKey()).isEqualTo("sample");

// do not save change
assertThat(issue.currentChange()).isNull();
assertThat(issue.mustSendNotifications()).isFalse();
}

@Test
public void set_past_project() throws Exception {
issue.setProjectKey("new project key");
boolean updated = updater.setPastProject(issue, "past project key", context);
assertThat(updated).isTrue();
assertThat(issue.projectKey()).isEqualTo("new project key");

// do not save change
assertThat(issue.currentChange()).isNull();
assertThat(issue.mustSendNotifications()).isFalse();
}

@Test
public void not_set_past_project_if_no_change() throws Exception {
issue.setProjectKey("key");
boolean updated = updater.setPastProject(issue, "key", context);
assertThat(updated).isFalse();
assertThat(issue.projectKey()).isEqualTo("key");
}

}

+ 27
- 1
sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java Visa fil

@@ -310,7 +310,7 @@ public class IssueDaoTest extends AbstractDaoTestCase {
}

@Test
public void should_select_non_closed_issues_by_module() {
public void select_non_closed_issues_by_module() {
setupData("shared", "should_select_non_closed_issues_by_module");

// 400 is a non-root module, we should find 2 issues from classes and one on itself
@@ -322,11 +322,37 @@ public class IssueDaoTest extends AbstractDaoTestCase {
assertThat(issue.getRuleRepo()).isNotNull();
assertThat(issue.getRule()).isNotNull();
assertThat(issue.getComponentKey()).isNotNull();
assertThat(issue.getRootComponentKey()).isEqualTo("struts");

// 399 is the root module, we should only find 1 issue on itself
handler = new DefaultResultHandler();
dao.selectNonClosedIssuesByModule(399, handler);
assertThat(handler.getResultList()).hasSize(1);

issue = (IssueDto) handler.getResultList().get(0);
assertThat(issue.getComponentKey()).isEqualTo("struts");
assertThat(issue.getRootComponentKey()).isEqualTo("struts");
}

/**
* SONAR-5218
*/
@Test
public void select_non_closed_issues_by_module_on_removed_project() {
// All issues are linked on a project that is not existing anymore

setupData("shared", "should_select_non_closed_issues_by_module_on_removed_project");

// 400 is a non-root module, we should find 2 issues from classes and one on itself
DefaultResultHandler handler = new DefaultResultHandler();
dao.selectNonClosedIssuesByModule(400, handler);
assertThat(handler.getResultList()).hasSize(3);

IssueDto issue = (IssueDto) handler.getResultList().get(0);
assertThat(issue.getRuleRepo()).isNotNull();
assertThat(issue.getRule()).isNotNull();
assertThat(issue.getComponentKey()).isNotNull();
assertThat(issue.getRootComponentKey()).isNull();
}

@Test

+ 3
- 3
sonar-core/src/test/java/org/sonar/core/issue/db/IssueMapperTest.java Visa fil

@@ -84,7 +84,7 @@ public class IssueMapperTest extends AbstractDaoTestCase {

IssueDto dto = new IssueDto();
dto.setComponentId(123l);
dto.setRootComponentId(100l);
dto.setRootComponentId(101l);
dto.setRuleId(200);
dto.setKee("ABCDE");
dto.setLine(500);
@@ -119,7 +119,7 @@ public class IssueMapperTest extends AbstractDaoTestCase {

IssueDto dto = new IssueDto();
dto.setComponentId(123l);
dto.setRootComponentId(100l);
dto.setRootComponentId(101l);
dto.setRuleId(200);
dto.setKee("ABCDE");
dto.setLine(500);
@@ -157,7 +157,7 @@ public class IssueMapperTest extends AbstractDaoTestCase {

IssueDto dto = new IssueDto();
dto.setComponentId(123l);
dto.setRootComponentId(100l);
dto.setRootComponentId(101l);
dto.setRuleId(200);
dto.setKee("ABCDE");
dto.setLine(500);

+ 127
- 0
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_non_closed_issues_by_module_on_removed_project.xml Visa fil

@@ -0,0 +1,127 @@
<!--
~ SonarQube, open source software quality management tool.
~ Copyright (C) 2008-2014 SonarSource
~ mailto:contact AT sonarsource DOT com
~
~ SonarQube 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.
~
~ SonarQube 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.
-->

<dataset>

<!-- Open Issue on a file -->
<issues
id="100"
kee="100"
component_id="401"
root_component_id="111"
rule_id="500"
severity="BLOCKER"
manual_severity="[false]"
message="[null]"
line="200"
effort_to_fix="[null]"
status="OPEN"
resolution="[null]"
checksum="[null]"
reporter="user"
assignee="user"
author_login="[null]"
issue_attributes="[null]"
issue_creation_date="2013-04-16"
issue_update_date="2013-04-16"
issue_close_date="2013-04-16"
created_at="2013-04-16"
updated_at="[null]"
/>

<!-- Open Issue on a file -->
<issues
id="101"
kee="101"
component_id="402"
root_component_id="111"
rule_id="501"
severity="MAJOR"
manual_severity="[false]"
message="[null]"
line="120"
effort_to_fix="[null]"
status="OPEN"
resolution="[null]"
checksum="[null]"
reporter="[null]"
assignee="user"
author_login="[null]"
issue_attributes="[null]"
issue_creation_date="2013-04-16"
issue_update_date="2013-04-16"
issue_close_date="2013-04-16"
created_at="2013-04-10"
updated_at="[null]"
/>

<!-- Closed Issue on a file -->
<issues
id="102"
kee="102"
component_id="402"
root_component_id="111"
rule_id="501"
severity="MAJOR"
manual_severity="[false]"
message="[null]"
line="120"
effort_to_fix="[null]"
status="CLOSED"
resolution="FIXED"
checksum="[null]"
reporter="[null]"
assignee="user"
author_login="[null]"
issue_attributes="[null]"
issue_creation_date="2013-04-16"
issue_update_date="2013-04-16"
issue_close_date="2013-04-16"
created_at="2013-04-10"
updated_at="[null]"
/>

<!-- Open Issue on a sub module -->
<issues
id="103"
kee="103"
component_id="400"
root_component_id="111"
rule_id="501"
severity="MAJOR"
manual_severity="[false]"
message="[null]"
line="[null]"
effort_to_fix="[null]"
status="OPEN"
resolution="[null]"
checksum="[null]"
reporter="[null]"
assignee="user"
author_login="[null]"
issue_attributes="[null]"
issue_creation_date="2013-04-16"
issue_update_date="2013-04-16"
issue_close_date="2013-04-16"
created_at="2013-04-10"
updated_at="[null]"
/>

</dataset>

+ 1
- 1
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml Visa fil

@@ -3,7 +3,7 @@
id="100"
kee="ABCDE"
component_id="123"
root_component_id="100"
root_component_id="101"
rule_id="200"
severity="BLOCKER"
manual_severity="[false]"

+ 2
- 13
sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java Visa fil

@@ -21,7 +21,6 @@ package org.sonar.server.issue;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
@@ -36,11 +35,9 @@ import org.sonar.api.issue.action.Action;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.issue.internal.FieldDiffs;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.utils.SonarException;
import org.sonar.core.issue.ActionPlanStats;
import org.sonar.core.issue.DefaultActionPlan;
import org.sonar.core.issue.DefaultIssueBuilder;
import org.sonar.core.issue.DefaultIssueFilter;
import org.sonar.core.issue.workflow.Transition;
import org.sonar.core.resource.ResourceDao;
@@ -236,16 +233,8 @@ public class InternalRubyIssueService implements ServerComponent {
}

if (result.ok()) {
DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder()
.componentKey(componentKey)
.line(RubyUtils.toInteger(params.get("line")))
.message(params.get("message"))
.severity(Objects.firstNonNull(params.get("severity"), Severity.MAJOR))
.effortToFix(RubyUtils.toDouble(params.get("effortToFix")))
.ruleKey(ruleKey)
.reporter(UserSession.get().login())
.build();
issue = issueService.createManualIssue(issue, UserSession.get());
DefaultIssue issue = issueService.createManualIssue(componentKey, ruleKey, RubyUtils.toInteger(params.get("line")), params.get("message"), params.get("severity"),
RubyUtils.toDouble(params.get("effortToFix")), UserSession.get());
result.set(issue);
}


+ 22
- 7
sonar-server/src/main/java/org/sonar/server/issue/IssueService.java Visa fil

@@ -19,6 +19,7 @@
*/
package org.sonar.server.issue;

import com.google.common.base.Objects;
import com.google.common.base.Strings;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.ServerComponent;
@@ -28,11 +29,14 @@ import org.sonar.api.issue.IssueQuery;
import org.sonar.api.issue.IssueQueryResult;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.issue.internal.IssueChangeContext;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleFinder;
import org.sonar.api.user.User;
import org.sonar.api.user.UserFinder;
import org.sonar.api.web.UserRole;
import org.sonar.core.issue.DefaultIssueBuilder;
import org.sonar.core.issue.IssueNotifications;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.core.issue.db.IssueStorage;
@@ -202,15 +206,26 @@ public class IssueService implements ServerComponent {
return issue;
}

public DefaultIssue createManualIssue(DefaultIssue issue, UserSession userSession) {
public DefaultIssue createManualIssue(String componentKey, RuleKey ruleKey, @Nullable Integer line, @Nullable String message, @Nullable String severity,
@Nullable Double effortToFix, UserSession userSession) {
verifyLoggedIn(userSession);
ResourceDto resourceDto = resourceDao.getResource(ResourceQuery.create().setKey(issue.componentKey()));
if (resourceDto == null) {
throw new IllegalArgumentException("Unknown component: " + issue.componentKey());
ResourceDto resourceDto = resourceDao.getResource(ResourceQuery.create().setKey(componentKey));
ResourceDto project = resourceDao.getRootProjectByComponentKey(componentKey);
if (resourceDto == null || project == null) {
throw new IllegalArgumentException("Unknown component: " + componentKey);
}
// Force use of correct key in case deprecated key is used
issue.setComponentKey(resourceDto.getKey());
issue.setComponentId(resourceDto.getId());

DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder()
.componentKey(resourceDto.getKey())
.projectKey(project.getKey())
.line(line)
.message(message)
.severity(Objects.firstNonNull(severity, Severity.MAJOR))
.effortToFix(effortToFix)
.ruleKey(ruleKey)
.reporter(UserSession.get().login())
.build();

if (!authorizationDao.isAuthorizedComponentKey(resourceDto.getKey(), userSession.userId(), UserRole.USER)) {
// TODO throw unauthorized
throw new IllegalStateException("User does not have the required role");

+ 94
- 56
sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java Visa fil

@@ -22,7 +22,10 @@ package org.sonar.server.issue;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueQuery;
@@ -61,23 +64,56 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class IssueServiceTest {

private DefaultIssueFinder finder = mock(DefaultIssueFinder.class);
private IssueWorkflow workflow = mock(IssueWorkflow.class);
private IssueUpdater issueUpdater = mock(IssueUpdater.class);
private IssueStorage issueStorage = mock(IssueStorage.class);
private IssueNotifications issueNotifications = mock(IssueNotifications.class);
private ActionPlanService actionPlanService = mock(ActionPlanService.class);
private RuleFinder ruleFinder = mock(RuleFinder.class);
private ResourceDao resourceDao = mock(ResourceDao.class);
private AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
private UserFinder userFinder = mock(UserFinder.class);
private UserSession userSession = mock(UserSession.class);
private Transition transition = Transition.create("reopen", Issue.STATUS_RESOLVED, Issue.STATUS_REOPENED);
private IssueQueryResult issueQueryResult = mock(IssueQueryResult.class);
private DefaultIssue issue = new DefaultIssue().setKey("ABCD");
private IssueService issueService;
@Mock
DefaultIssueFinder finder;

@Mock
IssueWorkflow workflow;

@Mock
IssueUpdater issueUpdater;

@Mock
IssueStorage issueStorage;

@Mock
IssueNotifications issueNotifications;

@Mock
ActionPlanService actionPlanService;

@Mock
RuleFinder ruleFinder;

@Mock
ResourceDao resourceDao;

@Mock
AuthorizationDao authorizationDao;

@Mock
UserFinder userFinder;

@Mock
UserSession userSession;

@Mock
IssueQueryResult issueQueryResult;

@Mock
ResourceDto resource;

@Mock
ResourceDto project;

Transition transition = Transition.create("reopen", Issue.STATUS_RESOLVED, Issue.STATUS_REOPENED);

DefaultIssue issue = new DefaultIssue().setKey("ABCD");

IssueService issueService;

@Before
public void before() {
@@ -90,18 +126,21 @@ public class IssueServiceTest {
when(issueQueryResult.issues()).thenReturn(newArrayList((Issue) issue));
when(issueQueryResult.first()).thenReturn(issue);

when(resource.getKey()).thenReturn("org.sonar.Sample");
when(project.getKey()).thenReturn("Sample");

issueService = new IssueService(finder, workflow, issueStorage, issueUpdater, issueNotifications, actionPlanService, ruleFinder, resourceDao, authorizationDao, userFinder,
mock(PreviewCache.class));
}

@Test
public void should_load_issue() {
public void load_issue() {
IssueQueryResult result = issueService.loadIssue("ABCD");
assertThat(result).isEqualTo(issueQueryResult);
}

@Test
public void should_fail_to_load_issue() {
public void fail_to_load_issue() {
when(issueQueryResult.issues()).thenReturn(Collections.<Issue>emptyList());
when(finder.find(any(IssueQuery.class))).thenReturn(issueQueryResult);

@@ -114,13 +153,13 @@ public class IssueServiceTest {
}

@Test
public void should_list_status() {
public void list_status() {
issueService.listStatus();
verify(workflow).statusKeys();
}

@Test
public void should_list_transitions() {
public void list_transitions() {
List<Transition> transitions = newArrayList(transition);
when(workflow.outTransitions(issue)).thenReturn(transitions);

@@ -130,7 +169,7 @@ public class IssueServiceTest {
}

@Test
public void should_return_no_transition() {
public void return_no_transition() {
when(issueQueryResult.first()).thenReturn(null);
when(issueQueryResult.issues()).thenReturn(newArrayList((Issue) new DefaultIssue()));

@@ -139,7 +178,7 @@ public class IssueServiceTest {
}

@Test
public void should_do_transition() {
public void do_transition() {
when(workflow.doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class))).thenReturn(true);

Issue result = issueService.doTransition("ABCD", transition.key(), userSession);
@@ -157,7 +196,7 @@ public class IssueServiceTest {
}

@Test
public void should_not_do_transition() {
public void not_do_transition() {
when(workflow.doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class))).thenReturn(false);

Issue result = issueService.doTransition("ABCD", transition.key(), userSession);
@@ -168,7 +207,7 @@ public class IssueServiceTest {
}

@Test
public void should_fail_do_transition_if_not_logged() {
public void fail_do_transition_if_not_logged() {
when(userSession.isLoggedIn()).thenReturn(false);
try {
issueService.doTransition("ABCD", transition.key(), userSession);
@@ -180,7 +219,7 @@ public class IssueServiceTest {
}

@Test
public void should_assign() {
public void assign() {
String assignee = "perceval";
User user = new DefaultUser();

@@ -202,7 +241,7 @@ public class IssueServiceTest {
}

@Test
public void should_unassign() {
public void unassign() {
when(issueUpdater.assign(eq(issue), eq((User) null), any(IssueChangeContext.class))).thenReturn(true);

Issue result = issueService.assign("ABCD", null, userSession);
@@ -221,7 +260,7 @@ public class IssueServiceTest {
}

@Test
public void should_not_assign() {
public void not_assign() {
String assignee = "perceval";
User user = new DefaultUser();

@@ -237,7 +276,7 @@ public class IssueServiceTest {
}

@Test
public void should_fail_assign_if_assignee_not_found() {
public void fail_assign_if_assignee_not_found() {
String assignee = "perceval";

when(userFinder.findByLogin(assignee)).thenReturn(null);
@@ -255,7 +294,7 @@ public class IssueServiceTest {
}

@Test
public void should_plan() {
public void plan() {
String actionPlanKey = "EFGH";

ActionPlan actionPlan = new DefaultActionPlan();
@@ -278,7 +317,7 @@ public class IssueServiceTest {
}

@Test
public void should_unplan() {
public void unplan() {
when(issueUpdater.plan(eq(issue), eq((ActionPlan) null), any(IssueChangeContext.class))).thenReturn(true);

Issue result = issueService.plan("ABCD", null, userSession);
@@ -297,7 +336,7 @@ public class IssueServiceTest {
}

@Test
public void should_not_plan() {
public void not_plan() {
String actionPlanKey = "EFGH";

ActionPlan actionPlan = new DefaultActionPlan();
@@ -313,7 +352,7 @@ public class IssueServiceTest {
}

@Test
public void should_fail_plan_if_action_plan_not_found() {
public void fail_plan_if_action_plan_not_found() {
String actionPlanKey = "EFGH";

when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(null);
@@ -330,7 +369,7 @@ public class IssueServiceTest {
}

@Test
public void should_set_severity() {
public void set_severity() {
String severity = "MINOR";
when(issueUpdater.setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class))).thenReturn(true);

@@ -349,7 +388,7 @@ public class IssueServiceTest {
}

@Test
public void should_not_set_severity() {
public void not_set_severity() {
String severity = "MINOR";
when(issueUpdater.setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class))).thenReturn(false);

@@ -363,47 +402,47 @@ public class IssueServiceTest {
@Test
public void create_manual_issue() {
RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey");
DefaultIssue manualIssue = new DefaultIssue().setKey("GHIJ").setRuleKey(RuleKey.of("manual", "manualRuleKey")).setComponentKey("org.sonar.Sample").setMessage("Fix it");
when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey"));
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(mock(ResourceDto.class));
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource);
when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project);

Issue result = issueService.createManualIssue(manualIssue, userSession);
Issue result = issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, "Fix it", null, null, userSession);
assertThat(result).isNotNull();
assertThat(result.message()).isEqualTo("Fix it");
assertThat(result.creationDate()).isNotNull();
assertThat(result.updateDate()).isNotNull();

verify(issueStorage).save(manualIssue);
verify(issueStorage).save(any(DefaultIssue.class));
verify(authorizationDao).isAuthorizedComponentKey(anyString(), anyInt(), eq(UserRole.USER));
}

@Test
public void create_manual_issue_use_rule_name_if_no_message() {
RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey");
DefaultIssue manualIssue = new DefaultIssue().setKey("GHIJ").setRuleKey(RuleKey.of("manual", "manualRuleKey")).setComponentKey("org.sonar.Sample").setMessage("");
when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey").setName("Manual Rule"));
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(mock(ResourceDto.class));
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource);
when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project);

Issue result = issueService.createManualIssue(manualIssue, userSession);
Issue result = issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, "", null, null, userSession);
assertThat(result).isNotNull();
assertThat(result.message()).isEqualTo("Manual Rule");
assertThat(result.creationDate()).isNotNull();
assertThat(result.updateDate()).isNotNull();

verify(issueStorage).save(manualIssue);
verify(issueStorage).save(any(DefaultIssue.class));
verify(authorizationDao).isAuthorizedComponentKey(anyString(), anyInt(), eq(UserRole.USER));
}

@Test
public void should_fail_create_manual_issue_if_not_having_required_role() {
public void fail_create_manual_issue_if_not_having_required_role() {
RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey");
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(mock(ResourceDto.class));
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource);
when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project);
when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey"));
when(authorizationDao.isAuthorizedComponentKey(anyString(), eq(10), anyString())).thenReturn(false);

DefaultIssue manualIssue = new DefaultIssue().setKey("GHIJ").setRuleKey(ruleKey).setComponentKey("org.sonar.Sample");
try {
issueService.createManualIssue(manualIssue, userSession);
issueService.createManualIssue("org.sonar.Sample", ruleKey, null, null, null, null, userSession);
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("User does not have the required role");
@@ -411,13 +450,13 @@ public class IssueServiceTest {
}

@Test
public void should_fail_create_manual_issue_if_not_manual_rule() {
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(mock(ResourceDto.class));
public void fail_create_manual_issue_if_not_manual_rule() {
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource);
when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project);

RuleKey ruleKey = RuleKey.of("squid", "s100");
DefaultIssue manualIssue = new DefaultIssue().setKey("GHIJ").setRuleKey(ruleKey).setComponentKey("org.sonar.Sample");
try {
issueService.createManualIssue(manualIssue, userSession);
issueService.createManualIssue("org.sonar.Sample", ruleKey, null, null, null, null, userSession);
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Issues can be created only on rules marked as 'manual': squid:s100");
@@ -426,14 +465,14 @@ public class IssueServiceTest {
}

@Test
public void should_fail_create_manual_issue_if_rule_not_found() {
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(mock(ResourceDto.class));
public void fail_create_manual_issue_if_rule_not_found() {
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource);
when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project);

RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey");
DefaultIssue manualIssue = new DefaultIssue().setKey("GHIJ").setRuleKey(RuleKey.of("manual", "manualRuleKey")).setComponentKey("org.sonar.Sample");
when(ruleFinder.findByKey(ruleKey)).thenReturn(null);
try {
issueService.createManualIssue(manualIssue, userSession);
issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, null, null, null, userSession);
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown rule: manual:manualRuleKey");
@@ -442,13 +481,12 @@ public class IssueServiceTest {
}

@Test
public void should_fail_create_manual_issue_if_component_not_found() {
public void fail_create_manual_issue_if_component_not_found() {
RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey");
DefaultIssue manualIssue = new DefaultIssue().setKey("GHIJ").setRuleKey(RuleKey.of("manual", "manualRuleKey")).setComponentKey("org.sonar.Sample");
when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey"));
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
try {
issueService.createManualIssue(manualIssue, userSession);
issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, null, null, null, userSession);
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown component: org.sonar.Sample");

Laddar…
Avbryt
Spara