aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-core
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@gmail.com>2013-05-02 14:39:34 +0200
committerSimon Brandhof <simon.brandhof@gmail.com>2013-05-02 14:50:19 +0200
commit1fd5d8abd320428e1e8e4cd1b3ebf793678b10f2 (patch)
tree73668ba184e89bcadf405ae15e1ba3bbd22054d6 /sonar-core
parent5f2b527e01b997a551a1a57779a57a80c1ba3411 (diff)
downloadsonarqube-1fd5d8abd320428e1e8e4cd1b3ebf793678b10f2.tar.gz
sonarqube-1fd5d8abd320428e1e8e4cd1b3ebf793678b10f2.zip
SONAR-3755 implement changelog
Diffstat (limited to 'sonar-core')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java43
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/FieldDiffs.java129
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/IssueChange.java56
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/IssueChangeContext.java10
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/IssueComment.java58
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java63
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/ChangeDtoConverter.java74
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java24
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDto.java23
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java11
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java27
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueStorage.java90
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/workflow/FunctionExecutor.java3
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml25
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl16
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java16
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/FieldDiffsTest.java115
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/IssueChangeContextTest.java48
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java196
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/db/ChangeDtoConverterTest.java78
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java45
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java48
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/db/IssueStorageTest.java121
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert-result.xml11
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert.xml3
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/shared.xml22
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/insert-result.xml22
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/insert.xml3
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/update-result.xml26
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/update.xml26
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml10
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml2
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml2
33 files changed, 1103 insertions, 343 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
index bf4dd2ed49b..1d69fe1a059 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
@@ -23,6 +23,7 @@ import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
@@ -31,10 +32,12 @@ import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
+import java.util.List;
import java.util.Map;
public class DefaultIssue implements Issue {
@@ -60,7 +63,8 @@ public class DefaultIssue implements Issue {
private boolean isAlive = true;
private Map<String, String> attributes = null;
private String authorLogin = null;
- private IssueChange change = null;
+ private FieldDiffs diffs = null;
+ private List<IssueComment> newComments = null;
public String key() {
return key;
@@ -239,6 +243,7 @@ public class DefaultIssue implements Issue {
return this;
}
+ @CheckForNull
public String attribute(String key) {
return attributes == null ? null : attributes.get(key);
}
@@ -259,11 +264,17 @@ public class DefaultIssue implements Issue {
return attributes == null ? Collections.<String, String>emptyMap() : ImmutableMap.copyOf(attributes);
}
- public DefaultIssue setAttributes(Map<String, String> attributes) {
- this.attributes = attributes;
+ public DefaultIssue setAttributes(@Nullable Map<String, String> map) {
+ if (map != null) {
+ if (attributes == null) {
+ attributes = Maps.newHashMap();
+ }
+ attributes.putAll(map);
+ }
return this;
}
+ @CheckForNull
public String authorLogin() {
return authorLogin;
}
@@ -273,18 +284,32 @@ public class DefaultIssue implements Issue {
return this;
}
- public DefaultIssue setDiff(String field, @Nullable Serializable oldValue, @Nullable Serializable newValue) {
+ public DefaultIssue setFieldDiff(IssueChangeContext context, String field, @Nullable Serializable oldValue, @Nullable Serializable newValue) {
if (!Objects.equal(oldValue, newValue)) {
- if (change == null) {
- change = new IssueChange();
+ if (diffs == null) {
+ diffs = new FieldDiffs();
+ diffs.setUserLogin(context.login());
}
- change.setDiff(field, oldValue, newValue);
+ diffs.setDiff(field, oldValue, newValue);
+ }
+ return this;
+ }
+
+ @CheckForNull
+ public FieldDiffs diffs() {
+ return diffs;
+ }
+
+ public DefaultIssue addComment(IssueComment comment) {
+ if (newComments == null) {
+ newComments = Lists.newArrayList();
}
+ newComments.add(comment);
return this;
}
- public IssueChange change() {
- return change;
+ public List<IssueComment> newComments() {
+ return Objects.firstNonNull(newComments, Collections.<IssueComment>emptyList());
}
@Override
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/FieldDiffs.java b/sonar-core/src/main/java/org/sonar/core/issue/FieldDiffs.java
new file mode 100644
index 00000000000..a7004285ecd
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/FieldDiffs.java
@@ -0,0 +1,129 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.
+ */
+package org.sonar.core.issue;
+
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+
+import javax.annotation.Nullable;
+import java.io.Serializable;
+import java.util.Map;
+
+public class FieldDiffs extends IssueChange {
+
+ public static final Splitter FIELDS_SPLITTER = Splitter.on(',').omitEmptyStrings();
+
+ private final Map<String, Diff> diffs = Maps.newLinkedHashMap();
+
+ Map<String, Diff> diffs() {
+ return diffs;
+ }
+
+ Diff get(String field) {
+ return diffs.get(field);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void setDiff(String field, @Nullable Serializable oldValue, @Nullable Serializable newValue) {
+ Diff diff = diffs.get(field);
+ if (diff == null) {
+ diff = new Diff(oldValue, newValue);
+ diffs.put(field, diff);
+ } else {
+ diff.setNewValue(newValue);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ boolean notFirst = false;
+ for (Map.Entry<String, Diff> entry : diffs.entrySet()) {
+ if (notFirst) {
+ sb.append(',');
+ } else {
+ notFirst = true;
+ }
+ sb.append(entry.getKey());
+ sb.append('=');
+ sb.append(entry.getValue().toString());
+ }
+ return sb.toString();
+ }
+
+ public static FieldDiffs parse(@Nullable String s) {
+ FieldDiffs diffs = new FieldDiffs();
+ if (!Strings.isNullOrEmpty(s)) {
+ Iterable<String> fields = FIELDS_SPLITTER.split(s);
+ for (String field : fields) {
+ String[] keyValues = field.split("=");
+ if (keyValues.length == 2) {
+ String[] values = keyValues[1].split("\\|");
+ String oldValue = "";
+ String newValue = "";
+ if (values.length > 0) {
+ oldValue = Strings.nullToEmpty(values[0]);
+ }
+ if (values.length > 1) {
+ newValue = Strings.nullToEmpty(values[1]);
+ }
+ diffs.setDiff(keyValues[0], oldValue, newValue);
+ }
+ }
+ }
+ return diffs;
+ }
+
+ public static class Diff<T extends Serializable> implements Serializable {
+ private T oldValue, newValue;
+
+ public Diff(@Nullable T oldValue, @Nullable T newValue) {
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ }
+
+ public T oldValue() {
+ return oldValue;
+ }
+
+ public T newValue() {
+ return newValue;
+ }
+
+ void setNewValue(T t) {
+ this.newValue = t;
+ }
+
+ @Override
+ public String toString() {
+ //TODO escape , and | characters
+ StringBuilder sb = new StringBuilder();
+ if (oldValue != null) {
+ sb.append(oldValue.toString());
+ }
+ sb.append('|');
+ if (newValue != null) {
+ sb.append(newValue.toString());
+ }
+ return sb.toString();
+ }
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueChange.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueChange.java
index d64c6b98411..527965cb442 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/IssueChange.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueChange.java
@@ -19,49 +19,37 @@
*/
package org.sonar.core.issue;
-import com.google.common.collect.Maps;
-
-import javax.annotation.Nullable;
import java.io.Serializable;
-import java.util.Map;
-
-public class IssueChange implements Serializable {
-
- public static class Diff<T extends Serializable> implements Serializable {
- private T before, after;
+import java.util.Date;
- public Diff(@Nullable T before, @Nullable T after) {
- this.before = before;
- this.after = after;
- }
+public abstract class IssueChange implements Serializable {
+ private String userLogin;
+ private Date createdAt, updatedAt;
- public T before() {
- return before;
- }
+ public String userLogin() {
+ return userLogin;
+ }
- public T after() {
- return after;
- }
+ public IssueChange setUserLogin(String s) {
+ this.userLogin = s;
+ return this;
+ }
- void setAfter(T t) {
- this.after = t;
- }
+ public Date createdAt() {
+ return createdAt;
}
- private Map<String, Diff> diffs = Maps.newLinkedHashMap();
+ public IssueChange setCreatedAt(Date d) {
+ this.createdAt = d;
+ return this;
+ }
- public Map<String, Diff> diffs() {
- return diffs;
+ public Date updatedAt() {
+ return updatedAt;
}
- @SuppressWarnings("unchecked")
- public void setDiff(String field, @Nullable Serializable oldValue, @Nullable Serializable newValue) {
- Diff diff = diffs.get(field);
- if (diff == null) {
- diff = new Diff(oldValue, newValue);
- diffs.put(field, diff);
- } else {
- diff.setAfter(newValue);
- }
+ public IssueChange setUpdatedAt(Date d) {
+ this.updatedAt = d;
+ return this;
}
}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueChangeContext.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueChangeContext.java
index a8422d6edd9..440fbff092c 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/IssueChangeContext.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueChangeContext.java
@@ -28,12 +28,12 @@ public class IssueChangeContext implements Serializable {
private String login;
private Date date;
- private boolean automatic;
+ private boolean scan;
- private IssueChangeContext(@Nullable String login, Date date, boolean automatic) {
+ private IssueChangeContext(@Nullable String login, Date date, boolean scan) {
this.login = login;
this.date = date;
- this.automatic = automatic;
+ this.scan = scan;
}
@CheckForNull
@@ -45,8 +45,8 @@ public class IssueChangeContext implements Serializable {
return date;
}
- public boolean automatic() {
- return automatic;
+ public boolean scan() {
+ return scan;
}
public static IssueChangeContext createScan(Date date) {
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueComment.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueComment.java
new file mode 100644
index 00000000000..975c9ae5978
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueComment.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.
+ */
+package org.sonar.core.issue;
+
+import javax.annotation.Nullable;
+import java.util.Date;
+import java.util.UUID;
+
+public class IssueComment extends IssueChange {
+
+ private String key;
+ private String text;
+
+ public String text() {
+ return text;
+ }
+
+ public IssueComment setText(String s) {
+ this.text = s;
+ return this;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public IssueComment setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public static IssueComment create(@Nullable String login, String text) {
+ IssueComment comment = new IssueComment();
+ comment.setKey(UUID.randomUUID().toString());
+ Date now = new Date();
+ comment.setUserLogin(login);
+ comment.setText(text);
+ comment.setCreatedAt(now).setUpdatedAt(now);
+ return comment;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java
index 58341de0ea3..2d5725dda68 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java
@@ -30,18 +30,21 @@ import java.util.Date;
public class IssueUpdater implements BatchComponent, ServerComponent {
- public boolean setSeverity(DefaultIssue issue, String severity) {
+ public boolean setSeverity(DefaultIssue issue, String severity, IssueChangeContext context) {
+ if (issue.manualSeverity()) {
+ throw new IllegalStateException("Severity can't be changed");
+ }
if (!Objects.equal(severity, issue.severity())) {
- issue.setDiff("severity", issue.severity(), severity);
+ issue.setFieldDiff(context, "severity", issue.severity(), severity);
issue.setSeverity(severity);
return true;
}
return false;
}
- public boolean setManualSeverity(DefaultIssue issue, String severity) {
+ public boolean setManualSeverity(DefaultIssue issue, String severity, IssueChangeContext context) {
if (!issue.manualSeverity() || !Objects.equal(severity, issue.severity())) {
- issue.setDiff("severity", issue.severity(), severity);
+ issue.setFieldDiff(context, "severity", issue.severity(), severity);
issue.setSeverity(severity);
issue.setManualSeverity(true);
return true;
@@ -49,62 +52,56 @@ public class IssueUpdater implements BatchComponent, ServerComponent {
return false;
}
- public boolean assign(DefaultIssue issue, @Nullable String assignee) {
+ public boolean assign(DefaultIssue issue, @Nullable String assignee, IssueChangeContext context) {
String sanitizedAssignee = StringUtils.defaultIfBlank(assignee, null);
if (!Objects.equal(sanitizedAssignee, issue.assignee())) {
- issue.setDiff("assignee", issue.assignee(), sanitizedAssignee);
+ issue.setFieldDiff(context, "assignee", issue.assignee(), sanitizedAssignee);
issue.setAssignee(sanitizedAssignee);
return true;
}
return false;
}
- public DefaultIssue setLine(DefaultIssue issue, @Nullable Integer line) {
+ public boolean setLine(DefaultIssue issue, @Nullable Integer line) {
if (!Objects.equal(line, issue.line())) {
issue.setLine(line);
+ return true;
}
- return issue;
+ return false;
}
- public DefaultIssue setResolution(DefaultIssue issue, String resolution) {
+ public boolean setResolution(DefaultIssue issue, String resolution, IssueChangeContext context) {
if (!Objects.equal(resolution, issue.resolution())) {
- issue.setDiff("resolution", issue.resolution(), resolution);
+ issue.setFieldDiff(context, "resolution", issue.resolution(), resolution);
issue.setResolution(resolution);
+ return true;
}
- return issue;
+ return false;
}
- public DefaultIssue setStatus(DefaultIssue issue, String status) {
+ public boolean setStatus(DefaultIssue issue, String status, IssueChangeContext context) {
if (!Objects.equal(status, issue.status())) {
- issue.setDiff("status", issue.status(), status);
+ issue.setFieldDiff(context, "status", issue.status(), status);
issue.setStatus(status);
+ return true;
}
- return issue;
+ return false;
}
- public DefaultIssue setAuthorLogin(DefaultIssue issue, @Nullable String authorLogin) {
- if (!Objects.equal(authorLogin, issue.authorLogin())) {
- issue.setAuthorLogin(authorLogin);
- }
- return issue;
+ public void setAuthorLogin(DefaultIssue issue, @Nullable String authorLogin) {
+ issue.setAuthorLogin(authorLogin);
}
- public DefaultIssue setDescription(DefaultIssue issue, @Nullable String description) {
- if (!Objects.equal(description, issue.description())) {
- if (issue.manual()) {
- issue.setDiff("description", issue.description(), description);
- }
- issue.setDescription(description);
- }
- return issue;
+ public void setDescription(DefaultIssue issue, @Nullable String description) {
+ issue.setDescription(description);
}
- public DefaultIssue setClosedDate(DefaultIssue issue, @Nullable Date date) {
- if (!Objects.equal(date, issue.closedAt())) {
- issue.setDiff("closedDate", issue.closedAt(), date);
- issue.setClosedAt(date);
- }
- return issue;
+ public void addComment(DefaultIssue issue, String text, IssueChangeContext context) {
+ issue.addComment(IssueComment.create(context.login(), text));
+ }
+
+ public void setClosedDate(DefaultIssue issue, @Nullable Date date) {
+ issue.setClosedAt(date);
}
// TODO setAttribute
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/ChangeDtoConverter.java b/sonar-core/src/main/java/org/sonar/core/issue/db/ChangeDtoConverter.java
new file mode 100644
index 00000000000..9403cd08d1d
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/ChangeDtoConverter.java
@@ -0,0 +1,74 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.
+ */
+package org.sonar.core.issue.db;
+
+import com.google.common.collect.Lists;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.core.issue.IssueComment;
+
+import java.util.Date;
+import java.util.List;
+
+class ChangeDtoConverter {
+
+ static final String TYPE_FIELD_CHANGE = "diff";
+ static final String TYPE_COMMENT = "comment";
+
+ static List<IssueChangeDto> toChangeDtos(DefaultIssue issue) {
+ List<IssueChangeDto> dtos = Lists.newArrayList();
+ for (IssueComment comment : issue.newComments()) {
+ dtos.add(commentToDto(issue.key(), comment));
+ }
+ if (issue.diffs() != null) {
+ dtos.add(diffsToDto(issue.key(), issue.diffs()));
+ }
+ return dtos;
+ }
+
+ static IssueChangeDto commentToDto(String issueKey, IssueComment comment) {
+ IssueChangeDto dto = newDto(issueKey);
+ dto.setKey(comment.key());
+ dto.setChangeType(TYPE_COMMENT);
+ dto.setChangeData(comment.text());
+ dto.setUserLogin(comment.userLogin());
+ return dto;
+ }
+
+ static IssueChangeDto diffsToDto(String issueKey, FieldDiffs diffs) {
+ IssueChangeDto dto = newDto(issueKey);
+ dto.setChangeType(TYPE_FIELD_CHANGE);
+ dto.setChangeData(diffs.toString());
+ dto.setUserLogin(diffs.userLogin());
+ return dto;
+ }
+
+ private static IssueChangeDto newDto(String issueKey) {
+ IssueChangeDto dto = new IssueChangeDto();
+ dto.setIssueKey(issueKey);
+
+ // technical dates - do not use the context date
+ Date now = new Date();
+ dto.setCreatedAt(now);
+ dto.setUpdatedAt(new Date());
+ return dto;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java
index 44b90236ea9..42a213d02e8 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java
@@ -25,7 +25,8 @@ import org.sonar.api.BatchComponent;
import org.sonar.api.ServerComponent;
import org.sonar.core.persistence.MyBatis;
-import java.util.Collection;
+import javax.annotation.CheckForNull;
+import java.util.List;
/**
* @since 3.6
@@ -38,28 +39,21 @@ public class IssueChangeDao implements BatchComponent, ServerComponent {
this.mybatis = mybatis;
}
- public void insert(IssueChangeDto issueChangeDto) {
- SqlSession session = mybatis.openSession();
- IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class);
- try {
- mapper.insert(issueChangeDto);
- session.commit();
- } finally {
- MyBatis.closeQuietly(session);
- }
- }
-
- public IssueChangeDto findById(long issueChangeLogId) {
+ @CheckForNull
+ public IssueChangeDto selectById(long id) {
SqlSession session = mybatis.openSession();
try {
IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class);
- return mapper.findById(issueChangeLogId);
+ return mapper.selectById(id);
} finally {
MyBatis.closeQuietly(session);
}
}
- public Collection<IssueChangeDto> selectByIssue(String issueKey) {
+ /**
+ * Issue changes ordered by descending creation date.
+ */
+ public List<IssueChangeDto> selectByIssue(String issueKey) {
SqlSession session = mybatis.openSession();
try {
IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class);
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDto.java
index db1734c00e3..1ab40848704 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDto.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDto.java
@@ -22,6 +22,7 @@ package org.sonar.core.issue.db;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
+import javax.annotation.Nullable;
import java.util.Date;
/**
@@ -30,11 +31,11 @@ import java.util.Date;
public final class IssueChangeDto {
private Long id;
+ private String key;
private String issueKey;
private String userLogin;
private String changeType;
private String changeData;
- private String message;
private Date createdAt;
private Date updatedAt;
@@ -47,6 +48,15 @@ public final class IssueChangeDto {
return this;
}
+ public String getKey() {
+ return key;
+ }
+
+ public IssueChangeDto setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
public String getIssueKey() {
return issueKey;
}
@@ -60,7 +70,7 @@ public final class IssueChangeDto {
return userLogin;
}
- public IssueChangeDto setUserLogin(String userLogin) {
+ public IssueChangeDto setUserLogin(@Nullable String userLogin) {
this.userLogin = userLogin;
return this;
}
@@ -83,15 +93,6 @@ public final class IssueChangeDto {
return this;
}
- public String getMessage() {
- return message;
- }
-
- public IssueChangeDto setMessage(String message) {
- this.message = message;
- return this;
- }
-
public Date getCreatedAt() {
return createdAt;
}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java
index 988c6a41924..077d1f37f40 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java
@@ -20,17 +20,20 @@
package org.sonar.core.issue.db;
-import java.util.Collection;
+import java.util.List;
/**
* @since 3.6
*/
public interface IssueChangeMapper {
- void insert(IssueChangeDto issueChangeDto);
+ void insert(IssueChangeDto dto);
- IssueChangeDto findById(long issueChangeLogId);
+ IssueChangeDto selectById(long id);
- Collection<IssueChangeDto> selectByIssue(String issueUuid);
+ /**
+ * Issue changes ordered by descending creation date.
+ */
+ List<IssueChangeDto> selectByIssue(String issueKey);
}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java
index 75f055a176d..1fb43d57578 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java
@@ -48,35 +48,10 @@ public class IssueDao implements BatchComponent, ServerComponent {
public IssueDao(MyBatis mybatis) {
this.mybatis = mybatis;
+ // FIXME
this.avalailableSorts = getAvalailableSorts();
}
- public void insert(IssueDto issueDto) {
- SqlSession session = mybatis.openSession();
- try {
- // TODO bulk insert
- session.insert("org.sonar.core.issue.db.IssueMapper.insert", issueDto);
- session.commit();
- } finally {
- MyBatis.closeQuietly(session);
- }
- }
-
- public IssueDao update(Collection<IssueDto> issues) {
- SqlSession session = mybatis.openBatchSession();
- try {
- // TODO bulk update
- for (IssueDto issue : issues) {
- session.update("org.sonar.core.issue.db.IssueMapper.update", issue);
- }
- session.commit();
- return this;
-
- } finally {
- MyBatis.closeQuietly(session);
- }
- }
-
public IssueDto selectById(long id) {
SqlSession session = mybatis.openSession();
try {
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStorage.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStorage.java
new file mode 100644
index 00000000000..25872741a9f
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStorage.java
@@ -0,0 +1,90 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.
+ */
+package org.sonar.core.issue.db;
+
+import com.google.common.collect.Lists;
+import org.apache.ibatis.session.SqlSession;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.persistence.MyBatis;
+
+import java.util.Arrays;
+import java.util.List;
+
+public abstract class IssueStorage {
+
+ private static final String MYBATIS_INSERT_CHANGE = "org.sonar.core.issue.db.IssueChangeMapper.insert";
+ private static final String MYBATIS_INSERT_ISSUE = "org.sonar.core.issue.db.IssueMapper.insert";
+ private static final String MYBATIS_UPDATE_ISSUE = "org.sonar.core.issue.db.IssueMapper.update";
+
+ private final MyBatis mybatis;
+ private final RuleFinder ruleFinder;
+
+ protected IssueStorage(MyBatis mybatis, RuleFinder ruleFinder) {
+ this.mybatis = mybatis;
+ this.ruleFinder = ruleFinder;
+ }
+
+ public void save(DefaultIssue issue) {
+ save(Arrays.asList(issue));
+ }
+
+ public void save(Iterable<DefaultIssue> issues) {
+ SqlSession session = mybatis.openSession();
+ try {
+ List<DefaultIssue> conflicts = Lists.newArrayList();
+ for (DefaultIssue issue : issues) {
+ int ruleId = ruleId(issue);
+ int componentId = componentId(issue);
+
+ IssueDto dto = IssueDto.toDto(issue, componentId, ruleId);
+ // TODO set technical created/updated dates
+ if (issue.isNew()) {
+ session.insert(MYBATIS_INSERT_ISSUE, dto);
+ } else /* TODO if hasChanges */ {
+ // TODO manage condition on update date
+ int count = session.update(MYBATIS_UPDATE_ISSUE, dto);
+ if (count < 1) {
+ conflicts.add(issue);
+ }
+ }
+ for (IssueChangeDto changeDto : ChangeDtoConverter.toChangeDtos(issue)) {
+ session.insert(MYBATIS_INSERT_CHANGE, changeDto);
+ }
+ }
+ session.commit();
+ // TODO log and fix conflicts
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ protected abstract int componentId(DefaultIssue issue);
+
+ private int ruleId(Issue issue) {
+ Rule rule = ruleFinder.findByKey(issue.ruleKey());
+ if (rule == null) {
+ throw new IllegalStateException("Rule not found: " + issue.ruleKey());
+ }
+ return rule.getId();
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/workflow/FunctionExecutor.java b/sonar-core/src/main/java/org/sonar/core/issue/workflow/FunctionExecutor.java
index a54badf74cd..097e5cfe5b5 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/workflow/FunctionExecutor.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/workflow/FunctionExecutor.java
@@ -44,7 +44,6 @@ public class FunctionExecutor implements BatchComponent, ServerComponent {
}
static class FunctionContext implements Function.Context {
-
private final IssueUpdater updater;
private final DefaultIssue issue;
private final IssueChangeContext changeContext;
@@ -62,7 +61,7 @@ public class FunctionExecutor implements BatchComponent, ServerComponent {
@Override
public Function.Context setResolution(String s) {
- updater.setResolution(issue, s);
+ updater.setResolution(issue, s, changeContext);
return this;
}
diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml
index 9ed85877273..3dd61b273e1 100644
--- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml
@@ -26,39 +26,42 @@
<sql id="issueChangeColumns">
i.id,
+ i.kee as kee,
i.issue_key as issueKey,
i.user_login as userLogin,
i.change_type as changeType,
i.change_data as changeData,
- i.message as message,
i.created_at as createdAt,
i.updated_at as updatedAt
</sql>
- <insert id="insert" parameterType="IssueChange" useGeneratedKeys="true" keyProperty ="id">
- INSERT INTO issue_changes (issue_key, user_login, change_type, change_data, message, created_at, updated_at)
- VALUES (#{issueKey}, #{userLogin}, #{changeType}, #{changeData}, #{message}, #{createdAt}, #{updatedAt})
+ <insert id="insert" parameterType="IssueChange" useGeneratedKeys="true" keyProperty="id">
+ INSERT INTO issue_changes (kee, issue_key, user_login, change_type, change_data, created_at, updated_at)
+ VALUES (#{key}, #{issueKey}, #{userLogin}, #{changeType}, #{changeData}, #{createdAt}, #{updatedAt})
</insert>
<!-- Oracle -->
- <insert id="insert" databaseId="oracle" parameterType="IssueChange" keyColumn="id" useGeneratedKeys="true" keyProperty ="id">
- <selectKey order="BEFORE" resultType="Long" keyProperty="id" >
+ <insert id="insert" databaseId="oracle" parameterType="IssueChange" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
+ <selectKey order="BEFORE" resultType="Long" keyProperty="id">
select issue_changes_seq.NEXTVAL from DUAL
</selectKey>
- INSERT INTO issue_changes (id, issue_key, user_login, change_type, change_data, message, created_at, updated_at)
- VALUES (#{id}, #{issueKey}, #{userLogin}, #{changeType}, #{changeData}, #{message}, #{createdAt}, #{updatedAt})
+ INSERT INTO issue_changes (id, kee, issue_key, user_login, change_type, change_data, created_at, updated_at)
+ VALUES (#{id}, #{key}, #{issueKey}, #{userLogin}, #{changeType}, #{changeData}, #{createdAt}, #{updatedAt})
</insert>
- <select id="findById" parameterType="long" resultType="IssueChange">
- select <include refid="issueChangeColumns"/>
+ <select id="selectById" parameterType="long" resultType="IssueChange">
+ select
+ <include refid="issueChangeColumns"/>
from issue_changes i
where i.id=#{id}
</select>
<select id="selectByIssue" parameterType="string" resultType="IssueChange">
- select <include refid="issueChangeColumns"/>
+ select
+ <include refid="issueChangeColumns"/>
from issue_changes i
where i.issue_key=#{issueKey}
+ order by i.created_at desc
</select>
</mapper>
diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
index 34f3fd55b10..b7b0e86adbe 100644
--- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
+++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
@@ -518,7 +518,7 @@ CREATE TABLE "GRAPHS" (
CREATE TABLE "ISSUES" (
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
- "KEE" VARCHAR(36) NOT NULL,
+ "KEE" VARCHAR(100) NOT NULL,
"RESOURCE_ID" INTEGER NOT NULL,
"RULE_ID" INTEGER NOT NULL,
"SEVERITY" VARCHAR(10),
@@ -541,11 +541,11 @@ CREATE TABLE "ISSUES" (
CREATE TABLE "ISSUE_CHANGES" (
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
- "ISSUE_KEY" VARCHAR(36) NOT NULL,
+ "KEE" VARCHAR(100),
+ "ISSUE_KEY" VARCHAR(100) NOT NULL,
"USER_LOGIN" VARCHAR(40),
- "CHANGE_TYPE" VARCHAR(50),
- "CHANGE_DATA" VARCHAR(4000),
- "MESSAGE" VARCHAR(16777215),
+ "CHANGE_TYPE" VARCHAR(40),
+ "CHANGE_DATA" VARCHAR(16777215),
"CREATED_AT" TIMESTAMP,
"UPDATED_AT" TIMESTAMP,
);
@@ -677,12 +677,14 @@ CREATE INDEX "MEASURE_FILTER_FAVS_USERID" ON "MEASURE_FILTER_FAVOURITES" ("USER_
CREATE UNIQUE INDEX "GRAPHS_PERSPECTIVES" ON "GRAPHS" ("SNAPSHOT_ID", "PERSPECTIVE");
+CREATE INDEX "ISSUES_KEE" ON "ISSUES" ("KEE");
+
CREATE INDEX "ISSUES_RESOURCE_ID" ON "ISSUES" ("RESOURCE_ID");
CREATE INDEX "INDEX_ACTION_PLANS_ISSUES_ON_ACTION_PLAN_ID" ON "ACTION_PLANS_ISSUES" ("ACTION_PLAN_ID");
CREATE INDEX "INDEX_ACTION_PLANS_ISSUES_ON_ISSUE_ID" ON "ACTION_PLANS_ISSUES" ("ISSUE_ID");
---CREATE INDEX "ISSUES_RULE_ID" ON "ISSUES" ("RULE_ID");
+CREATE INDEX "ISSUE_CHANGES_ISSUE_KEY" ON "ISSUE_CHANGES" ("ISSUE_KEY");
---CREATE INDEX "SNAPSHOT_DATA_SNAPSHOT_ID" ON "SNAPSHOT_DATA" ("SNAPSHOT_ID");
+CREATE INDEX "ISSUE_CHANGES_KEE" ON "ISSUE_CHANGES" ("KEE");
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java
index 64e5970e8fb..1ac246e0db3 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java
@@ -19,11 +19,13 @@
*/
package org.sonar.core.issue;
+import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
+import static org.fest.assertions.MapAssert.entry;
public class DefaultIssueTest {
@@ -41,6 +43,20 @@ public class DefaultIssueTest {
}
@Test
+ public void setAttributes_should_not_clear_existing_values() throws Exception {
+ issue.setAttributes(ImmutableMap.of("1", "one"));
+ assertThat(issue.attribute("1")).isEqualTo("one");
+
+ issue.setAttributes(ImmutableMap.of("2", "two"));
+ assertThat(issue.attributes()).hasSize(2);
+ assertThat(issue.attributes()).includes(entry("1", "one"), entry("2", "two"));
+
+ issue.setAttributes(null);
+ assertThat(issue.attributes()).hasSize(2);
+ assertThat(issue.attributes()).includes(entry("1", "one"), entry("2", "two"));
+ }
+
+ @Test
public void should_fail_on_empty_status() {
try {
issue.setStatus("");
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/FieldDiffsTest.java b/sonar-core/src/test/java/org/sonar/core/issue/FieldDiffsTest.java
new file mode 100644
index 00000000000..08d436c7f36
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/issue/FieldDiffsTest.java
@@ -0,0 +1,115 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.
+ */
+package org.sonar.core.issue;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class FieldDiffsTest {
+
+ FieldDiffs diffs = new FieldDiffs();
+
+ @Test
+ public void diffs_should_be_empty_by_default() throws Exception {
+ assertThat(diffs.diffs()).isEmpty();
+ }
+
+ @Test
+ public void test_diff() throws Exception {
+ diffs.setDiff("severity", "BLOCKER", "INFO");
+ diffs.setDiff("resolution", "OPEN", "FIXED");
+
+ assertThat(diffs.diffs()).hasSize(2);
+
+ FieldDiffs.Diff diff = diffs.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("INFO");
+
+ diff = diffs.diffs().get("resolution");
+ assertThat(diff.oldValue()).isEqualTo("OPEN");
+ assertThat(diff.newValue()).isEqualTo("FIXED");
+ }
+
+ @Test
+ public void should_keep_old_value() throws Exception {
+ diffs.setDiff("severity", "BLOCKER", "INFO");
+ diffs.setDiff("severity", null, "MAJOR");
+ FieldDiffs.Diff diff = diffs.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("MAJOR");
+ }
+
+ @Test
+ public void test_toString() throws Exception {
+ diffs.setDiff("severity", "BLOCKER", "INFO");
+ diffs.setDiff("resolution", "OPEN", "FIXED");
+
+ assertThat(diffs.toString()).isEqualTo("severity=BLOCKER|INFO,resolution=OPEN|FIXED");
+ }
+
+ @Test
+ public void test_toString_with_null_values() throws Exception {
+ diffs.setDiff("severity", null, "INFO");
+ diffs.setDiff("resolution", "OPEN", null);
+
+ assertThat(diffs.toString()).isEqualTo("severity=|INFO,resolution=OPEN|");
+ }
+
+ @Test
+ public void test_parse() throws Exception {
+ diffs = FieldDiffs.parse("severity=BLOCKER|INFO,resolution=OPEN|FIXED");
+ assertThat(diffs.diffs()).hasSize(2);
+
+ FieldDiffs.Diff diff = diffs.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("INFO");
+
+ diff = diffs.diffs().get("resolution");
+ assertThat(diff.oldValue()).isEqualTo("OPEN");
+ assertThat(diff.newValue()).isEqualTo("FIXED");
+ }
+
+ @Test
+ public void test_parse_empty_values() throws Exception {
+ diffs = FieldDiffs.parse("severity=|INFO,resolution=OPEN|");
+ assertThat(diffs.diffs()).hasSize(2);
+
+ FieldDiffs.Diff diff = diffs.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("");
+ assertThat(diff.newValue()).isEqualTo("INFO");
+
+ diff = diffs.diffs().get("resolution");
+ assertThat(diff.oldValue()).isEqualTo("OPEN");
+ assertThat(diff.newValue()).isEqualTo("");
+ }
+
+ @Test
+ public void test_parse_null() throws Exception {
+ diffs = FieldDiffs.parse(null);
+ assertThat(diffs.diffs()).isEmpty();
+ }
+
+ @Test
+ public void test_parse_empty() throws Exception {
+ diffs = FieldDiffs.parse("");
+ assertThat(diffs.diffs()).isEmpty();
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/IssueChangeContextTest.java b/sonar-core/src/test/java/org/sonar/core/issue/IssueChangeContextTest.java
new file mode 100644
index 00000000000..101b5fd1472
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/issue/IssueChangeContextTest.java
@@ -0,0 +1,48 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.
+ */
+package org.sonar.core.issue;
+
+import org.junit.Test;
+
+import java.util.Date;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class IssueChangeContextTest {
+ @Test
+ public void test_scan_context() throws Exception {
+ Date now = new Date();
+ IssueChangeContext context = IssueChangeContext.createScan(now);
+
+ assertThat(context.scan()).isTrue();
+ assertThat(context.login()).isNull();
+ assertThat(context.date()).isEqualTo(now);
+ }
+
+ @Test
+ public void test_end_user_context() throws Exception {
+ Date now = new Date();
+ IssueChangeContext context = IssueChangeContext.createUser(now, "emmerik");
+
+ assertThat(context.scan()).isFalse();
+ assertThat(context.login()).isEqualTo("emmerik");
+ assertThat(context.date()).isEqualTo(now);
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java b/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java
new file mode 100644
index 00000000000..478121c2e45
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java
@@ -0,0 +1,196 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.
+ */
+package org.sonar.core.issue;
+
+import org.junit.Test;
+
+import java.util.Date;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class IssueUpdaterTest {
+
+ IssueUpdater updater = new IssueUpdater();
+ DefaultIssue issue = new DefaultIssue();
+ IssueChangeContext context = IssueChangeContext.createUser(new Date(), "emmerik");
+
+ @Test
+ public void should_assign() throws Exception {
+ boolean updated = updater.assign(issue, "emmerik", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.assignee()).isEqualTo("emmerik");
+ FieldDiffs.Diff diff = issue.diffs().get("assignee");
+ assertThat(diff.oldValue()).isNull();
+ assertThat(diff.newValue()).isEqualTo("emmerik");
+ }
+
+ @Test
+ public void should_unassign() throws Exception {
+ issue.setAssignee("morgan");
+ boolean updated = updater.assign(issue, null, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.assignee()).isNull();
+ FieldDiffs.Diff diff = issue.diffs().get("assignee");
+ assertThat(diff.oldValue()).isEqualTo("morgan");
+ assertThat(diff.newValue()).isNull();
+ }
+
+ @Test
+ public void should_change_assignee() throws Exception {
+ issue.setAssignee("morgan");
+ boolean updated = updater.assign(issue, "emmerik", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.assignee()).isEqualTo("emmerik");
+ FieldDiffs.Diff diff = issue.diffs().get("assignee");
+ assertThat(diff.oldValue()).isEqualTo("morgan");
+ assertThat(diff.newValue()).isEqualTo("emmerik");
+ }
+
+ @Test
+ public void should_not_change_assignee() throws Exception {
+ issue.setAssignee("morgan");
+ boolean updated = updater.assign(issue, "morgan", context);
+ assertThat(updated).isFalse();
+ assertThat(issue.diffs()).isNull();
+ }
+
+
+ @Test
+ public void should_set_severity() throws Exception {
+ boolean updated = updater.setSeverity(issue, "BLOCKER", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.severity()).isEqualTo("BLOCKER");
+ assertThat(issue.manualSeverity()).isFalse();
+
+ FieldDiffs.Diff diff = issue.diffs().get("severity");
+ assertThat(diff.oldValue()).isNull();
+ assertThat(diff.newValue()).isEqualTo("BLOCKER");
+ }
+
+ @Test
+ public void should_update_severity() throws Exception {
+ issue.setSeverity("BLOCKER");
+ boolean updated = updater.setSeverity(issue, "MINOR", context);
+
+ assertThat(updated).isTrue();
+ assertThat(issue.severity()).isEqualTo("MINOR");
+ FieldDiffs.Diff diff = issue.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("MINOR");
+ }
+
+ @Test
+ public void should_not_change_severity() throws Exception {
+ issue.setSeverity("MINOR");
+ boolean updated = updater.setSeverity(issue, "MINOR", context);
+ assertThat(updated).isFalse();
+ assertThat(issue.diffs()).isNull();
+ }
+
+ @Test
+ public void should_not_revert_manual_severity() throws Exception {
+ issue.setSeverity("MINOR").setManualSeverity(true);
+ try {
+ updater.setSeverity(issue, "MAJOR", context);
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("Severity can't be changed");
+ }
+ }
+
+ @Test
+ public void should_set_manual_severity() throws Exception {
+ issue.setSeverity("BLOCKER");
+ boolean updated = updater.setManualSeverity(issue, "MINOR", context);
+
+ assertThat(updated).isTrue();
+ assertThat(issue.severity()).isEqualTo("MINOR");
+ assertThat(issue.manualSeverity()).isTrue();
+ FieldDiffs.Diff diff = issue.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("MINOR");
+ }
+
+ @Test
+ public void should_not_change_manual_severity() throws Exception {
+ issue.setSeverity("MINOR").setManualSeverity(true);
+ boolean updated = updater.setManualSeverity(issue, "MINOR", context);
+ assertThat(updated).isFalse();
+ assertThat(issue.diffs()).isNull();
+ }
+
+ @Test
+ public void should_set_line() throws Exception {
+ boolean updated = updater.setLine(issue, 123);
+ assertThat(updated).isTrue();
+ assertThat(issue.line()).isEqualTo(123);
+
+ // do not save change
+ assertThat(issue.diffs()).isNull();
+ }
+
+ @Test
+ public void should_not_change_line() throws Exception {
+ issue.setLine(123);
+ boolean updated = updater.setLine(issue, 123);
+ assertThat(updated).isFalse();
+ assertThat(issue.line()).isEqualTo(123);
+ assertThat(issue.diffs()).isNull();
+ }
+
+ @Test
+ public void should_set_resolution() throws Exception {
+ boolean updated = updater.setResolution(issue, "OPEN", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.resolution()).isEqualTo("OPEN");
+
+ FieldDiffs.Diff diff = issue.diffs().get("resolution");
+ assertThat(diff.oldValue()).isNull();
+ assertThat(diff.newValue()).isEqualTo("OPEN");
+ }
+
+ @Test
+ public void should_not_change_resolution() throws Exception {
+ issue.setResolution("FIXED");
+ boolean updated = updater.setResolution(issue, "FIXED", context);
+ assertThat(updated).isFalse();
+ assertThat(issue.resolution()).isEqualTo("FIXED");
+ assertThat(issue.diffs()).isNull();
+ }
+
+ @Test
+ public void should_set_status() throws Exception {
+ boolean updated = updater.setStatus(issue, "OPEN", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.status()).isEqualTo("OPEN");
+
+ FieldDiffs.Diff diff = issue.diffs().get("status");
+ assertThat(diff.oldValue()).isNull();
+ assertThat(diff.newValue()).isEqualTo("OPEN");
+ }
+
+ @Test
+ public void should_not_change_status() throws Exception {
+ issue.setStatus("CLOSED");
+ boolean updated = updater.setStatus(issue, "CLOSED", context);
+ assertThat(updated).isFalse();
+ assertThat(issue.status()).isEqualTo("CLOSED");
+ assertThat(issue.diffs()).isNull();
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/ChangeDtoConverterTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/ChangeDtoConverterTest.java
new file mode 100644
index 00000000000..dec00ee6cf9
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/issue/db/ChangeDtoConverterTest.java
@@ -0,0 +1,78 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.
+ */
+package org.sonar.core.issue.db;
+
+import org.junit.Test;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.core.issue.IssueComment;
+
+import java.util.Date;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ChangeDtoConverterTest {
+ @Test
+ public void testToChangeDtos() throws Exception {
+ IssueComment comment = IssueComment.create("emmerik", "the comment");
+
+ IssueChangeDto dto = ChangeDtoConverter.commentToDto("ABCDE", comment);
+
+ assertThat(dto.getChangeData()).isEqualTo("the comment");
+ assertThat(dto.getChangeType()).isEqualTo("comment");
+ assertThat(dto.getCreatedAt()).isNotNull();
+ assertThat(dto.getUpdatedAt()).isNotNull();
+ assertThat(dto.getIssueKey()).isEqualTo("ABCDE");
+ assertThat(dto.getUserLogin()).isEqualTo("emmerik");
+ }
+
+ @Test
+ public void testToDiffsDtos() throws Exception {
+ FieldDiffs diffs = new FieldDiffs();
+ diffs.setDiff("severity", "INFO", "BLOCKER");
+ diffs.setUserLogin("emmerik");
+
+ IssueChangeDto dto = ChangeDtoConverter.diffsToDto("ABCDE", diffs);
+
+ assertThat(dto.getChangeData()).isEqualTo("severity=INFO|BLOCKER");
+ assertThat(dto.getChangeType()).isEqualTo("diff");
+ assertThat(dto.getCreatedAt()).isNotNull();
+ assertThat(dto.getUpdatedAt()).isNotNull();
+ assertThat(dto.getIssueKey()).isEqualTo("ABCDE");
+ assertThat(dto.getUserLogin()).isEqualTo("emmerik");
+ }
+
+ @Test
+ public void test_toChangeDtos() throws Exception {
+ IssueChangeContext context = IssueChangeContext.createUser(new Date(), "emmerik");
+ IssueComment comment = IssueComment.create("emmerik", "the comment");
+ DefaultIssue issue = new DefaultIssue();
+ issue.setFieldDiff(context, "severity", "INFO", "BLOCKER");
+ issue.addComment(comment);
+
+ List<IssueChangeDto> changeDtos = ChangeDtoConverter.toChangeDtos(issue);
+ assertThat(changeDtos).hasSize(2);
+ assertThat(changeDtos.get(0).getChangeType()).isEqualTo("comment");
+ assertThat(changeDtos.get(1).getChangeType()).isEqualTo("diff");
+
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java
index 437ec98973a..c3c23a7c4a1 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java
@@ -21,12 +21,10 @@ package org.sonar.core.issue.db;
import org.junit.Before;
import org.junit.Test;
-import org.sonar.core.issue.db.IssueChangeDao;
-import org.sonar.core.issue.db.IssueChangeDto;
import org.sonar.core.persistence.AbstractDaoTestCase;
-import java.util.Collection;
import java.util.Date;
+import java.util.List;
import static org.fest.assertions.Assertions.assertThat;
@@ -40,46 +38,27 @@ public class IssueChangeDaoTest extends AbstractDaoTestCase {
}
@Test
- public void should_insert() {
- setupData("insert");
-
- IssueChangeDto dto = new IssueChangeDto();
- dto.setIssueKey("100");
- dto.setUserLogin("arthur");
- dto.setChangeType("type");
- dto.setChangeData("data");
- dto.setMessage("some message");
-
- Date today = new Date();
- dto.setCreatedAt(today);
- dto.setUpdatedAt(today);
-
- dao.insert(dto);
-
- checkTables("insert", new String[]{"id", "created_at", "updated_at"}, "issue_changes");
- }
-
- @Test
- public void should_find_by_id() {
+ public void should_select_by_id() {
setupData("shared");
- IssueChangeDto dto = dao.findById(100L);
+ IssueChangeDto dto = dao.selectById(100L);
assertThat(dto.getId()).isEqualTo(100L);
- assertThat(dto.getIssueKey()).isEqualTo("100");
+ assertThat(dto.getIssueKey()).isEqualTo("1000");
assertThat(dto.getUserLogin()).isEqualTo("arthur");
- assertThat(dto.getChangeType()).isEqualTo("type");
- assertThat(dto.getChangeData()).isEqualTo("data");
- assertThat(dto.getMessage()).isEqualTo("some message");
- assertThat(dto.getCreatedAt()).isNull();
- assertThat(dto.getUpdatedAt()).isNull();
+ assertThat(dto.getChangeType()).isEqualTo("comment");
+ assertThat(dto.getChangeData()).isEqualTo("this is a comment");
+ assertThat(dto.getCreatedAt()).isNotNull();
+ assertThat(dto.getUpdatedAt()).isNotNull();
}
@Test
public void should_select_by_issue() {
setupData("shared");
- Collection<IssueChangeDto> dtoList = dao.selectByIssue("100");
- assertThat(dtoList).hasSize(2);
+ List<IssueChangeDto> ordered = dao.selectByIssue("1000");
+ assertThat(ordered).hasSize(2);
+ assertThat(ordered.get(0).getId()).isEqualTo(101);
+ assertThat(ordered.get(1).getId()).isEqualTo(100);
}
}
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java
index e988b9c2bd9..26975e3251e 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java
@@ -45,54 +45,6 @@ public class IssueDaoTest extends AbstractDaoTestCase {
}
@Test
- public void should_insert() {
- setupData("insert");
-
- IssueDto issueDto = new IssueDto();
- issueDto.setKey("100");
- issueDto.setResourceId(400);
- issueDto.setRuleId(12);
- issueDto.setSeverity("BLOCKER");
- issueDto.setLine(200);
- issueDto.setStatus("OPEN");
- issueDto.setAssignee("user");
- issueDto.setDescription("the description");
- issueDto.setCost(10.0);
- issueDto.setChecksum("checksum");
- issueDto.setAuthorLogin("arthur");
-
- Date today = new Date();
- issueDto.setCreatedAt(today);
- issueDto.setUpdatedAt(today);
- issueDto.setClosedAt(today);
-
- dao.insert(issueDto);
-
- checkTables("insert", new String[]{"id", "created_at", "updated_at", "closed_at"}, "issues");
- }
-
- @Test
- public void update() {
- setupData("shared", "update");
- Collection<IssueDto> issues = newArrayList(dao.selectById(100L));
- IssueDto issue = issues.iterator().next();
- issue.setLine(1000);
- issue.setResolution("NEW_RESOLUTION");
- issue.setStatus("NEW_STATUS");
- issue.setSeverity("NEW_SEV");
- issue.setAssignee("new_user");
- issue.setManualSeverity(true);
- issue.setManualIssue(false);
- issue.setCreatedAt(DateUtils.parseDate("2012-05-18"));
- issue.setUpdatedAt(DateUtils.parseDate("2012-07-01"));
- issue.setAttributes("big=bang");
-
- dao.update(issues);
-
- checkTables("update", "issues");
- }
-
- @Test
public void should_select_by_id() {
setupData("shared", "should_select_by_id");
IssueDto issue = dao.selectById(100L);
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStorageTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStorageTest.java
new file mode 100644
index 00000000000..c49b404fbbf
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStorageTest.java
@@ -0,0 +1,121 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.
+ */
+package org.sonar.core.issue.db;
+
+import org.junit.Ignore;
+import org.junit.Test;
+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.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.core.issue.IssueComment;
+import org.sonar.core.persistence.AbstractDaoTestCase;
+import org.sonar.core.persistence.MyBatis;
+
+import java.util.Collection;
+import java.util.Date;
+
+public class IssueStorageTest extends AbstractDaoTestCase {
+ @Test
+ public void should_insert_new_issues() throws Exception {
+ FakeSaver saver = new FakeSaver(getMyBatis(), new FakeRuleFinder());
+
+ IssueChangeContext context = IssueChangeContext.createUser(new Date(), "emmerik");
+ IssueComment comment = IssueComment.create("emmerik", "the comment");
+ // override generated key
+ comment.setKey("FGHIJ");
+
+ DefaultIssue issue = new DefaultIssue();
+ issue.setKey("ABCDE");
+ issue.setRuleKey(RuleKey.of("squid", "AvoidCycle"));
+ issue.setLine(5000);
+ issue.setNew(true);
+ issue.setFieldDiff(context, "severity", "INFO", "BLOCKER");
+ issue.setUserLogin("emmerik");
+ issue.setResolution("OPEN").setStatus("OPEN").setSeverity("BLOCKER");
+ issue.setAttribute("foo", "bar");
+ issue.addComment(comment);
+
+ saver.save(issue);
+
+ checkTables("should_insert_new_issues", new String[]{"id", "created_at", "updated_at", "closed_at"}, "issues", "issue_changes");
+ }
+
+ @Ignore("TODO")
+ @Test
+ public void should_update_issues() throws Exception {
+
+ }
+
+ @Ignore("TODO")
+ @Test
+ public void should_fail_if_unknown_rule() throws Exception {
+
+ }
+
+ @Ignore("TODO")
+ @Test
+ public void should_fail_if_unknown_component() throws Exception {
+
+ }
+
+ static class FakeSaver extends IssueStorage {
+ protected FakeSaver(MyBatis mybatis, RuleFinder ruleFinder) {
+ super(mybatis, ruleFinder);
+ }
+
+ @Override
+ protected int componentId(DefaultIssue issue) {
+ return 100;
+ }
+ }
+
+ static class FakeRuleFinder implements RuleFinder {
+
+ @Override
+ public Rule findById(int ruleId) {
+ return null;
+ }
+
+ @Override
+ public Rule findByKey(String repositoryKey, String key) {
+ return null;
+ }
+
+ @Override
+ public Rule findByKey(RuleKey key) {
+ Rule rule = new Rule().setRepositoryKey(key.repository()).setKey(key.rule());
+ rule.setId(200);
+ return rule;
+ }
+
+ @Override
+ public Rule find(RuleQuery query) {
+ return null;
+ }
+
+ @Override
+ public Collection<Rule> findAll(RuleQuery query) {
+ return null;
+ }
+ }
+}
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert-result.xml
deleted file mode 100644
index cf0a89b4610..00000000000
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert-result.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<dataset>
-
- <issue_changes
- issue_key="100"
- user_login="arthur"
- change_type="type"
- change_data="data"
- message="some message"
- />
-
-</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert.xml
deleted file mode 100644
index 871dedcb5e9..00000000000
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/insert.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<dataset>
-
-</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/shared.xml
index e3e9559f8ca..739f6c802df 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/shared.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/shared.xml
@@ -2,24 +2,22 @@
<issue_changes
id="100"
- issue_key="100"
+ issue_key="1000"
user_login="arthur"
- change_type="type"
- change_data="data"
- message="some message"
- created_at="[null]"
- updated_at="[null]"
+ change_type="comment"
+ change_data="this is a comment"
+ created_at="2013-01-01"
+ updated_at="2013-01-01"
/>
<issue_changes
id="101"
- issue_key="100"
+ issue_key="1000"
user_login="arthur"
- change_type="type"
- change_data="data"
- message="some message"
- created_at="[null]"
- updated_at="[null]"
+ change_type="fields"
+ change_data="severity:MAJOR,BLOCKER"
+ created_at="2013-02-02"
+ updated_at="2013-02-02"
/>
</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/insert-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/insert-result.xml
deleted file mode 100644
index 5868688a27b..00000000000
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/insert-result.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<dataset>
-
- <issues
- kee="100"
- resource_id="400"
- rule_id="12"
- severity="BLOCKER"
- manual_severity="[false]"
- manual_issue="[false]"
- description="the description"
- line="200"
- cost="10.0"
- status="OPEN"
- resolution="[null]"
- checksum="checksum"
- user_login="[null]"
- assignee_login="user"
- author_login="arthur"
- attributes="[null]"
- />
-
-</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/insert.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/insert.xml
deleted file mode 100644
index 871dedcb5e9..00000000000
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/insert.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<dataset>
-
-</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/update-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/update-result.xml
deleted file mode 100644
index 0574a4c9d88..00000000000
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/update-result.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<dataset>
-
- <issues
- id="100"
- kee="100"
- resource_id="400"
- rule_id="500"
- severity="NEW_SEV"
- manual_severity="[true]"
- manual_issue="[false]"
- description="[null]"
- line="1000"
- cost="[null]"
- status="NEW_STATUS"
- resolution="NEW_RESOLUTION"
- checksum="[null]"
- user_login="user"
- assignee_login="new_user"
- author_login="[null]"
- attributes="big=bang"
- created_at="2012-05-18"
- updated_at="2012-07-01"
- closed_at="[null]"
- />
-
-</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/update.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/update.xml
deleted file mode 100644
index d701d8d68c3..00000000000
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/update.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<dataset>
-
- <issues
- id="100"
- kee="100"
- resource_id="400"
- rule_id="500"
- severity="BLOCKER"
- manual_severity="[false]"
- manual_issue="[false]"
- description="[null]"
- line="200"
- cost="[null]"
- status="OPEN"
- resolution="RESOLVE"
- checksum="[null]"
- user_login="user"
- assignee_login="user"
- author_login="[null]"
- attributes="prop1=foo;prop2=bar"
- created_at="[null]"
- updated_at="[null]"
- closed_at="[null]"
- />
-
-</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml
new file mode 100644
index 00000000000..24140048336
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml
@@ -0,0 +1,10 @@
+<dataset>
+ <issues id="1" kee="ABCDE" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee_login="[null]" author_login="[null]" checksum="[null]" closed_at="[null]"
+ cost="[null]" created_at="[null]" description="[null]" line="5000"
+ manual_issue="[false]" resource_id="100" rule_id="200" updated_at="[null]" user_login="emmerik"
+ attributes="foo=bar" />
+
+ <issue_changes id="1" kee="FGHIJ" issue_key="ABCDE" change_type="comment" user_login="emmerik" change_data="the comment" created_at="[null]" updated_at="[null]" />
+ <issue_changes id="2" kee="[null]" issue_key="ABCDE" change_type="diff" user_login="emmerik" change_data="severity=INFO|BLOCKER" created_at="[null]" updated_at="[null]" />
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml
index f603e893826..1c4b7929732 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml
@@ -27,7 +27,7 @@
<issues id="1" kee="ABCDE" resource_id="1" status="CLOSED" resolution="[null]" created_at="[null]" line="200" severity="BLOCKER"
user_login="perceval" assignee_login="arthur" rule_id="500" manual_issue="[true]" manual_severity="[false]" description="[null]"/>
- <issue_changes id="1" issue_key="ABCDE" created_at="[null]" updated_at="[null]" user_login="admin" message="abc"/>
+ <issue_changes id="1" kee="ABDA" issue_key="ABCDE" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="this is a comment"/>
<authors id="1" person_id="1" login="tartanpion" created_at="[null]" updated_at="[null]" />
<authors id="2" person_id="1" login="fanfoue" created_at="[null]" updated_at="[null]" />
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml
index 3df0470a044..611e020fda0 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml
@@ -39,7 +39,7 @@
<issues id="2" kee="ABCDF" resource_id="1" status="CLOSED" resolution="[null]" created_at="[null]" line="200" severity="BLOCKER"
user_login="perceval" assignee_login="arthur" rule_id="500" manual_issue="[true]" manual_severity="[false]" description="[null]"/>
- <issue_changes id="1" issue_key="ABCDF" created_at="[null]" updated_at="[null]" user_login="admin" message="abc"/>
+ <issue_changes id="1" kee="[null]" issue_key="ABCDF" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
<!-- modules -->
<projects id="2" enabled="[true]" root_id="1"