]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9013 Remove usage of ServerTester in RuleUpdaterMediumTest
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 14 Apr 2017 16:11:54 +0000 (18:11 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 18 Apr 2017 11:44:43 +0000 (13:44 +0200)
server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterTest.java [new file with mode: 0644]

diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java
deleted file mode 100644 (file)
index 6db2952..0000000
+++ /dev/null
@@ -1,625 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.server.rule;
-
-import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import java.util.List;
-import java.util.Map;
-import javax.annotation.Nonnull;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rule.RuleStatus;
-import org.sonar.api.rule.Severity;
-import org.sonar.api.server.debt.DebtRemediationFunction;
-import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.qualityprofile.ActiveRuleDto;
-import org.sonar.db.qualityprofile.ActiveRuleKey;
-import org.sonar.db.qualityprofile.ActiveRuleParamDto;
-import org.sonar.db.qualityprofile.QualityProfileDto;
-import org.sonar.db.rule.RuleDao;
-import org.sonar.db.rule.RuleDefinitionDto;
-import org.sonar.db.rule.RuleDto;
-import org.sonar.db.rule.RuleParamDto;
-import org.sonar.db.rule.RuleTesting;
-import org.sonar.server.es.SearchOptions;
-import org.sonar.server.organization.DefaultOrganizationProvider;
-import org.sonar.server.qualityprofile.QProfileTesting;
-import org.sonar.server.qualityprofile.RuleActivation;
-import org.sonar.server.qualityprofile.RuleActivator;
-import org.sonar.server.rule.index.RuleIndex;
-import org.sonar.server.rule.index.RuleQuery;
-import org.sonar.server.tester.ServerTester;
-import org.sonar.server.tester.UserSessionRule;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-
-// TODO remove ServerTester usage when ActiveRule Daov2 is removed
-public class RuleUpdaterMediumTest {
-
-  static final RuleKey RULE_KEY = RuleKey.of("squid", "S001");
-
-  @ClassRule
-  public static ServerTester tester = new ServerTester().withEsIndexes();
-
-  @org.junit.Rule
-  public UserSessionRule userSessionRule = UserSessionRule.forServerTester(tester);
-
-  private DbClient db = tester.get(DbClient.class);
-  private RuleDao ruleDao = tester.get(RuleDao.class);
-  private DbSession dbSession = db.openSession(false);
-  private RuleIndex ruleIndex = tester.get(RuleIndex.class);
-  private OrganizationDto defaultOrganization;
-
-  RuleUpdater underTest = tester.get(RuleUpdater.class);
-
-  @Before
-  public void before() {
-    tester.clearDbAndIndexes();
-    String defaultOrganizationUuid = tester.get(DefaultOrganizationProvider.class).get().getUuid();
-    defaultOrganization = db.organizationDao().selectByUuid(dbSession, defaultOrganizationUuid).get();
-  }
-
-  @After
-  public void after() {
-    dbSession.close();
-  }
-
-  @Test
-  public void do_not_update_rule_with_removed_status() {
-    ruleDao.insert(dbSession, RuleTesting.newRule(RULE_KEY).setStatus(RuleStatus.REMOVED));
-    dbSession.commit();
-
-    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
-      .setTags(Sets.newHashSet("java9"))
-      .setOrganization(defaultOrganization);
-    try {
-      underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-      fail();
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Rule with REMOVED status cannot be updated: squid:S001");
-    }
-  }
-
-  @Test
-  public void no_changes() {
-    RuleDto ruleDto = RuleTesting.newDto(RULE_KEY, defaultOrganization)
-      // the following fields are not supposed to be updated
-      .setNoteData("my *note*")
-      .setNoteUserLogin("me")
-      .setTags(ImmutableSet.of("tag1"))
-      .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name())
-      .setRemediationGapMultiplier("1d")
-      .setRemediationBaseEffort("5min");
-    ruleDao.insert(dbSession, ruleDto.getDefinition());
-    ruleDao.insertOrUpdate(dbSession, ruleDto.getMetadata().setRuleId(ruleDto.getId()));
-    dbSession.commit();
-
-    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY);
-    assertThat(update.isEmpty()).isTrue();
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-
-    dbSession.clearCache();
-    RuleDto rule = ruleDao.selectOrFailByKey(dbSession, defaultOrganization, RULE_KEY);
-    assertThat(rule.getNoteData()).isEqualTo("my *note*");
-    assertThat(rule.getNoteUserLogin()).isEqualTo("me");
-    assertThat(rule.getTags()).containsOnly("tag1");
-    assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name());
-    assertThat(rule.getRemediationGapMultiplier()).isEqualTo("1d");
-    assertThat(rule.getRemediationBaseEffort()).isEqualTo("5min");
-  }
-
-  @Test
-  public void set_markdown_note() {
-    userSessionRule.logIn("me");
-
-    RuleDto ruleDto = RuleTesting.newDto(RULE_KEY, defaultOrganization)
-      .setNoteData(null)
-      .setNoteUserLogin(null)
-
-      // the following fields are not supposed to be updated
-      .setTags(ImmutableSet.of("tag1"))
-      .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name())
-      .setRemediationGapMultiplier("1d")
-      .setRemediationBaseEffort("5min");
-    ruleDao.insert(dbSession, ruleDto.getDefinition());
-    ruleDao.insertOrUpdate(dbSession, ruleDto.getMetadata().setRuleId(ruleDto.getId()));
-    dbSession.commit();
-
-    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
-      .setMarkdownNote("my *note*")
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-
-    dbSession.clearCache();
-    RuleDto rule = ruleDao.selectOrFailByKey(dbSession, defaultOrganization, RULE_KEY);
-    assertThat(rule.getNoteData()).isEqualTo("my *note*");
-    assertThat(rule.getNoteUserLogin()).isEqualTo("me");
-    assertThat(rule.getNoteCreatedAt()).isNotNull();
-    assertThat(rule.getNoteUpdatedAt()).isNotNull();
-    // no other changes
-    assertThat(rule.getTags()).containsOnly("tag1");
-    assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name());
-    assertThat(rule.getRemediationGapMultiplier()).isEqualTo("1d");
-    assertThat(rule.getRemediationBaseEffort()).isEqualTo("5min");
-  }
-
-  @Test
-  public void remove_markdown_note() {
-    RuleDto ruleDto = RuleTesting.newDto(RULE_KEY, defaultOrganization)
-      .setNoteData("my *note*")
-      .setNoteUserLogin("me");
-    ruleDao.insert(dbSession, ruleDto.getDefinition());
-    ruleDao.insertOrUpdate(dbSession, ruleDto.getMetadata().setRuleId(ruleDto.getId()));
-    dbSession.commit();
-
-    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
-      .setMarkdownNote(null)
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-
-    dbSession.clearCache();
-    RuleDto rule = ruleDao.selectOrFailByKey(dbSession, defaultOrganization, RULE_KEY);
-    assertThat(rule.getNoteData()).isNull();
-    assertThat(rule.getNoteUserLogin()).isNull();
-    assertThat(rule.getNoteCreatedAt()).isNull();
-    assertThat(rule.getNoteUpdatedAt()).isNull();
-  }
-
-  @Test
-  public void set_tags() {
-    // insert db
-    ruleDao.insert(dbSession, RuleTesting.newDto(RULE_KEY, defaultOrganization)
-      .setTags(Sets.newHashSet("security"))
-      .setSystemTags(Sets.newHashSet("java8", "javadoc")).getDefinition());
-    dbSession.commit();
-
-    // java8 is a system tag -> ignore
-    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
-      .setTags(Sets.newHashSet("bug", "java8"))
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-
-    RuleDto rule = ruleDao.selectOrFailByKey(dbSession, defaultOrganization, RULE_KEY);
-    assertThat(rule.getTags()).containsOnly("bug");
-    assertThat(rule.getSystemTags()).containsOnly("java8", "javadoc");
-
-    // verify that tags are indexed in index
-    List<String> tags = ruleIndex.listTags(defaultOrganization, null, 10);
-    assertThat(tags).containsExactly("bug", "java8", "javadoc");
-  }
-
-  @Test
-  public void remove_tags() {
-    RuleDto ruleDto = RuleTesting.newDto(RULE_KEY, defaultOrganization)
-      .setTags(Sets.newHashSet("security"))
-      .setSystemTags(Sets.newHashSet("java8", "javadoc"));
-    ruleDao.insert(dbSession, ruleDto.getDefinition());
-    ruleDao.insertOrUpdate(dbSession, ruleDto.getMetadata());
-    dbSession.commit();
-
-    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
-      .setTags(null)
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-
-    dbSession.clearCache();
-    RuleDto rule = ruleDao.selectOrFailByKey(dbSession, defaultOrganization, RULE_KEY);
-    assertThat(rule.getTags()).isEmpty();
-    assertThat(rule.getSystemTags()).containsOnly("java8", "javadoc");
-
-    // verify that tags are indexed in index
-    List<String> tags = ruleIndex.listTags(defaultOrganization, null, 10);
-    assertThat(tags).containsExactly("java8", "javadoc");
-  }
-
-  @Test
-  public void override_debt() {
-    ruleDao.insert(dbSession, RuleTesting.newRule(RULE_KEY)
-      .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
-      .setDefRemediationGapMultiplier("1d")
-      .setDefRemediationBaseEffort("5min"));
-    dbSession.commit();
-
-    DefaultDebtRemediationFunction fn = new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, null, "1min");
-    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
-      .setDebtRemediationFunction(fn)
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-    dbSession.clearCache();
-
-    // verify debt is overridden
-    RuleDto rule = ruleDao.selectOrFailByKey(dbSession, defaultOrganization, RULE_KEY);
-    assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name());
-    assertThat(rule.getRemediationGapMultiplier()).isNull();
-    assertThat(rule.getRemediationBaseEffort()).isEqualTo("1min");
-
-    assertThat(rule.getDefRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
-    assertThat(rule.getDefRemediationGapMultiplier()).isEqualTo("1d");
-    assertThat(rule.getDefRemediationBaseEffort()).isEqualTo("5min");
-  }
-
-  @Test
-  public void override_debt_only_offset() {
-    ruleDao.insert(dbSession, RuleTesting.newRule(RULE_KEY)
-      .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR.name())
-      .setDefRemediationGapMultiplier("1d")
-      .setDefRemediationBaseEffort(null));
-    dbSession.commit();
-
-    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
-      .setDebtRemediationFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR, "2d", null))
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-    dbSession.clearCache();
-
-    // verify debt is overridden
-    RuleDto rule = ruleDao.selectOrFailByKey(dbSession, defaultOrganization, RULE_KEY);
-    assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR.name());
-    assertThat(rule.getRemediationGapMultiplier()).isEqualTo("2d");
-    assertThat(rule.getRemediationBaseEffort()).isNull();
-
-    assertThat(rule.getDefRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR.name());
-    assertThat(rule.getDefRemediationGapMultiplier()).isEqualTo("1d");
-    assertThat(rule.getDefRemediationBaseEffort()).isNull();
-  }
-
-  @Test
-  public void override_debt_from_linear_with_offset_to_constant() {
-    ruleDao.insert(dbSession, RuleTesting.newRule(RULE_KEY)
-      .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
-      .setDefRemediationGapMultiplier("1d")
-      .setDefRemediationBaseEffort("5min"));
-    dbSession.commit();
-
-    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
-      .setDebtRemediationFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, null, "10min"))
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-    dbSession.clearCache();
-
-    // verify debt is overridden
-    RuleDto rule = ruleDao.selectOrFailByKey(dbSession, defaultOrganization, RULE_KEY);
-    assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name());
-    assertThat(rule.getRemediationGapMultiplier()).isNull();
-    assertThat(rule.getRemediationBaseEffort()).isEqualTo("10min");
-
-    assertThat(rule.getDefRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
-    assertThat(rule.getDefRemediationGapMultiplier()).isEqualTo("1d");
-    assertThat(rule.getDefRemediationBaseEffort()).isEqualTo("5min");
-  }
-
-  @Test
-  public void reset_remediation_function() {
-    RuleDto ruleDto = RuleTesting.newDto(RULE_KEY, defaultOrganization)
-      .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR.name())
-      .setDefRemediationGapMultiplier("1d")
-      .setDefRemediationBaseEffort("5min")
-      .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name())
-      .setRemediationGapMultiplier(null)
-      .setRemediationBaseEffort("1min");
-    ruleDao.insert(dbSession, ruleDto.getDefinition());
-    ruleDao.insertOrUpdate(dbSession, ruleDto.getMetadata().setRuleId(ruleDto.getId()));
-    dbSession.commit();
-
-    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
-      .setDebtRemediationFunction(null)
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-    dbSession.clearCache();
-
-    // verify debt is coming from default values
-    RuleDto rule = ruleDao.selectOrFailByKey(dbSession, defaultOrganization, RULE_KEY);
-    assertThat(rule.getDefRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR.name());
-    assertThat(rule.getDefRemediationGapMultiplier()).isEqualTo("1d");
-    assertThat(rule.getDefRemediationBaseEffort()).isEqualTo("5min");
-
-    assertThat(rule.getRemediationFunction()).isNull();
-    assertThat(rule.getRemediationGapMultiplier()).isNull();
-    assertThat(rule.getRemediationBaseEffort()).isNull();
-  }
-
-  @Test
-  public void update_custom_rule() {
-    // Create template rule
-    RuleDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001"));
-    ruleDao.insert(dbSession, templateRule.getDefinition());
-    RuleParamDto templateRuleParam1 = RuleParamDto.createFor(templateRule.getDefinition()).setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(".*");
-    RuleParamDto templateRuleParam2 = RuleParamDto.createFor(templateRule.getDefinition()).setName("format").setType("STRING").setDescription("Format");
-    ruleDao.insertRuleParam(dbSession, templateRule.getDefinition(), templateRuleParam1);
-    ruleDao.insertRuleParam(dbSession, templateRule.getDefinition(), templateRuleParam2);
-
-    // Create custom rule
-    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule)
-      .setName("Old name")
-      .setDescription("Old description")
-      .setSeverity(Severity.MINOR)
-      .setStatus(RuleStatus.BETA)
-      .getDefinition();
-    ruleDao.insert(dbSession, customRule);
-    ruleDao.insertRuleParam(dbSession, customRule, templateRuleParam1.setDefaultValue("a.*"));
-    ruleDao.insertRuleParam(dbSession, customRule, templateRuleParam2.setDefaultValue(null));
-
-    dbSession.commit();
-
-    // Update custom rule
-    RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
-      .setName("New name")
-      .setMarkdownDescription("New description")
-      .setSeverity("MAJOR")
-      .setStatus(RuleStatus.READY)
-      .setParameters(ImmutableMap.of("regex", "b.*"))
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-
-    dbSession.clearCache();
-
-    // Verify custom rule is updated
-    RuleDto customRuleReloaded = ruleDao.selectOrFailByKey(dbSession, defaultOrganization, customRule.getKey());
-    assertThat(customRuleReloaded).isNotNull();
-    assertThat(customRuleReloaded.getName()).isEqualTo("New name");
-    assertThat(customRuleReloaded.getDescription()).isEqualTo("New description");
-    assertThat(customRuleReloaded.getSeverityString()).isEqualTo("MAJOR");
-    assertThat(customRuleReloaded.getStatus()).isEqualTo(RuleStatus.READY);
-
-    List<RuleParamDto> params = ruleDao.selectRuleParamsByRuleKey(dbSession, customRuleReloaded.getKey());
-    assertThat(params).hasSize(2);
-    assertThat(params.get(0).getDefaultValue()).isEqualTo("b.*");
-    assertThat(params.get(1).getDefaultValue()).isNull();
-
-    // Verify in index
-    assertThat(ruleIndex.search(new RuleQuery().setQueryText("New name"), new SearchOptions()).getIds()).containsOnly(customRule.getKey());
-    assertThat(ruleIndex.search(new RuleQuery().setQueryText("New description"), new SearchOptions()).getIds()).containsOnly(customRule.getKey());
-
-    assertThat(ruleIndex.search(new RuleQuery().setQueryText("Old name"), new SearchOptions()).getTotal()).isZero();
-    assertThat(ruleIndex.search(new RuleQuery().setQueryText("Old description"), new SearchOptions()).getTotal()).isZero();
-  }
-
-  @Test
-  public void update_custom_rule_with_empty_parameter() {
-    // Create template rule
-    RuleDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001"));
-    ruleDao.insert(dbSession, templateRule.getDefinition());
-    RuleParamDto templateRuleParam = RuleParamDto.createFor(templateRule.getDefinition()).setName("regex").setType("STRING").setDescription("Reg ex");
-    ruleDao.insertRuleParam(dbSession, templateRule.getDefinition(), templateRuleParam);
-
-    // Create custom rule
-    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule)
-      .setName("Old name")
-      .setDescription("Old description")
-      .setSeverity(Severity.MINOR)
-      .setStatus(RuleStatus.BETA)
-      .getDefinition();
-    ruleDao.insert(dbSession, customRule);
-    ruleDao.insertRuleParam(dbSession, customRule, templateRuleParam);
-
-    dbSession.commit();
-
-    // Update custom rule without setting a value for the parameter
-    RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
-      .setName("New name")
-      .setMarkdownDescription("New description")
-      .setSeverity("MAJOR")
-      .setStatus(RuleStatus.READY)
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-
-    dbSession.clearCache();
-
-    // Verify custom rule is updated
-    List<RuleParamDto> params = ruleDao.selectRuleParamsByRuleKey(dbSession, customRule.getKey());
-    assertThat(params.get(0).getDefaultValue()).isNull();
-  }
-
-  @Test
-  public void update_active_rule_parameters_when_updating_custom_rule() {
-    // Create template rule with 3 parameters
-    RuleDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001")).setLanguage("xoo");
-    RuleDefinitionDto templateRuleDefinition = templateRule.getDefinition();
-    ruleDao.insert(dbSession, templateRuleDefinition);
-    RuleParamDto templateRuleParam1 = RuleParamDto.createFor(templateRuleDefinition).setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(".*");
-    ruleDao.insertRuleParam(dbSession, templateRuleDefinition, templateRuleParam1);
-    RuleParamDto templateRuleParam2 = RuleParamDto.createFor(templateRuleDefinition).setName("format").setType("STRING").setDescription("format").setDefaultValue("csv");
-    ruleDao.insertRuleParam(dbSession, templateRuleDefinition, templateRuleParam2);
-    RuleParamDto templateRuleParam3 = RuleParamDto.createFor(templateRuleDefinition).setName("message").setType("STRING").setDescription("message");
-    ruleDao.insertRuleParam(dbSession, templateRuleDefinition, templateRuleParam3);
-
-    // Create custom rule
-    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule)
-      .setSeverity(Severity.MAJOR)
-      .setLanguage("xoo")
-      .getDefinition();
-    ruleDao.insert(dbSession, customRule);
-    ruleDao.insertRuleParam(dbSession, customRule, templateRuleParam1.setDefaultValue("a.*"));
-    ruleDao.insertRuleParam(dbSession, customRule, templateRuleParam2.setDefaultValue("txt"));
-    ruleDao.insertRuleParam(dbSession, customRule, templateRuleParam3);
-
-    // Create a quality profile
-    QualityProfileDto profileDto = QProfileTesting.newXooP1(defaultOrganization);
-    db.qualityProfileDao().insert(dbSession, profileDto);
-    dbSession.commit();
-
-    // Activate the custom rule
-    RuleActivation activation = new RuleActivation(customRule.getKey()).setSeverity(Severity.BLOCKER);
-    tester.get(RuleActivator.class).activate(dbSession, activation, profileDto);
-    dbSession.commit();
-    dbSession.clearCache();
-
-    // Update custom rule parameter 'regex', add 'message' and remove 'format'
-    RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
-      .setParameters(ImmutableMap.of("regex", "b.*", "message", "a message"))
-      .setOrganization(defaultOrganization);
-    underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-
-    dbSession.clearCache();
-
-    // Verify custom rule parameters has been updated
-    List<RuleParamDto> params = ruleDao.selectRuleParamsByRuleKey(dbSession, customRule.getKey());
-    assertThat(params).hasSize(3);
-
-    Map<String, RuleParamDto> paramsByKey = paramsByKey(params);
-    assertThat(paramsByKey.get("regex")).isNotNull();
-    assertThat(paramsByKey.get("regex").getDefaultValue()).isEqualTo("b.*");
-    assertThat(paramsByKey.get("message")).isNotNull();
-    assertThat(paramsByKey.get("message").getDefaultValue()).isEqualTo("a message");
-    assertThat(paramsByKey.get("format")).isNotNull();
-    assertThat(paramsByKey.get("format").getDefaultValue()).isNull();
-
-    // Verify that severity has not changed
-    ActiveRuleDto activeRuleDto = db.activeRuleDao().selectOrFailByKey(dbSession, ActiveRuleKey.of(profileDto.getKey(), customRule.getKey()));
-    assertThat(activeRuleDto.getSeverityString()).isEqualTo(Severity.BLOCKER);
-
-    // Verify active rule parameters has been updated
-    List<ActiveRuleParamDto> activeRuleParams = db.activeRuleDao().selectParamsByActiveRuleId(dbSession, activeRuleDto.getId());
-
-    assertThat(activeRuleParams).hasSize(2);
-    Map<String, ActiveRuleParamDto> activeRuleParamsByKey = ActiveRuleParamDto.groupByKey(activeRuleParams);
-    assertThat(activeRuleParamsByKey.get("regex").getValue()).isEqualTo("b.*");
-    assertThat(activeRuleParamsByKey.get("message").getValue()).isEqualTo("a message");
-    assertThat(activeRuleParamsByKey.get("format")).isNull();
-  }
-
-  @Test
-  public void fail_to_update_custom_rule_when_empty_name() {
-    // Create template rule
-    RuleDefinitionDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001")).getDefinition();
-    ruleDao.insert(dbSession, templateRule);
-
-    // Create custom rule
-    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule);
-    ruleDao.insert(dbSession, customRule);
-
-    dbSession.commit();
-
-    // Update custom rule
-    RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
-      .setName("")
-      .setMarkdownDescription("New desc")
-      .setOrganization(defaultOrganization);
-    try {
-      underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("The name is missing");
-    }
-  }
-
-  @Test
-  public void fail_to_update_custom_rule_when_empty_description() {
-    // Create template rule
-    RuleDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001"));
-    ruleDao.insert(dbSession, templateRule.getDefinition());
-
-    // Create custom rule
-    RuleDto customRule = RuleTesting.newCustomRule(templateRule);
-    ruleDao.insert(dbSession, customRule.getDefinition());
-
-    dbSession.commit();
-
-    // Update custom rule
-    RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
-      .setName("New name")
-      .setMarkdownDescription("")
-      .setOrganization(defaultOrganization);
-    try {
-      underTest.update(dbSession, update, defaultOrganization, userSessionRule);
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("The description is missing");
-    }
-  }
-
-  @Test
-  public void fail_to_update_plugin_rule_if_name_is_set() {
-    // Create rule rule
-    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("squid", "S01"));
-    ruleDao.insert(dbSession, ruleDto);
-
-    dbSession.commit();
-
-    try {
-      // Update rule
-      RuleUpdate.createForPluginRule(ruleDto.getKey())
-        .setName("New name");
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Not a custom rule");
-    }
-  }
-
-  @Test
-  public void fail_to_update_plugin_rule_if_description_is_set() {
-    // Create rule rule
-    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("squid", "S01"));
-    ruleDao.insert(dbSession, ruleDto);
-
-    dbSession.commit();
-
-    try {
-      // Update rule
-      RuleUpdate.createForPluginRule(ruleDto.getKey())
-        .setMarkdownDescription("New description");
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Not a custom rule");
-    }
-  }
-
-  @Test
-  public void fail_to_update_plugin_rule_if_severity_is_set() {
-    // Create rule rule
-    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("squid", "S01"));
-    ruleDao.insert(dbSession, ruleDto);
-
-    dbSession.commit();
-
-    try {
-      // Update rule
-      RuleUpdate.createForPluginRule(ruleDto.getKey())
-        .setSeverity(Severity.CRITICAL);
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Not a custom rule");
-    }
-  }
-
-  private static Map<String, RuleParamDto> paramsByKey(List<RuleParamDto> params) {
-    return FluentIterable.from(params).uniqueIndex(RuleParamToKey.INSTANCE);
-  }
-
-  private enum RuleParamToKey implements Function<RuleParamDto, String> {
-    INSTANCE;
-
-    @Override
-    public String apply(@Nonnull RuleParamDto input) {
-      return input.getName();
-    }
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterTest.java
new file mode 100644 (file)
index 0000000..2987f0e
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.server.rule;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.MapSettings;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.server.debt.DebtRemediationFunction;
+import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.qualityprofile.ActiveRuleDto;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
+import org.sonar.db.qualityprofile.ActiveRuleParamDto;
+import org.sonar.db.qualityprofile.QualityProfileDto;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleParamDto;
+import org.sonar.db.rule.RuleTesting;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.organization.TestDefaultOrganizationProvider;
+import org.sonar.server.qualityprofile.QProfileTesting;
+import org.sonar.server.rule.index.RuleIndex;
+import org.sonar.server.rule.index.RuleIndexDefinition;
+import org.sonar.server.rule.index.RuleIndexer;
+import org.sonar.server.rule.index.RuleQuery;
+import org.sonar.server.tester.UserSessionRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+public class RuleUpdaterTest {
+
+  static final RuleKey RULE_KEY = RuleKey.of("squid", "S001");
+
+  private System2 system2 = mock(System2.class);
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Rule
+  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+
+  @Rule
+  public DbTester db = DbTester.create(system2);
+
+  @Rule
+  public EsTester es = new EsTester(new RuleIndexDefinition(new MapSettings()));
+
+  private RuleIndex ruleIndex = new RuleIndex(es.client());
+  private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
+  private DbSession dbSession = db.getSession();
+  private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
+
+  private RuleUpdater underTest = new RuleUpdater(db.getDbClient(), ruleIndexer, system2);
+
+  @Test
+  public void do_not_update_rule_with_removed_status() {
+    db.rules().insert(RuleTesting.newRule(RULE_KEY).setStatus(RuleStatus.REMOVED));
+    dbSession.commit();
+
+    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
+      .setTags(Sets.newHashSet("java9"))
+      .setOrganization(db.getDefaultOrganization());
+    try {
+      underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Rule with REMOVED status cannot be updated: squid:S001");
+    }
+  }
+
+  @Test
+  public void no_changes() {
+    RuleDto ruleDto = RuleTesting.newDto(RULE_KEY, db.getDefaultOrganization())
+      // the following fields are not supposed to be updated
+      .setNoteData("my *note*")
+      .setNoteUserLogin("me")
+      .setTags(ImmutableSet.of("tag1"))
+      .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name())
+      .setRemediationGapMultiplier("1d")
+      .setRemediationBaseEffort("5min");
+    db.rules().insert(ruleDto.getDefinition());
+    db.rules().insertOrUpdateMetadata(ruleDto.getMetadata().setRuleId(ruleDto.getId()));
+    dbSession.commit();
+
+    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY);
+    assertThat(update.isEmpty()).isTrue();
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+
+    dbSession.clearCache();
+    RuleDto rule = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, db.getDefaultOrganization(), RULE_KEY);
+    assertThat(rule.getNoteData()).isEqualTo("my *note*");
+    assertThat(rule.getNoteUserLogin()).isEqualTo("me");
+    assertThat(rule.getTags()).containsOnly("tag1");
+    assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name());
+    assertThat(rule.getRemediationGapMultiplier()).isEqualTo("1d");
+    assertThat(rule.getRemediationBaseEffort()).isEqualTo("5min");
+  }
+
+  @Test
+  public void set_markdown_note() {
+    userSessionRule.logIn("me");
+
+    RuleDto ruleDto = RuleTesting.newDto(RULE_KEY, db.getDefaultOrganization())
+      .setNoteData(null)
+      .setNoteUserLogin(null)
+
+      // the following fields are not supposed to be updated
+      .setTags(ImmutableSet.of("tag1"))
+      .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name())
+      .setRemediationGapMultiplier("1d")
+      .setRemediationBaseEffort("5min");
+    db.rules().insert(ruleDto.getDefinition());
+    db.rules().insertOrUpdateMetadata(ruleDto.getMetadata().setRuleId(ruleDto.getId()));
+    dbSession.commit();
+
+    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
+      .setMarkdownNote("my *note*")
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+
+    dbSession.clearCache();
+    RuleDto rule = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, db.getDefaultOrganization(), RULE_KEY);
+    assertThat(rule.getNoteData()).isEqualTo("my *note*");
+    assertThat(rule.getNoteUserLogin()).isEqualTo("me");
+    assertThat(rule.getNoteCreatedAt()).isNotNull();
+    assertThat(rule.getNoteUpdatedAt()).isNotNull();
+    // no other changes
+    assertThat(rule.getTags()).containsOnly("tag1");
+    assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name());
+    assertThat(rule.getRemediationGapMultiplier()).isEqualTo("1d");
+    assertThat(rule.getRemediationBaseEffort()).isEqualTo("5min");
+  }
+
+  @Test
+  public void remove_markdown_note() {
+    RuleDto ruleDto = RuleTesting.newDto(RULE_KEY, db.getDefaultOrganization())
+      .setNoteData("my *note*")
+      .setNoteUserLogin("me");
+    db.rules().insert(ruleDto.getDefinition());
+    db.rules().insertOrUpdateMetadata(ruleDto.getMetadata().setRuleId(ruleDto.getId()));
+    dbSession.commit();
+
+    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
+      .setMarkdownNote(null)
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+
+    dbSession.clearCache();
+    RuleDto rule = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, db.getDefaultOrganization(), RULE_KEY);
+    assertThat(rule.getNoteData()).isNull();
+    assertThat(rule.getNoteUserLogin()).isNull();
+    assertThat(rule.getNoteCreatedAt()).isNull();
+    assertThat(rule.getNoteUpdatedAt()).isNull();
+  }
+
+  @Test
+  public void set_tags() {
+    // insert db
+    db.rules().insert(RuleTesting.newDto(RULE_KEY, db.getDefaultOrganization())
+      .setTags(Sets.newHashSet("security"))
+      .setSystemTags(Sets.newHashSet("java8", "javadoc")).getDefinition());
+    dbSession.commit();
+
+    // java8 is a system tag -> ignore
+    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
+      .setTags(Sets.newHashSet("bug", "java8"))
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+
+    RuleDto rule = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, db.getDefaultOrganization(), RULE_KEY);
+    assertThat(rule.getTags()).containsOnly("bug");
+    assertThat(rule.getSystemTags()).containsOnly("java8", "javadoc");
+
+    // verify that tags are indexed in index
+    List<String> tags = ruleIndex.listTags(db.getDefaultOrganization(), null, 10);
+    assertThat(tags).containsExactly("bug", "java8", "javadoc");
+  }
+
+  @Test
+  public void remove_tags() {
+    RuleDto ruleDto = RuleTesting.newDto(RULE_KEY, db.getDefaultOrganization())
+      .setTags(Sets.newHashSet("security"))
+      .setSystemTags(Sets.newHashSet("java8", "javadoc"));
+    db.rules().insert(ruleDto.getDefinition());
+    db.rules().insertOrUpdateMetadata(ruleDto.getMetadata());
+    dbSession.commit();
+
+    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
+      .setTags(null)
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+
+    dbSession.clearCache();
+    RuleDto rule = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, db.getDefaultOrganization(), RULE_KEY);
+    assertThat(rule.getTags()).isEmpty();
+    assertThat(rule.getSystemTags()).containsOnly("java8", "javadoc");
+
+    // verify that tags are indexed in index
+    List<String> tags = ruleIndex.listTags(db.getDefaultOrganization(), null, 10);
+    assertThat(tags).containsExactly("java8", "javadoc");
+  }
+
+  @Test
+  public void override_debt() {
+    db.rules().insert(RuleTesting.newRule(RULE_KEY)
+      .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
+      .setDefRemediationGapMultiplier("1d")
+      .setDefRemediationBaseEffort("5min"));
+    dbSession.commit();
+
+    DefaultDebtRemediationFunction fn = new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, null, "1min");
+    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
+      .setDebtRemediationFunction(fn)
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+    dbSession.clearCache();
+
+    // verify debt is overridden
+    RuleDto rule = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, db.getDefaultOrganization(), RULE_KEY);
+    assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name());
+    assertThat(rule.getRemediationGapMultiplier()).isNull();
+    assertThat(rule.getRemediationBaseEffort()).isEqualTo("1min");
+
+    assertThat(rule.getDefRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
+    assertThat(rule.getDefRemediationGapMultiplier()).isEqualTo("1d");
+    assertThat(rule.getDefRemediationBaseEffort()).isEqualTo("5min");
+  }
+
+  @Test
+  public void override_debt_only_offset() {
+    db.rules().insert(RuleTesting.newRule(RULE_KEY)
+      .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR.name())
+      .setDefRemediationGapMultiplier("1d")
+      .setDefRemediationBaseEffort(null));
+    dbSession.commit();
+
+    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
+      .setDebtRemediationFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR, "2d", null))
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+    dbSession.clearCache();
+
+    // verify debt is overridden
+    RuleDto rule = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, db.getDefaultOrganization(), RULE_KEY);
+    assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR.name());
+    assertThat(rule.getRemediationGapMultiplier()).isEqualTo("2d");
+    assertThat(rule.getRemediationBaseEffort()).isNull();
+
+    assertThat(rule.getDefRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR.name());
+    assertThat(rule.getDefRemediationGapMultiplier()).isEqualTo("1d");
+    assertThat(rule.getDefRemediationBaseEffort()).isNull();
+  }
+
+  @Test
+  public void override_debt_from_linear_with_offset_to_constant() {
+    db.rules().insert(RuleTesting.newRule(RULE_KEY)
+      .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
+      .setDefRemediationGapMultiplier("1d")
+      .setDefRemediationBaseEffort("5min"));
+    dbSession.commit();
+
+    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
+      .setDebtRemediationFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, null, "10min"))
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+    dbSession.clearCache();
+
+    // verify debt is overridden
+    RuleDto rule = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, db.getDefaultOrganization(), RULE_KEY);
+    assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name());
+    assertThat(rule.getRemediationGapMultiplier()).isNull();
+    assertThat(rule.getRemediationBaseEffort()).isEqualTo("10min");
+
+    assertThat(rule.getDefRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
+    assertThat(rule.getDefRemediationGapMultiplier()).isEqualTo("1d");
+    assertThat(rule.getDefRemediationBaseEffort()).isEqualTo("5min");
+  }
+
+  @Test
+  public void reset_remediation_function() {
+    RuleDto ruleDto = RuleTesting.newDto(RULE_KEY, db.getDefaultOrganization())
+      .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR.name())
+      .setDefRemediationGapMultiplier("1d")
+      .setDefRemediationBaseEffort("5min")
+      .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name())
+      .setRemediationGapMultiplier(null)
+      .setRemediationBaseEffort("1min");
+    db.rules().insert(ruleDto.getDefinition());
+    db.rules().insertOrUpdateMetadata(ruleDto.getMetadata().setRuleId(ruleDto.getId()));
+    dbSession.commit();
+
+    RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
+      .setDebtRemediationFunction(null)
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+    dbSession.clearCache();
+
+    // verify debt is coming from default values
+    RuleDto rule = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, db.getDefaultOrganization(), RULE_KEY);
+    assertThat(rule.getDefRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.LINEAR.name());
+    assertThat(rule.getDefRemediationGapMultiplier()).isEqualTo("1d");
+    assertThat(rule.getDefRemediationBaseEffort()).isEqualTo("5min");
+
+    assertThat(rule.getRemediationFunction()).isNull();
+    assertThat(rule.getRemediationGapMultiplier()).isNull();
+    assertThat(rule.getRemediationBaseEffort()).isNull();
+  }
+
+  @Test
+  public void update_custom_rule() {
+    // Create template rule
+    RuleDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001"));
+    db.rules().insert(templateRule.getDefinition());
+    db.rules().insertRuleParam(templateRule.getDefinition(), param -> param.setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(".*"));
+    db.rules().insertRuleParam(templateRule.getDefinition(), param -> param.setName("format").setType("STRING").setDescription("Format"));
+
+    // Create custom rule
+    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule)
+      .setName("Old name")
+      .setDescription("Old description")
+      .setSeverity(Severity.MINOR)
+      .setStatus(RuleStatus.BETA)
+      .getDefinition();
+    db.rules().insert(customRule);
+    db.rules().insertRuleParam(customRule, param -> param.setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue("a.*"));
+    db.rules().insertRuleParam(customRule, param -> param.setName("format").setType("STRING").setDescription("Format").setDefaultValue(null));
+
+    // Update custom rule
+    RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
+      .setName("New name")
+      .setMarkdownDescription("New description")
+      .setSeverity("MAJOR")
+      .setStatus(RuleStatus.READY)
+      .setParameters(ImmutableMap.of("regex", "b.*"))
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+
+    dbSession.clearCache();
+
+    // Verify custom rule is updated
+    RuleDto customRuleReloaded = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, db.getDefaultOrganization(), customRule.getKey());
+    assertThat(customRuleReloaded).isNotNull();
+    assertThat(customRuleReloaded.getName()).isEqualTo("New name");
+    assertThat(customRuleReloaded.getDescription()).isEqualTo("New description");
+    assertThat(customRuleReloaded.getSeverityString()).isEqualTo("MAJOR");
+    assertThat(customRuleReloaded.getStatus()).isEqualTo(RuleStatus.READY);
+
+    List<RuleParamDto> params = db.getDbClient().ruleDao().selectRuleParamsByRuleKey(dbSession, customRuleReloaded.getKey());
+    assertThat(params).extracting(RuleParamDto::getDefaultValue).containsOnly("b.*", null);
+
+    // Verify in index
+    assertThat(ruleIndex.search(new RuleQuery().setQueryText("New name"), new SearchOptions()).getIds()).containsOnly(customRule.getKey());
+    assertThat(ruleIndex.search(new RuleQuery().setQueryText("New description"), new SearchOptions()).getIds()).containsOnly(customRule.getKey());
+
+    assertThat(ruleIndex.search(new RuleQuery().setQueryText("Old name"), new SearchOptions()).getTotal()).isZero();
+    assertThat(ruleIndex.search(new RuleQuery().setQueryText("Old description"), new SearchOptions()).getTotal()).isZero();
+  }
+
+  @Test
+  public void update_custom_rule_with_empty_parameter() {
+    // Create template rule
+    RuleDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001"));
+    db.rules().insert(templateRule.getDefinition());
+    db.rules().insertRuleParam(templateRule.getDefinition(), param -> param.setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(null));
+
+    // Create custom rule
+    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule)
+      .setName("Old name")
+      .setDescription("Old description")
+      .setSeverity(Severity.MINOR)
+      .setStatus(RuleStatus.BETA)
+      .getDefinition();
+    db.rules().insert(customRule);
+    db.rules().insertRuleParam(customRule, param -> param.setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(null));
+
+    dbSession.commit();
+
+    // Update custom rule without setting a value for the parameter
+    RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
+      .setName("New name")
+      .setMarkdownDescription("New description")
+      .setSeverity("MAJOR")
+      .setStatus(RuleStatus.READY)
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+
+    dbSession.clearCache();
+
+    // Verify custom rule is updated
+    List<RuleParamDto> params = db.getDbClient().ruleDao().selectRuleParamsByRuleKey(dbSession, customRule.getKey());
+    assertThat(params.get(0).getDefaultValue()).isNull();
+  }
+
+  @Test
+  public void update_active_rule_parameters_when_updating_custom_rule() {
+    // Create template rule with 3 parameters
+    RuleDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001")).setLanguage("xoo");
+    RuleDefinitionDto templateRuleDefinition = templateRule.getDefinition();
+    db.rules().insert(templateRuleDefinition);
+    db.rules().insertRuleParam(templateRuleDefinition, param -> param.setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(".*"));
+    db.rules().insertRuleParam(templateRuleDefinition, param -> param.setName("format").setType("STRING").setDescription("format").setDefaultValue("csv"));
+    db.rules().insertRuleParam(templateRuleDefinition, param -> param.setName("message").setType("STRING").setDescription("message"));
+
+    // Create custom rule
+    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule)
+      .setSeverity(Severity.MAJOR)
+      .setLanguage("xoo")
+      .getDefinition();
+    db.rules().insert(customRule);
+    RuleParamDto ruleParam1 = db.rules().insertRuleParam(customRule, param -> param.setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue("a.*"));
+    db.rules().insertRuleParam(customRule, param -> param.setName("format").setType("STRING").setDescription("format").setDefaultValue("txt"));
+    db.rules().insertRuleParam(customRule, param -> param.setName("message").setType("STRING").setDescription("message"));
+
+    // Create a quality profile
+    QualityProfileDto profileDto = QProfileTesting.newXooP1(db.getDefaultOrganization());
+    db.getDbClient().qualityProfileDao().insert(dbSession, profileDto);
+    dbSession.commit();
+
+    // Activate the custom rule
+    ActiveRuleDto activeRuleDto = new ActiveRuleDto()
+      .setProfileId(profileDto.getId())
+      .setRuleId(customRule.getId())
+      .setSeverity(Severity.BLOCKER);
+    db.getDbClient().activeRuleDao().insert(dbSession, activeRuleDto);
+    db.getDbClient().activeRuleDao().insertParam(dbSession, activeRuleDto, new ActiveRuleParamDto()
+      .setActiveRuleId(activeRuleDto.getId())
+      .setRulesParameterId(ruleParam1.getId())
+      .setKey(ruleParam1.getName())
+      .setValue(ruleParam1.getDefaultValue()));
+    dbSession.commit();
+
+    // Update custom rule parameter 'regex', add 'message' and remove 'format'
+    RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
+      .setParameters(ImmutableMap.of("regex", "b.*", "message", "a message"))
+      .setOrganization(db.getDefaultOrganization());
+    underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+
+    // Verify custom rule parameters has been updated
+    List<RuleParamDto> params = db.getDbClient().ruleDao().selectRuleParamsByRuleKey(dbSession, customRule.getKey());
+    assertThat(params).hasSize(3);
+
+    Map<String, RuleParamDto> paramsByKey = paramsByKey(params);
+    assertThat(paramsByKey.get("regex")).isNotNull();
+    assertThat(paramsByKey.get("regex").getDefaultValue()).isEqualTo("b.*");
+    assertThat(paramsByKey.get("message")).isNotNull();
+    assertThat(paramsByKey.get("message").getDefaultValue()).isEqualTo("a message");
+    assertThat(paramsByKey.get("format")).isNotNull();
+    assertThat(paramsByKey.get("format").getDefaultValue()).isNull();
+
+    // Verify that severity has not changed
+    ActiveRuleDto activeRuleReloaded = db.getDbClient().activeRuleDao().selectOrFailByKey(dbSession, ActiveRuleKey.of(profileDto.getKey(), customRule.getKey()));
+    assertThat(activeRuleReloaded.getSeverityString()).isEqualTo(Severity.BLOCKER);
+
+    // Verify active rule parameters has been updated
+    List<ActiveRuleParamDto> activeRuleParams = db.getDbClient().activeRuleDao().selectParamsByActiveRuleId(dbSession, activeRuleReloaded.getId());
+
+    assertThat(activeRuleParams).hasSize(2);
+    Map<String, ActiveRuleParamDto> activeRuleParamsByKey = ActiveRuleParamDto.groupByKey(activeRuleParams);
+    assertThat(activeRuleParamsByKey.get("regex").getValue()).isEqualTo("b.*");
+    assertThat(activeRuleParamsByKey.get("message").getValue()).isEqualTo("a message");
+    assertThat(activeRuleParamsByKey.get("format")).isNull();
+  }
+
+  @Test
+  public void fail_to_update_custom_rule_when_empty_name() {
+    // Create template rule
+    RuleDefinitionDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001")).getDefinition();
+    db.rules().insert(templateRule);
+
+    // Create custom rule
+    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule);
+    db.rules().insert(customRule);
+
+    dbSession.commit();
+
+    // Update custom rule
+    RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
+      .setName("")
+      .setMarkdownDescription("New desc")
+      .setOrganization(db.getDefaultOrganization());
+    try {
+      underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+      fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("The name is missing");
+    }
+  }
+
+  @Test
+  public void fail_to_update_custom_rule_when_empty_description() {
+    // Create template rule
+    RuleDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001"));
+    db.rules().insert(templateRule.getDefinition());
+
+    // Create custom rule
+    RuleDto customRule = RuleTesting.newCustomRule(templateRule);
+    db.rules().insert(customRule.getDefinition());
+
+    dbSession.commit();
+
+    // Update custom rule
+    RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
+      .setName("New name")
+      .setMarkdownDescription("")
+      .setOrganization(db.getDefaultOrganization());
+    try {
+      underTest.update(dbSession, update, db.getDefaultOrganization(), userSessionRule);
+      fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("The description is missing");
+    }
+  }
+
+  @Test
+  public void fail_to_update_plugin_rule_if_name_is_set() {
+    // Create rule rule
+    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("squid", "S01"));
+    db.rules().insert(ruleDto);
+
+    dbSession.commit();
+
+    try {
+      // Update rule
+      RuleUpdate.createForPluginRule(ruleDto.getKey())
+        .setName("New name");
+      fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Not a custom rule");
+    }
+  }
+
+  @Test
+  public void fail_to_update_plugin_rule_if_description_is_set() {
+    // Create rule rule
+    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("squid", "S01"));
+    db.rules().insert(ruleDto);
+
+    dbSession.commit();
+
+    try {
+      // Update rule
+      RuleUpdate.createForPluginRule(ruleDto.getKey())
+        .setMarkdownDescription("New description");
+      fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Not a custom rule");
+    }
+  }
+
+  @Test
+  public void fail_to_update_plugin_rule_if_severity_is_set() {
+    // Create rule rule
+    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("squid", "S01"));
+    db.rules().insert(ruleDto);
+
+    dbSession.commit();
+
+    try {
+      // Update rule
+      RuleUpdate.createForPluginRule(ruleDto.getKey())
+        .setSeverity(Severity.CRITICAL);
+      fail();
+    } catch (Exception e) {
+      assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Not a custom rule");
+    }
+  }
+
+  private static Map<String, RuleParamDto> paramsByKey(List<RuleParamDto> params) {
+    return FluentIterable.from(params).uniqueIndex(RuleParamToKey.INSTANCE);
+  }
+
+  private enum RuleParamToKey implements Function<RuleParamDto, String> {
+    INSTANCE;
+
+    @Override
+    public String apply(@Nonnull RuleParamDto input) {
+      return input.getName();
+    }
+  }
+}