@Test
  public void register_rules_at_startup() throws Exception {
    verifyRulesInDb();

    Result<Rule> searchResult = index.search(new RuleQuery(), new QueryOptions());
    assertThat(searchResult.getTotal()).isEqualTo(3);
    assertThat(searchResult.getHits()).hasSize(3);

    Rule rule = index.getByKey(RuleTesting.XOO_X1);
    assertThat(rule.severity()).isEqualTo(Severity.MINOR);
    assertThat(rule.name()).isEqualTo("x1 name");
    assertThat(rule.htmlDescription()).isEqualTo("x1 desc");
    assertThat(rule.systemTags()).contains("tag1");

    assertThat(rule.params()).hasSize(1);
    assertThat(rule.param("acceptWhitespace").type()).isEqualTo(RuleParamType.BOOLEAN);
    assertThat(rule.param("acceptWhitespace").defaultValue()).isEqualTo("false");
    assertThat(rule.param("acceptWhitespace").description())
        .isEqualTo("Accept whitespaces on the line");

    assertThat(rule.debtSubCharacteristicKey())
        .isEqualTo(RulesDefinition.SubCharacteristics.INTEGRATION_TESTABILITY);
    assertThat(rule.debtRemediationFunction().type())
        .isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
    assertThat(rule.debtRemediationFunction().coefficient()).isEqualTo("1h");
    assertThat(rule.debtRemediationFunction().offset()).isEqualTo("30min");
    assertThat(rule.effortToFixDescription()).isEqualTo("x1 effort to fix");
  }
  BulkChangeResult bulkActivate(
      RuleQuery ruleQuery, QualityProfileKey profileKey, @Nullable String severity) {
    BulkChangeResult result = new BulkChangeResult();
    RuleIndex ruleIndex = index.get(RuleIndex.class);
    DbSession dbSession = db.openSession(false);
    try {
      Result<Rule> ruleSearchResult =
          ruleIndex.search(ruleQuery, new QueryOptions().setScroll(true));
      Iterator<Rule> rules = ruleSearchResult.scroll();
      while (rules.hasNext()) {
        Rule rule = rules.next();
        try {
          ActiveRuleKey key = ActiveRuleKey.of(profileKey, rule.key());
          RuleActivation activation = new RuleActivation(key);
          activation.setSeverity(severity);
          List<ActiveRuleChange> changes = activate(dbSession, activation);
          result.addChanges(changes);
          result.incrementSucceeded();

        } catch (BadRequestException e) {
          // other exceptions stop the bulk activation
          result.incrementFailed();
          // TODO result.addMessage
        }
      }
      dbSession.commit();
    } finally {
      dbSession.close();
    }
    return result;
  }
  @Test
  public void reactivate_disabled_custom_rules() {
    Rule templateRule = index.getByKey(RuleKey.of("xoo", "template1"));

    // Create custom rule
    RuleKey customRuleKey =
        tester
            .get(RuleCreator.class)
            .create(
                NewRule.createForCustomRule("CUSTOM_RULE", templateRule.key())
                    .setName("My custom")
                    .setHtmlDescription("Some description")
                    .setSeverity(Severity.MAJOR)
                    .setStatus(RuleStatus.READY));
    dbSession.commit();
    dbSession.clearCache();
    assertThat(index.getByKey(customRuleKey).status()).isEqualTo(RuleStatus.READY);

    // Restart without template rule
    rulesDefinition.includeTemplate1 = false;
    tester.get(Platform.class).executeStartupTasks();
    dbSession.clearCache();

    // Verify custom rule is removed
    assertThat(index.getByKey(customRuleKey).status()).isEqualTo(RuleStatus.REMOVED);

    // Restart with template rule
    rulesDefinition.includeTemplate1 = true;
    tester.get(Platform.class).executeStartupTasks();
    dbSession.clearCache();

    // Verify custom rule is reactivate
    assertThat(index.getByKey(customRuleKey).status()).isEqualTo(RuleStatus.READY);
  }
  @Test
  public void not_update_custom_rule_from_template_if_no_change() throws Exception {
    Rule templateRule = index.getByKey(RuleKey.of("xoo", "template1"));

    // Create custom rule
    RuleKey customRuleKey =
        tester
            .get(RuleCreator.class)
            .create(
                NewRule.createForCustomRule("CUSTOM_RULE", templateRule.key())
                    .setName("My custom")
                    .setHtmlDescription("Some description")
                    .setSeverity(Severity.MAJOR)
                    .setStatus(RuleStatus.READY)
                    .setParameters(ImmutableMap.of("format", "txt")));
    dbSession.commit();
    dbSession.clearCache();

    // Store updated at date
    Date updatedAt = index.getByKey(customRuleKey).updatedAt();

    // Re-execute startup tasks
    tester.get(Platform.class).executeStartupTasks();

    // Verify custom rule has not been updated
    Rule customRuleReloaded = index.getByKey(customRuleKey);
    assertThat(customRuleReloaded.updatedAt()).isEqualTo(updatedAt);
  }
  @Test
  public void not_update_rule_if_no_change() throws Exception {
    // Store updated at date
    Date updatedAt = index.getByKey(RuleTesting.XOO_X1).updatedAt();

    // Re-execute startup tasks
    tester.get(Platform.class).executeStartupTasks();
    dbSession.clearCache();

    // Verify rule has not been updated
    Rule customRuleReloaded = index.getByKey(RuleTesting.XOO_X1);
    assertThat(DateUtils.isSameInstant(customRuleReloaded.updatedAt(), updatedAt));
  }
  @Test
  public void remove_debt_rule() throws Exception {
    verifyRulesInDb();

    // Set some default debt on x2 rule, which has no debt provided by th plugin
    RuleDto ruleDto = db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X2);
    db.ruleDao()
        .update(
            dbSession,
            ruleDto
                .setDefaultSubCharacteristicId(
                    db.debtCharacteristicDao()
                        .selectByKey(
                            RulesDefinition.SubCharacteristics.INTEGRATION_TESTABILITY, dbSession)
                        .getId())
                .setDefaultRemediationFunction("LINEAR_OFFSET")
                .setDefaultRemediationCoefficient("2h")
                .setDefaultRemediationOffset("35min"));
    dbSession.commit();
    dbSession.clearCache();

    // Re-execute startup tasks
    tester.get(Platform.class).executeStartupTasks();

    // Verify default debt has been removed
    Rule ruleReloaded = index.getByKey(RuleTesting.XOO_X2);
    assertThat(ruleReloaded.debtSubCharacteristicKey()).isNull();
    assertThat(ruleReloaded.debtRemediationFunction()).isNull();
  }
  @Test
  public void update_debt_rule() throws Exception {
    verifyRulesInDb();

    // Update x1 rule
    RuleDto ruleDto = db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X1);
    db.ruleDao()
        .update(
            dbSession,
            ruleDto
                .setDefaultSubCharacteristicId(123456)
                .setDefaultRemediationFunction("LINEAR_OFFSET")
                .setDefaultRemediationCoefficient("2h")
                .setDefaultRemediationOffset("35min"));
    dbSession.commit();
    dbSession.clearCache();

    // Re-execute startup tasks
    tester.get(Platform.class).executeStartupTasks();

    // Verify default debt has been reset to plugin definition
    Rule ruleReloaded = index.getByKey(RuleTesting.XOO_X1);
    assertThat(ruleReloaded.debtSubCharacteristicKey())
        .isEqualTo(RulesDefinition.SubCharacteristics.INTEGRATION_TESTABILITY);
    assertThat(ruleReloaded.debtRemediationFunction().type())
        .isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
    assertThat(ruleReloaded.debtRemediationFunction().coefficient()).isEqualTo("1h");
    assertThat(ruleReloaded.debtRemediationFunction().offset()).isEqualTo("30min");
  }
  @Test
  public void update_rule() {
    verifyRulesInDb();

    // The plugin X1 will be updated
    rulesDefinition.includeX1 = false;
    rulesDefinition.includeX1bis = true;
    tester.get(Platform.class).executeStartupTasks();

    Rule rule = index.getByKey(RuleTesting.XOO_X1);
    assertThat(rule.severity()).isEqualTo(Severity.INFO);
    assertThat(rule.name()).isEqualTo("x1 name updated");
    assertThat(rule.htmlDescription()).isEqualTo("x1 desc updated");
    assertThat(rule.systemTags()).contains("tag1", "tag2");

    assertThat(rule.params()).hasSize(2);

    assertThat(rule.param("acceptWhitespace").type()).isEqualTo(RuleParamType.BOOLEAN);
    assertThat(rule.param("acceptWhitespace").defaultValue()).isEqualTo("true");
    assertThat(rule.param("acceptWhitespace").description())
        .isEqualTo("Accept whitespaces on the line updated");

    // New parameter
    assertThat(rule.param("format").type()).isEqualTo(RuleParamType.TEXT);
    assertThat(rule.param("format").defaultValue()).isEqualTo("txt");
    assertThat(rule.param("format").description()).isEqualTo("Format");

    assertThat(rule.debtSubCharacteristicKey())
        .isEqualTo(RulesDefinition.SubCharacteristics.INSTRUCTION_RELIABILITY);
    assertThat(rule.debtRemediationFunction().type())
        .isEqualTo(DebtRemediationFunction.Type.LINEAR);
    assertThat(rule.debtRemediationFunction().coefficient()).isEqualTo("2h");
    assertThat(rule.debtRemediationFunction().offset()).isNull();
    assertThat(rule.effortToFixDescription()).isEqualTo("x1 effort to fix updated");
  }
  @Test
  public void update_custom_rule_from_template() throws Exception {
    Rule templateRule = index.getByKey(RuleKey.of("xoo", "template1"));

    // Create custom rule
    RuleKey customRuleKey =
        tester
            .get(RuleCreator.class)
            .create(
                NewRule.createForCustomRule("CUSTOM_RULE", templateRule.key())
                    .setName("My custom")
                    .setHtmlDescription("Some description")
                    .setSeverity(Severity.MAJOR)
                    .setStatus(RuleStatus.READY)
                    .setParameters(ImmutableMap.of("format", "txt")));

    // Update custom rule
    RuleDto customRuleDto = db.ruleDao().getByKey(dbSession, customRuleKey);
    db.ruleDao()
        .update(
            dbSession,
            customRuleDto
                .setLanguage("other language")
                .setConfigKey("other config key")
                .setDefaultSubCharacteristicId(45)
                .setDefaultRemediationFunction("LINEAR_OFFSET")
                .setDefaultRemediationCoefficient("1h")
                .setDefaultRemediationOffset("5min")
                .setEffortToFixDescription("effort to fix desc"));
    dbSession.commit();
    dbSession.clearCache();

    // Re-execute startup tasks
    tester.get(Platform.class).executeStartupTasks();

    // Verify custom rule has been restore from the template
    Rule customRule = index.getByKey(customRuleKey);
    assertThat(customRule.language()).isEqualTo("xoo");
    assertThat(customRule.internalKey()).isNull();
    assertThat(customRule.debtSubCharacteristicKey()).isNull();
    assertThat(customRule.debtRemediationFunction()).isNull();
  }
  @Test
  public void not_disable_manual_rules() {
    // Create manual rule
    RuleKey manualRuleKey =
        tester
            .get(RuleCreator.class)
            .create(
                NewRule.createForManualRule("MANUAL_RULE")
                    .setName("My manual")
                    .setHtmlDescription("Some description"));
    dbSession.commit();
    dbSession.clearCache();
    assertThat(index.getByKey(manualRuleKey).status()).isEqualTo(RuleStatus.READY);

    // Restart
    tester.get(Platform.class).executeStartupTasks();

    // Verify manual rule is still ready
    assertThat(index.getByKey(manualRuleKey).status()).isEqualTo(RuleStatus.READY);
  }
 BulkChangeResult bulkDeactivate(RuleQuery ruleQuery, QualityProfileKey profile) {
   DbSession dbSession = db.openSession(false);
   try {
     RuleIndex ruleIndex = index.get(RuleIndex.class);
     BulkChangeResult result = new BulkChangeResult();
     Result<Rule> ruleSearchResult =
         ruleIndex.search(ruleQuery, new QueryOptions().setScroll(true));
     Iterator<Rule> rules = ruleSearchResult.scroll();
     while (rules.hasNext()) {
       Rule rule = rules.next();
       ActiveRuleKey key = ActiveRuleKey.of(profile, rule.key());
       List<ActiveRuleChange> changes = deactivate(dbSession, key);
       result.addChanges(changes);
       result.incrementSucceeded();
     }
     dbSession.commit();
     return result;
   } finally {
     dbSession.close();
   }
 }
  @Test
  public void not_update_custom_rule_params_from_template() throws Exception {
    Rule templateRule = index.getByKey(RuleKey.of("xoo", "template1"));

    // Create custom rule
    RuleKey customRuleKey =
        tester
            .get(RuleCreator.class)
            .create(
                NewRule.createForCustomRule("CUSTOM_RULE", templateRule.key())
                    .setName("My custom")
                    .setHtmlDescription("Some description")
                    .setSeverity(Severity.MAJOR)
                    .setStatus(RuleStatus.READY)
                    .setParameters(ImmutableMap.of("format", "txt")));
    dbSession.commit();
    dbSession.clearCache();

    // Update custom rule param name
    RuleDto customRuleDto = db.ruleDao().getByKey(dbSession, customRuleKey);
    RuleParamDto customRuleParamDto =
        db.ruleDao().findRuleParamsByRuleKey(dbSession, customRuleKey).get(0);
    db.ruleDao().removeRuleParam(dbSession, customRuleDto, customRuleParamDto);
    db.ruleDao().addRuleParam(dbSession, customRuleDto, customRuleParamDto.setName("format2"));
    dbSession.commit();
    dbSession.clearCache();

    // Verify param has been updated
    Rule customRule = index.getByKey(customRuleKey);
    assertThat(customRule.params()).hasSize(1);
    assertThat(customRule.params().get(0).key()).isEqualTo("format2");

    // Re-execute startup tasks
    tester.get(Platform.class).executeStartupTasks();

    // Verify custom rule param has not been changed!
    Rule customRuleReloaded = index.getByKey(customRuleKey);
    assertThat(customRuleReloaded.params().get(0).key()).isEqualTo("format2");
  }
  /**
   * support the use-case: 1. start server 2. stop server 3. drop elasticsearch index: rm -rf
   * data/es 4. start server -> db is up-to-date (no changes) but rules must be re-indexed
   */
  @Test
  public void index_even_if_no_changes() throws Exception {
    verifyRulesInDb();

    // clear ES but keep db
    tester.clearIndexes();
    verifyRulesInDb();
    Result<Rule> searchResult = index.search(new RuleQuery(), new QueryOptions());
    assertThat(searchResult.getTotal()).isEqualTo(0);
    assertThat(searchResult.getHits()).hasSize(0);

    // db is not updated (same rules) but es must be reindexed
    tester.get(Platform.class).executeStartupTasks();

    index = tester.get(RuleIndex.class);

    verifyRulesInDb();
    searchResult = index.search(new RuleQuery().setKey("xoo:x1"), new QueryOptions());
    assertThat(searchResult.getTotal()).isEqualTo(1);
    assertThat(searchResult.getHits()).hasSize(1);
    assertThat(searchResult.getHits().get(0).params()).hasSize(1);
  }
  @Test
  public void remove_end_user_tags_that_are_declared_as_system() {
    verifyRulesInDb();

    Rule rule = index.getByKey(RuleTesting.XOO_X1);
    assertThat(rule.systemTags()).contains("tag1");
    assertThat(rule.tags()).isEmpty();

    // Add a user tag
    tester
        .get(RuleUpdater.class)
        .update(
            RuleUpdate.createForPluginRule(rule.key()).setTags(newHashSet("user-tag")),
            UserSession.get());
    dbSession.clearCache();

    // Verify tags
    Rule ruleUpdated = index.getByKey(RuleTesting.XOO_X1);
    assertThat(ruleUpdated.systemTags()).contains("tag1");
    assertThat(ruleUpdated.tags()).contains("user-tag");

    // The plugin X1 will be updated
    rulesDefinition.includeX1 = false;
    rulesDefinition.includeX1bis = true;
    tester.get(Platform.class).executeStartupTasks();
    dbSession.clearCache();

    // User tag should become a system tag
    RuleDto ruleDtoReloaded = db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X1);
    assertThat(ruleDtoReloaded.getSystemTags()).contains("tag1", "tag2", "user-tag");
    assertThat(ruleDtoReloaded.getTags()).isEmpty();

    // User tag should become a system tag
    Rule ruleReloaded = index.getByKey(RuleTesting.XOO_X1);
    assertThat(ruleReloaded.systemTags()).contains("tag1", "tag2", "user-tag");
    assertThat(ruleReloaded.tags()).isEmpty();
  }