int resourceId = currentSnapshot.getResourceId();
int snapshotId = currentSnapshot.getId();
- closeReviews(resource, resourceId, snapshotId);
- reopenReviews(resource, resourceId);
+ closeReviewsOnResolvedViolations(resource, resourceId, snapshotId);
+ reopenReviewsOnUnresolvedViolations(resource, resourceId);
+
if (ResourceUtils.isRootProject(resource)) {
closeReviewsForDeletedResources(resourceId, currentSnapshot.getId());
}
/**
* Close reviews for which violations have been fixed.
*/
- protected int closeReviews(Resource resource, int resourceId, int snapshotId) {
- String conditions = " WHERE status!='CLOSED' AND resource_id=" + resourceId
- + " AND manual_violation=:manualViolation AND rule_failure_permanent_id NOT IN " + "(SELECT permanent_id FROM rule_failures WHERE snapshot_id=" + snapshotId
- + " AND permanent_id IS NOT NULL)";
+ protected int closeReviewsOnResolvedViolations(Resource resource, int resourceId, int snapshotId) {
+ String conditions = " WHERE resource_id=" + resourceId + " AND "
+ + "( "
+ + " (manual_violation=:automaticViolation AND status!='CLOSED' AND rule_failure_permanent_id NOT IN " + "(SELECT permanent_id FROM rule_failures WHERE snapshot_id=" + snapshotId + " AND permanent_id IS NOT NULL))"
+ + " OR "
+ + " (manual_violation=:manualViolation AND status='RESOLVED')"
+ + ")";
List<Review> reviews = databaseSession.getEntityManager().createNativeQuery("SELECT * FROM reviews " + conditions, Review.class)
- .setParameter("manualViolation", false)
- .getResultList();
+ .setParameter("automaticViolation", false)
+ .setParameter("manualViolation", true)
+ .getResultList();
+
for (Review review : reviews) {
notifyClosed(resource, review);
}
+
int rowUpdated = databaseSession.createNativeQuery("UPDATE reviews SET status='CLOSED', updated_at=CURRENT_TIMESTAMP" + conditions)
- .setParameter("manualViolation", false)
- .executeUpdate();
- LOG.debug("- {} reviews set to 'closed' on resource #{}", rowUpdated, resourceId);
+ .setParameter("automaticViolation", false)
+ .setParameter("manualViolation", true)
+ .executeUpdate();
+ if (rowUpdated > 0) {
+ LOG.debug("- {} reviews closed on #{}", rowUpdated, resourceId);
+ }
return rowUpdated;
}
/**
* Reopen reviews that had been set to resolved but for which the violation is still here.
+ * Manual violations are ignored.
*/
- protected int reopenReviews(Resource resource, int resourceId) {
- String conditions = " WHERE status='RESOLVED' AND resolution<>'FALSE-POSITIVE' AND resource_id=" + resourceId;
- List<Review> reviews = databaseSession.getEntityManager().createNativeQuery("SELECT * FROM reviews " + conditions, Review.class).getResultList();
+ protected int reopenReviewsOnUnresolvedViolations(Resource resource, int resourceId) {
+ String conditions = " WHERE status='RESOLVED' AND resolution<>'FALSE-POSITIVE' AND manual_violation=:manualViolation AND resource_id=" + resourceId;
+ List<Review> reviews = databaseSession.getEntityManager().createNativeQuery("SELECT * FROM reviews " + conditions, Review.class)
+ .setParameter("manualViolation", false)
+ .getResultList();
for (Review review : reviews) {
notifyReopened(resource, review);
}
- int rowUpdated = databaseSession.createNativeQuery("UPDATE reviews SET status='REOPENED', resolution=NULL, updated_at=CURRENT_TIMESTAMP" + conditions).executeUpdate();
- LOG.debug("- {} reviews set to 'reopened' on resource #{}", rowUpdated, resourceId);
+
+ int rowUpdated = databaseSession.createNativeQuery("UPDATE reviews SET status='REOPENED', resolution=NULL, updated_at=CURRENT_TIMESTAMP" + conditions)
+ .setParameter("manualViolation", false)
+ .executeUpdate();
+ if (rowUpdated > 0) {
+ LOG.debug("- {} reviews reopened on #{}", rowUpdated, resourceId);
+ }
return rowUpdated;
}
*/
protected int closeReviewsForDeletedResources(int projectId, int projectSnapshotId) {
String conditions = " WHERE status!='CLOSED' AND project_id=" + projectId
- + " AND resource_id IN ( SELECT prev.project_id FROM snapshots prev WHERE prev.root_project_id=" + projectId
- + " AND prev.islast=? AND NOT EXISTS ( SELECT cur.id FROM snapshots cur WHERE cur.root_snapshot_id=" + projectSnapshotId
- + " AND cur.created_at > prev.created_at AND cur.root_project_id=" + projectId + " AND cur.project_id=prev.project_id ) )";
+ + " AND resource_id IN ( SELECT prev.project_id FROM snapshots prev WHERE prev.root_project_id=" + projectId
+ + " AND prev.islast=? AND NOT EXISTS ( SELECT cur.id FROM snapshots cur WHERE cur.root_snapshot_id=" + projectSnapshotId
+ + " AND cur.created_at > prev.created_at AND cur.root_project_id=" + projectId + " AND cur.project_id=prev.project_id ) )";
List<Review> reviews = databaseSession.getEntityManager().createNativeQuery("SELECT * FROM reviews " + conditions, Review.class)
- .setParameter(1, Boolean.TRUE)
- .getResultList();
+ .setParameter(1, Boolean.TRUE)
+ .getResultList();
for (Review review : reviews) {
notifyClosed(null, review);
}
int rowUpdated = databaseSession.createNativeQuery("UPDATE reviews SET status='CLOSED', updated_at=CURRENT_TIMESTAMP" + conditions)
- .setParameter(1, Boolean.TRUE)
- .executeUpdate();
+ .setParameter(1, Boolean.TRUE)
+ .executeUpdate();
LOG.debug("- {} reviews set to 'closed' on project #{}", rowUpdated, projectSnapshotId);
return rowUpdated;
}
void notifyReopened(Resource resource, Review review) {
Notification notification = createReviewNotification(resource, review)
- .setFieldValue("old.status", review.getStatus())
- .setFieldValue("new.status", "REOPENED")
- .setFieldValue("old.resolution", review.getResolution())
- .setFieldValue("new.resolution", null);
+ .setFieldValue("old.status", review.getStatus())
+ .setFieldValue("new.status", "REOPENED")
+ .setFieldValue("old.resolution", review.getResolution())
+ .setFieldValue("new.resolution", null);
notificationManager.scheduleForSending(notification);
}
void notifyClosed(Resource resource, Review review) {
Notification notification = createReviewNotification(resource, review)
- .setFieldValue("old.status", review.getStatus())
- .setFieldValue("new.status", "CLOSED");
+ .setFieldValue("old.status", review.getStatus())
+ .setFieldValue("new.status", "CLOSED");
notificationManager.scheduleForSending(notification);
}
private Notification createReviewNotification(Resource resource, Review review) {
return new Notification("review-changed")
- .setFieldValue("reviewId", String.valueOf(review.getId()))
- .setFieldValue("project", project.getRoot().getLongName())
- .setFieldValue("resource", resource != null ? resource.getLongName() : null)
- .setFieldValue("title", review.getTitle())
- .setFieldValue("creator", getCreator(review))
- .setFieldValue("assignee", getAssignee(review));
+ .setFieldValue("reviewId", String.valueOf(review.getId()))
+ .setFieldValue("project", project.getRoot().getLongName())
+ .setFieldValue("resource", resource != null ? resource.getLongName() : null)
+ .setFieldValue("title", review.getTitle())
+ .setFieldValue("creator", getCreator(review))
+ .setFieldValue("assignee", getAssignee(review));
}
}
*/
package org.sonar.plugins.core.sensors;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import junit.framework.ComparisonFailure;
-
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationManager;
import org.sonar.api.resources.Project;
import org.sonar.api.security.UserFinder;
+import org.sonar.jpa.entity.Review;
import org.sonar.jpa.test.AbstractDbUnitTestCase;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
+
public class CloseReviewsDecoratorTest extends AbstractDbUnitTestCase {
private NotificationManager notificationManager;
public void shouldCloseReviewWithoutCorrespondingViolation() throws Exception {
setupData("fixture");
- int count = reviewsDecorator.closeReviews(null, 666, 222);
+ int count = reviewsDecorator.closeReviewsOnResolvedViolations(null, 666, 222);
assertThat(count, is(3));
verify(notificationManager, times(3)).scheduleForSending(any(Notification.class));
- checkTables("shouldCloseReviewWithoutCorrespondingViolation", new String[] { "updated_at" }, new String[] { "reviews" });
+ checkTables("shouldCloseReviewWithoutCorrespondingViolation", new String[]{"updated_at"}, "reviews");
try {
- checkTables("shouldCloseReviewWithoutCorrespondingViolation", new String[] { "reviews" });
+ checkTables("shouldCloseReviewWithoutCorrespondingViolation", "reviews");
fail("'updated_at' columns are identical whereas they should be different.");
} catch (ComparisonFailure e) {
// "updated_at" column must be different, so the comparison should raise this exception
setupData("fixture");
// First we close the reviews for which the violations have been fixed (this is because we use the same "fixture"...)
- reviewsDecorator.closeReviews(null, 666, 222);
+ reviewsDecorator.closeReviewsOnResolvedViolations(null, 666, 222);
// And now we reopen the reviews that still have a violation
- int count = reviewsDecorator.reopenReviews(null, 666);
+ int count = reviewsDecorator.reopenReviewsOnUnresolvedViolations(null, 666);
assertThat(count, is(1));
verify(notificationManager, times(4)).scheduleForSending(any(Notification.class));
- checkTables("shouldReopenResolvedReviewWithNonFixedViolation", new String[] { "updated_at" }, new String[] { "reviews" });
+ checkTables("shouldReopenResolvedReviewWithNonFixedViolation", new String[]{"updated_at"}, "reviews");
try {
- checkTables("shouldReopenResolvedReviewWithNonFixedViolation", new String[] { "reviews" });
+ checkTables("shouldReopenResolvedReviewWithNonFixedViolation", "reviews");
fail("'updated_at' columns are identical whereas they should be different.");
} catch (ComparisonFailure e) {
// "updated_at" column must be different, so the comparison should raise this exception
}
}
+
+ @Test
+ public void shouldCloseResolvedManualViolations() throws Exception {
+ setupData("shouldCloseResolvedManualViolations");
+
+ int count = reviewsDecorator.closeReviewsOnResolvedViolations(null, 555, 11);
+ assertThat(count, is(1));
+
+ verify(notificationManager, times(1)).scheduleForSending(any(Notification.class));
+
+ getSession().commit();
+ assertThat(getSession().getSingleResult(Review.class, "id", 1L).getStatus(), is("CLOSED")); // automatic violation not changed
+ assertThat(getSession().getSingleResult(Review.class, "id", 2L).getStatus(), is("CLOSED")); // manual violation resolved -> closed
+ assertThat(getSession().getSingleResult(Review.class, "id", 3L).getStatus(), is("OPEN")); // manual violation not changed
+ }
}