@Test
  public void do_not_deactivate_removed_rules_if_repository_accidentaly_uninstalled()
      throws Exception {
    MockUserSession.set()
        .setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)
        .setLogin("me");

    // create a profile and activate rule
    db.qualityProfileDao().insert(dbSession, QProfileTesting.newXooP1());
    dbSession.commit();
    dbSession.clearCache();
    RuleActivation activation = new RuleActivation(RuleTesting.XOO_X1);
    tester.get(QProfileService.class).activate(QProfileTesting.XOO_P1_KEY, activation);
    dbSession.clearCache();

    // restart without x1, x2, template1 -> keep active rule of x1
    rulesDefinition.includeX1 = false;
    rulesDefinition.includeX2 = false;
    rulesDefinition.includeTemplate1 = false;
    tester.get(Platform.class).executeStartupTasks();
    dbSession.clearCache();
    assertThat(db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X1).getStatus())
        .isEqualTo(RuleStatus.REMOVED);
    assertThat(db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X2).getStatus())
        .isEqualTo(RuleStatus.REMOVED);
    assertThat(db.activeRuleDao().findByProfileKey(dbSession, QProfileTesting.XOO_P1_KEY))
        .hasSize(1);
  }
  @Test
  public void deactivate_removed_rules_only_if_repository_still_exists() throws Exception {
    MockUserSession.set()
        .setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)
        .setLogin("me");

    // create a profile and activate rule
    db.qualityProfileDao().insert(dbSession, QProfileTesting.newXooP1());
    dbSession.commit();
    dbSession.clearCache();
    RuleActivation activation = new RuleActivation(RuleTesting.XOO_X1);
    tester.get(QProfileService.class).activate(QProfileTesting.XOO_P1_KEY, activation);
    dbSession.clearCache();

    // restart, x2 still exists -> deactivate x1
    rulesDefinition.includeX1 = false;
    rulesDefinition.includeX2 = true;
    tester.get(Platform.class).executeStartupTasks();
    dbSession.clearCache();
    assertThat(db.ruleDao().getByKey(dbSession, RuleKey.of("xoo", "x1")).getStatus())
        .isEqualTo(RuleStatus.REMOVED);
    assertThat(db.ruleDao().getByKey(dbSession, RuleKey.of("xoo", "x2")).getStatus())
        .isEqualTo(RuleStatus.READY);
    assertThat(db.activeRuleDao().findByProfileKey(dbSession, QProfileTesting.XOO_P1_KEY))
        .hasSize(0);
  }
  @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 stat_for_all_profiles() {
    MockUserSession.set()
        .setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)
        .setLogin("me");

    service.activate(XOO_P1_KEY, new RuleActivation(RuleTesting.XOO_X1).setSeverity("MINOR"));
    service.activate(XOO_P2_KEY, new RuleActivation(RuleTesting.XOO_X1).setSeverity("BLOCKER"));
    dbSession.clearCache();

    Map<String, Multimap<String, FacetValue>> stats = loader.getAllProfileStats();

    assertThat(stats.size()).isEqualTo(2);
    assertThat(stats.get(XOO_P1_KEY).size()).isEqualTo(3);
    assertThat(
            stats.get(XOO_P1_KEY).get(ActiveRuleNormalizer.ActiveRuleField.SEVERITY.field()).size())
        .isEqualTo(1);
    assertThat(
            stats
                .get(XOO_P1_KEY)
                .get(ActiveRuleNormalizer.ActiveRuleField.INHERITANCE.field())
                .size())
        .isEqualTo(1);
    assertThat(stats.get(XOO_P1_KEY).get("countActiveRules").size()).isEqualTo(1);
  }
  @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 search_by_characteristics() throws InterruptedException {
    CharacteristicDto char1 = DebtTesting.newCharacteristicDto("RELIABILITY");
    db.debtCharacteristicDao().insert(char1, dbSession);

    CharacteristicDto char11 =
        DebtTesting.newCharacteristicDto("SOFT_RELIABILITY").setParentId(char1.getId());
    db.debtCharacteristicDao().insert(char11, dbSession);
    dbSession.commit();

    dao.insert(
        dbSession,
        RuleTesting.newDto(RuleKey.of("java", "S001")).setSubCharacteristicId(char11.getId()));

    dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("javascript", "S002")));

    dbSession.commit();
    dbSession.clearCache();

    RuleQuery query;
    Result<Rule> results;

    // 0. we have 2 rules in index
    results = index.search(new RuleQuery(), new QueryOptions());
    assertThat(results.getHits()).hasSize(2);

    // filter by non-subChar
    query = new RuleQuery().setDebtCharacteristics(ImmutableSet.of("toto"));
    assertThat(index.search(query, new QueryOptions()).getHits()).isEmpty();

    // filter by subChar
    query = new RuleQuery().setDebtCharacteristics(ImmutableSet.of(char11.getKey()));
    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1);

    // filter by Char
    query = new RuleQuery().setDebtCharacteristics(ImmutableSet.of(char1.getKey()));
    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1);

    // filter by Char and SubChar
    query =
        new RuleQuery().setDebtCharacteristics(ImmutableSet.of(char11.getKey(), char1.getKey()));
    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1);

    // match by Char
    query = new RuleQuery().setQueryText(char1.getKey());
    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1);

    // match by SubChar
    query = new RuleQuery().setQueryText(char11.getKey());
    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1);

    // match by SubChar & Char
    query = new RuleQuery().setQueryText(char11.getKey() + " " + char1.getKey());
    assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1);
  }
  @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");
  }
  @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 set_default() throws Exception {
    MockUserSession.set()
        .setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)
        .setLogin("me");

    assertThat(service.getDefault("xoo")).isNull();

    service.setDefault(XOO_P1_KEY);
    dbSession.clearCache();

    assertThat(service.getDefault("xoo").getKey()).isEqualTo(XOO_P1_KEY);
  }
  @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();
  }
  @Before
  public void before() {
    tester.clearDbAndIndexes();
    rulesDefinition.includeX1 = true;
    rulesDefinition.includeX1bis = false;
    rulesDefinition.includeX2 = true;
    rulesDefinition.includeTemplate1 = true;
    rulesDefinition.includeRuleLinkedToRootCharacteristic = false;
    tester.get(Platform.class).executeStartupTasks();
    db = tester.get(DbClient.class);
    dbSession = tester.get(DbClient.class).openSession(false);
    dbSession.clearCache();

    index = tester.get(RuleIndex.class);
  }
  @Test
  public void count_by_all_profiles() throws Exception {
    MockUserSession.set()
        .setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)
        .setLogin("me");

    service.activate(XOO_P1_KEY, new RuleActivation(RuleTesting.XOO_X1).setSeverity("BLOCKER"));
    service.activate(XOO_P2_KEY, new RuleActivation(RuleTesting.XOO_X1).setSeverity("BLOCKER"));

    dbSession.clearCache();

    Map<String, Long> counts = loader.countAllActiveRules();
    assertThat(counts).hasSize(2);
    assertThat(counts.keySet()).containsOnly(XOO_P1_KEY, XOO_P2_KEY);
    assertThat(counts.values()).containsOnly(1L, 1L);
  }
