issue.setStatus(ref.getStatus());
issue.setAssignee(ref.getAssignee());
issue.setAuthorLogin(ref.getAuthorLogin());
+
if (ref.getIssueAttributes() != null) {
issue.setAttributes(KeyValueFormat.parse(ref.getIssueAttributes()));
}
Long debtInMinutes = ref.getDebt();
Duration previousTechnicalDebt = debtInMinutes != null ? Duration.create(debtInMinutes) : null;
updater.setPastTechnicalDebt(issue, previousTechnicalDebt, changeContext);
+ updater.setPastProject(issue, ref.getRootComponentKey(), changeContext);
}
}
@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);
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
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;
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
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;
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
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;
}
}
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())
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;
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();
}
@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();
}
}
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;
ModuleIssues moduleIssues = mock(ModuleIssues.class);
IssueCache cache = mock(IssueCache.class);
+ Project project = mock(Project.class);
Component component = mock(Component.class);
@Test
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);
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);
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;
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);
@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);
public void setUp() {
when(project.getAnalysisDate()).thenReturn(new Date());
when(project.getEffectiveKey()).thenReturn("org.apache:struts-core");
+ when(project.getRoot()).thenReturn(project);
}
@Test
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
*/
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;
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);
@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);
@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();
@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
public class DefaultIssueBuilder implements Issuable.IssueBuilder {
private String componentKey;
+ private String projectKey;
private RuleKey ruleKey;
private Integer line;
private String message;
return this;
}
+
+ public DefaultIssueBuilder projectKey(String projectKey) {
+ this.projectKey = projectKey;
+ return this;
+ }
+
@Override
public Issuable.IssueBuilder ruleKey(RuleKey ruleKey) {
this.ruleKey = ruleKey;
@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");
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);
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);
+ }
+
}
.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())
.setActionPlanKey(issue.actionPlanKey())
.setIssueAttributes(KeyValueFormat.format(issue.attributes()))
.setAuthorLogin(issue.authorLogin())
+ .setRootComponentId(rootComponentId)
.setIssueCreationDate(issue.creationDate())
.setIssueCloseDate(issue.closeDate())
.setIssueUpdateDate(issue.updateDate())
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.
}
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);
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()));
}
}
reporter=#{reporter},
assignee=#{assignee},
author_login=#{authorLogin},
+ root_component_id=#{rootComponentId},
issue_attributes=#{issueAttributes},
issue_creation_date=#{issueCreationDate},
issue_update_date=#{issueUpdateDate},
reporter=#{reporter},
assignee=#{assignee},
author_login=#{authorLogin},
+ root_component_id=#{rootComponentId},
issue_attributes=#{issueAttributes},
issue_creation_date=#{issueCreationDate},
issue_update_date=#{issueUpdateDate},
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>
@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)
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");
public void not_set_default_severity() {
DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder()
.componentKey("Action.java")
+ .projectKey("org.apache.struts")
.ruleKey(RuleKey.of("squid", "NullDereference"))
.build();
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");
+ }
+
}
}
@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
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
IssueDto dto = new IssueDto();
dto.setComponentId(123l);
- dto.setRootComponentId(100l);
+ dto.setRootComponentId(101l);
dto.setRuleId(200);
dto.setKee("ABCDE");
dto.setLine(500);
IssueDto dto = new IssueDto();
dto.setComponentId(123l);
- dto.setRootComponentId(100l);
+ dto.setRootComponentId(101l);
dto.setRuleId(200);
dto.setKee("ABCDE");
dto.setLine(500);
IssueDto dto = new IssueDto();
dto.setComponentId(123l);
- dto.setRootComponentId(100l);
+ dto.setRootComponentId(101l);
dto.setRuleId(200);
dto.setKee("ABCDE");
dto.setLine(500);
--- /dev/null
+<!--
+ ~ Sonar, open source software quality management tool.
+ ~ Copyright (C) 2008-2012 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ Sonar 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.
+ ~
+ ~ Sonar 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 Sonar; if not, write to the Free Software
+ ~ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ -->
+
+<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>
id="100"
kee="ABCDE"
component_id="123"
- root_component_id="100"
+ root_component_id="101"
rule_id="200"
severity="BLOCKER"
manual_severity="[false]"
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;
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;
}
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);
}
*/
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;
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;
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");
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;
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() {
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);
}
@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);
}
@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()));
}
@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);
}
@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);
}
@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);
}
@Test
- public void should_assign() {
+ public void assign() {
String assignee = "perceval";
User user = new DefaultUser();
}
@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);
}
@Test
- public void should_not_assign() {
+ public void not_assign() {
String assignee = "perceval";
User user = new DefaultUser();
}
@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);
}
@Test
- public void should_plan() {
+ public void plan() {
String actionPlanKey = "EFGH";
ActionPlan actionPlan = new DefaultActionPlan();
}
@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);
}
@Test
- public void should_not_plan() {
+ public void not_plan() {
String actionPlanKey = "EFGH";
ActionPlan actionPlan = new DefaultActionPlan();
}
@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);
}
@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);
}
@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);
@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");
}
@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");
}
@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");
}
@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");