@@ -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); | |||
} | |||
} | |||
@@ -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 |
@@ -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 |
@@ -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; | |||
} | |||
} |
@@ -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()) |
@@ -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(); | |||
} | |||
} |
@@ -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); |
@@ -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); | |||
@@ -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 |
@@ -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 |
@@ -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); |
@@ -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); | |||
} | |||
} |
@@ -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()) |
@@ -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. |
@@ -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); |
@@ -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())); | |||
} | |||
} | |||
@@ -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 <> '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 <> 'CLOSED' | |||
</where> |
@@ -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(); | |||
@@ -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"); | |||
} | |||
} |
@@ -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 |
@@ -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); |
@@ -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> |
@@ -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]" |
@@ -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); | |||
} | |||
@@ -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"); |
@@ -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"); |