Example #15
0
  @Test
  public void search_by_profile() throws InterruptedException {
    QualityProfileDto qualityProfileDto1 = QProfileTesting.newXooP1();
    QualityProfileDto qualityProfileDto2 = QProfileTesting.newXooP2();
    db.qualityProfileDao().insert(dbSession, qualityProfileDto1, qualityProfileDto2);

    RuleDto rule1 = RuleTesting.newXooX1();
    RuleDto rule2 = RuleTesting.newXooX2();
    RuleDto rule3 = RuleTesting.newXooX3();
    dao.insert(dbSession, rule1, rule2, rule3);

    db.activeRuleDao()
        .insert(
            dbSession,
            ActiveRuleDto.createFor(qualityProfileDto1, rule1).setSeverity("BLOCKER"),
            ActiveRuleDto.createFor(qualityProfileDto2, rule1).setSeverity("BLOCKER"),
            ActiveRuleDto.createFor(qualityProfileDto1, rule2).setSeverity("BLOCKER"));
    dbSession.commit();
    dbSession.clearCache();

    // 1. get all active rules.
    Result<Rule> result = index.search(new RuleQuery().setActivation(true), new QueryOptions());
    assertThat(result.getHits()).hasSize(2);

    // 2. get all inactive rules.
    result = index.search(new RuleQuery().setActivation(false), new QueryOptions());
    assertThat(result.getHits()).hasSize(1);
    assertThat(result.getHits().get(0).name()).isEqualTo(rule3.getName());

    // 3. get all rules not active on profile
    index.search(
        new RuleQuery().setActivation(false).setQProfileKey(qualityProfileDto2.getKey()),
        new QueryOptions());
    // TODO
    assertThat(result.getHits()).hasSize(1);

    // 4. get all active rules on profile
    result =
        index.search(
            new RuleQuery().setActivation(true).setQProfileKey(qualityProfileDto2.getKey()),
            new QueryOptions());
    assertThat(result.getHits()).hasSize(1);
    assertThat(result.getHits().get(0).name()).isEqualTo(rule1.getName());
  }
  @Before
  public void before() {
    tester.clearDbAndIndexes();
    db = tester.get(DbClient.class);
    dbSession = db.openSession(false);
    service = tester.get(QProfileService.class);
    loader = tester.get(QProfileLoader.class);
    activator = tester.get(RuleActivator.class);

    // create pre-defined rules
    RuleDto xooRule1 = RuleTesting.newXooX1().setSeverity("MINOR");
    db.ruleDao().insert(dbSession, xooRule1);

    // create pre-defined profiles P1 and P2
    db.qualityProfileDao()
        .insert(dbSession, QProfileTesting.newXooP1(), QProfileTesting.newXooP2());
    dbSession.commit();
    dbSession.clearCache();
  }
  @Test
  public void reactivate_disabled_rules() {
    verifyRulesInDb();

    // Disable plugin X1
    rulesDefinition.includeX1 = false;
    tester.get(Platform.class).executeStartupTasks();

    RuleDto rule = db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X1);
    assertThat(rule.getStatus()).isEqualTo(RuleStatus.REMOVED);
    dbSession.clearCache();

    // Reactivate plugin X1
    rulesDefinition.includeX1 = true;
    tester.get(Platform.class).executeStartupTasks();

    RuleDto ruleReloaded = db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X1);
    assertThat(ruleReloaded.getStatus()).isEqualTo(RuleStatus.READY);
  }
  @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);
  }
Example #20
0
  @Test
  public void search_by_characteristics_with_default_and_overridden_char()
      throws InterruptedException {
    CharacteristicDto char1 = DebtTesting.newCharacteristicDto("RELIABILITY");
    db.debtCharacteristicDao().insert(char1, dbSession);

    CharacteristicDto char11 =
        DebtTesting.newCharacteristicDto("SOFT_RELIABILITY").setParentId(char1.getId());
    db.debtCharacteristicDao().insert(char11, dbSession);
    dbSession.commit();

    CharacteristicDto char2 = DebtTesting.newCharacteristicDto("TESTABILITY");
    db.debtCharacteristicDao().insert(char2, dbSession);

    CharacteristicDto char21 =
        DebtTesting.newCharacteristicDto("UNIT_TESTABILITY").setParentId(char2.getId());
    db.debtCharacteristicDao().insert(char21, dbSession);
    dbSession.commit();

    // Rule with only default sub characteristic -> should be find by char11 and char1
    dao.insert(
        dbSession,
        RuleTesting.newDto(RuleKey.of("java", "S001"))
            .setSubCharacteristicId(char11.getId())
            .setDefaultSubCharacteristicId(null));

    // Rule with only sub characteristic -> should be find by char11 and char1
    dao.insert(
        dbSession,
        RuleTesting.newDto(RuleKey.of("java", "S002"))
            .setSubCharacteristicId(null)
            .setDefaultSubCharacteristicId(char11.getId()));

    // Rule with both default sub characteristic and overridden sub characteristic -> should only be
    // find by char21 and char2
    dao.insert(
            dbSession,
            RuleTesting.newDto(RuleKey.of("java", "S003")).setSubCharacteristicId(char21.getId()))
        .setDefaultSubCharacteristicId(char11.getId());

    // Rule with both default sub characteristic and overridden sub characteristic and with same
    // values -> should be find by char11 and char1
    dao.insert(
            dbSession,
            RuleTesting.newDto(RuleKey.of("java", "S004")).setSubCharacteristicId(char11.getId()))
        .setDefaultSubCharacteristicId(char11.getId());

    dbSession.commit();
    dbSession.clearCache();

    RuleQuery query;
    Result<Rule> results;

    // 0. we have 4 rules in index
    results = index.search(new RuleQuery(), new QueryOptions());
    assertThat(results.getHits()).hasSize(4);

    // filter by subChar
    query = new RuleQuery().setDebtCharacteristics(ImmutableSet.of(char11.getKey()));
    assertThat(ruleKeys(index.search(query, new QueryOptions()).getHits()))
        .containsOnly("S001", "S002", "S004");

    query = new RuleQuery().setDebtCharacteristics(ImmutableSet.of(char21.getKey()));
    assertThat(ruleKeys(index.search(query, new QueryOptions()).getHits())).containsOnly("S003");

    // filter by Char
    query = new RuleQuery().setDebtCharacteristics(ImmutableSet.of(char1.getKey()));
    assertThat(ruleKeys(index.search(query, new QueryOptions()).getHits()))
        .containsOnly("S001", "S002", "S004");

    query = new RuleQuery().setDebtCharacteristics(ImmutableSet.of(char2.getKey()));
    assertThat(ruleKeys(index.search(query, new QueryOptions()).getHits())).containsOnly("S003");

    // filter by Char and SubChar
    query =
        new RuleQuery()
            .setDebtCharacteristics(
                ImmutableSet.of(char11.getKey(), char1.getKey(), char2.getKey(), char21.getKey()));
    assertThat(ruleKeys(index.search(query, new QueryOptions()).getHits()))
        .containsOnly("S001", "S002", "S003", "S004");
  }