]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5174 Rules WS : search by languages, severities, statuses, tags and characteristics
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 28 Mar 2014 15:04:32 +0000 (16:04 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 28 Mar 2014 15:04:41 +0000 (16:04 +0100)
sonar-server/src/main/java/org/sonar/server/rule/RuleQuery.java
sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java
sonar-server/src/main/java/org/sonar/server/rule/ws/RuleSearchWsHandler.java
sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWs.java
sonar-server/src/main/java/org/sonar/server/util/RubyUtils.java
sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java
sonar-server/src/test/java/org/sonar/server/rule/ws/RuleSearchWsHandlerTest.java
sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule1.json
sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule2.json
sonar-server/src/test/resources/org/sonar/server/rule/ws/RuleSearchWsHandlerTest/search_rules.json [new file with mode: 0644]

index c6fa805af38c5c2097296be22579a57c0bb80898..30796ea285021a04b160f759588b107f68d7a8f0 100644 (file)
@@ -32,14 +32,17 @@ import java.util.Collections;
  */
 public class RuleQuery {
 
-  public static final int DEFAULT_PAGE_INDEX = 1;
+  private static final int DEFAULT_PAGE_INDEX = 1;
   private static final int DEFAULT_PAGE_SIZE = 25;
 
   private String key;
   private String query;
-  private String characteristic;
-  private String language;
+  private Collection<String> languages;
   private Collection<String> repositories;
+  private Collection<String> severities;
+  private Collection<String> statuses;
+  private Collection<String> tags;
+  private Collection<String> debtCharacteristics;
 
   private int pageSize;
   private int pageIndex;
@@ -47,9 +50,12 @@ public class RuleQuery {
   private RuleQuery(Builder builder) {
     this.key = builder.key;
     this.query = builder.query;
-    this.language = builder.language;
+    this.languages = defaultCollection(builder.languages);
     this.repositories = defaultCollection(builder.repositories);
-    this.characteristic = builder.characteristic;
+    this.severities = defaultCollection(builder.severities);
+    this.statuses = defaultCollection(builder.statuses);
+    this.tags = defaultCollection(builder.tags);
+    this.debtCharacteristics = defaultCollection(builder.debtCharacteristics);
     this.pageSize = builder.pageSize;
     this.pageIndex = builder.pageIndex;
   }
@@ -64,18 +70,28 @@ public class RuleQuery {
     return query;
   }
 
-  @CheckForNull
-  public String language() {
-    return language;
+  public Collection<String> languages() {
+    return languages;
   }
 
   public Collection<String> repositories() {
     return repositories;
   }
 
-  @CheckForNull
-  public String characteristic() {
-    return characteristic;
+  public Collection<String> severities() {
+    return severities;
+  }
+
+  public Collection<String> statuses() {
+    return statuses;
+  }
+
+  public Collection<String> tags() {
+    return tags;
+  }
+
+  public Collection<String> debtCharacteristics() {
+    return debtCharacteristics;
   }
 
   public int pageSize() {
@@ -94,9 +110,12 @@ public class RuleQuery {
 
     private String key;
     private String query;
-    private String characteristic;
-    private String language;
+    private Collection<String> languages;
     private Collection<String> repositories;
+    private Collection<String> severities;
+    private Collection<String> statuses;
+    private Collection<String> tags;
+    private Collection<String> debtCharacteristics;
 
     private Integer pageSize;
     private Integer pageIndex;
@@ -111,18 +130,33 @@ public class RuleQuery {
       return this;
     }
 
-    public Builder language(@Nullable String language) {
-      this.language = language;
+    public Builder languages(@Nullable Collection<String> languages) {
+      this.languages = languages;
       return this;
     }
 
-    public Builder repositories(Collection<String> repositories) {
+    public Builder repositories(@Nullable Collection<String> repositories) {
       this.repositories = repositories;
       return this;
     }
 
-    public Builder characteristic(@Nullable String characteristic) {
-      this.characteristic = characteristic;
+    public Builder severities(@Nullable Collection<String> severities) {
+      this.severities = severities;
+      return this;
+    }
+
+    public Builder statuses(@Nullable Collection<String> statuses) {
+      this.statuses = statuses;
+      return this;
+    }
+
+    public Builder tags(@Nullable Collection<String> tags) {
+      this.tags = tags;
+      return this;
+    }
+
+    public Builder debtCharacteristics(@Nullable Collection<String> debtCharacteristics) {
+      this.debtCharacteristics = debtCharacteristics;
       return this;
     }
 
index ea653871e663a29b83095e2e82be2911ab53dbd8..4c9a0e570c83dcf3d7568055616b2b4997a8138a 100644 (file)
@@ -31,6 +31,7 @@ import org.elasticsearch.common.io.BytesStream;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.index.query.BoolFilterBuilder;
+import org.elasticsearch.index.query.FilterBuilder;
 import org.elasticsearch.index.query.FilterBuilders;
 import org.elasticsearch.index.query.MatchQueryBuilder.Operator;
 import org.elasticsearch.index.query.QueryBuilders;
@@ -137,21 +138,21 @@ public class RuleRegistry {
       mainFilter.must(FilterBuilders.queryFilter(
         QueryBuilders.multiMatchQuery(query.query(), RuleDocument.FIELD_NAME + ".search", RuleDocument.FIELD_KEY).operator(Operator.AND)));
     }
-    if (query.characteristic() != null) {
+    addMustTermOrTerms(mainFilter, RuleDocument.FIELD_LANGUAGE, query.languages());
+    addMustTermOrTerms(mainFilter, RuleDocument.FIELD_REPOSITORY_KEY, query.repositories());
+    addMustTermOrTerms(mainFilter, RuleDocument.FIELD_SEVERITY, query.severities());
+    addMustTermOrTerms(mainFilter, RuleDocument.FIELD_STATUS, query.statuses());
+    if (!query.debtCharacteristics().isEmpty()) {
       mainFilter.must(FilterBuilders.queryFilter(
-          QueryBuilders.multiMatchQuery(query.characteristic(), RuleDocument.FIELD_CHARACTERISTIC_KEY, RuleDocument.FIELD_SUB_CHARACTERISTIC_KEY).operator(Operator.OR))
+          QueryBuilders.multiMatchQuery(query.debtCharacteristics(), RuleDocument.FIELD_CHARACTERISTIC_KEY, RuleDocument.FIELD_SUB_CHARACTERISTIC_KEY).operator(Operator.OR))
       );
     }
-    if (query.language() != null) {
-      mainFilter.must(termFilter(RuleDocument.FIELD_LANGUAGE, query.language()));
-    }
-    if (!query.repositories().isEmpty()) {
-      if (query.repositories().size() == 1) {
-        mainFilter.must(termFilter(RuleDocument.FIELD_REPOSITORY_KEY, query.repositories().iterator().next()));
-      } else {
-        mainFilter.must(termsFilter(RuleDocument.FIELD_REPOSITORY_KEY, query.repositories().toArray()));
-      }
+    if (!query.tags().isEmpty()) {
+      mainFilter.must(FilterBuilders.queryFilter(
+          QueryBuilders.multiMatchQuery(query.tags(), RuleDocument.FIELD_ADMIN_TAGS, RuleDocument.FIELD_SYSTEM_TAGS).operator(Operator.OR))
+      );
     }
+
     Paging paging = Paging.create(query.pageSize(), query.pageIndex());
     SearchHits hits = searchIndex.executeRequest(
       searchIndex.client().prepareSearch(INDEX_RULES).setTypes(TYPE_RULE)
@@ -168,6 +169,25 @@ public class RuleRegistry {
     return new PagedResult<Rule>(rulesBuilder.build(), PagingResult.create(paging.pageSize(), paging.pageIndex(), hits.getTotalHits()));
   }
 
+  private static void addMustTermOrTerms(BoolFilterBuilder filter, String field, Collection<String> terms) {
+    FilterBuilder termOrTerms = getTermOrTerms(field, terms);
+    if (termOrTerms != null) {
+      filter.must(termOrTerms);
+    }
+  }
+
+  private static FilterBuilder getTermOrTerms(String field, Collection<String> terms) {
+    if (terms.isEmpty()) {
+      return null;
+    } else {
+      if (terms.size() == 1) {
+        return termFilter(field, terms.iterator().next());
+      } else {
+        return termsFilter(field, terms.toArray());
+      }
+    }
+  }
+
   /**
    * Create or update definition of rule identified by <code>ruleId</code>
    */
index 0fc33b2b4a3ddba6c3e070f4b28bcd27f6e1c91b..17bb13c49517bd6183f0131547bd8c851e5cde55 100644 (file)
@@ -23,6 +23,7 @@ package org.sonar.server.rule.ws;
 import org.sonar.api.resources.Language;
 import org.sonar.api.resources.Languages;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.server.debt.DebtRemediationFunction;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.RequestHandler;
 import org.sonar.api.server.ws.Response;
@@ -31,6 +32,7 @@ import org.sonar.server.paging.PagedResult;
 import org.sonar.server.rule.Rule;
 import org.sonar.server.rule.RuleQuery;
 import org.sonar.server.rule.Rules;
+import org.sonar.server.util.RubyUtils;
 
 import javax.annotation.CheckForNull;
 
@@ -56,8 +58,12 @@ public class RuleSearchWsHandler implements RequestHandler {
     if (ruleKeyParam == null) {
       PagedResult<Rule> searchResult = rules.find(RuleQuery.builder()
         .searchQuery(request.param("s"))
-        .language(request.param("language"))
-        .characteristic(request.param("characteristic"))
+        .languages(RubyUtils.toStrings(request.param("languages")))
+        .repositories(RubyUtils.toStrings(request.param("repositories")))
+        .severities(RubyUtils.toStrings(request.param("severities")))
+        .statuses(RubyUtils.toStrings(request.param("statuses")))
+        .tags(RubyUtils.toStrings(request.param("tags")))
+        .debtCharacteristics(RubyUtils.toStrings(request.param("debtCharacteristics")))
         .pageSize(request.paramAsInt("ps"))
         .pageIndex(request.paramAsInt("p"))
         .build());
@@ -104,6 +110,19 @@ public class RuleSearchWsHandler implements RequestHandler {
       .prop("key", rule.ruleKey().toString())
       .prop("name", rule.name())
       .prop("language", languageName)
+      .prop("status", rule.status())
     ;
+    DebtRemediationFunction function = rule.debtRemediationFunction();
+    if (function != null) {
+      json
+        .prop("debtCharacteristic", rule.debtCharacteristicKey())
+        .prop("debtCharacteristicName", rule.debtCharacteristicName())
+        .prop("debtSubCharacteristic", rule.debtSubCharacteristicKey())
+        .prop("debtSubCharacteristicName", rule.debtSubCharacteristicName())
+        .prop("debtRemediationFunction", function.type().name())
+        .prop("debtRemediationCoefficient", function.coefficient())
+        .prop("debtRemediationOffset", function.offset())
+      ;
+    }
   }
 }
index 1fba1f5f6cec2aef285b68a3ed4b28a952b98d78..294f2b7d141f307a3e964b51d0d5168c74e7fc5d 100644 (file)
@@ -46,6 +46,7 @@ public class RulesWs implements WebService {
       .setHandler(searchHandler)
       .createParam("s", "An optional query that will be matched against rule titles.")
       .createParam("k", "An optional query that will be matched exactly agains rule keys.")
+      // TODO add description for languages, repositories, etc. params
       .createParam("ps", "Optional page size (default is 25).")
       .createParam("p", "Optional page number (default is 0).");
 
index 5004f600ac69d8ac958dbb4b1791982dba6f7b41..db33e56e8ea30f925568a07b83a5d36c6c5f2075 100644 (file)
@@ -40,6 +40,7 @@ public class RubyUtils {
     // only static methods
   }
 
+  @CheckForNull
   public static List<String> toStrings(@Nullable Object o) {
     List<String> result = null;
     if (o != null) {
index b54a81f151c967b5c06698758d52c0e339ca88c2..eb57b4edef2bd456bb6e1ed4868961a785bd0ceb 100644 (file)
@@ -226,8 +226,7 @@ public class RuleRegistryTest {
     registry.bulkRegisterRules(rules, Maps.<Integer, CharacteristicDto>newHashMap(), params, tags);
     assertThat(registry.findIds(ImmutableMap.of("repositoryKey", "repo"))).hasSize(2);
 
-    Map<String, Object> rule2Document = esSetup.client().prepareGet("rules", "rule", Integer.toString(ruleId2))
-      .execute().actionGet().getSourceAsMap();
+    Map<String, Object> rule2Document = esSetup.client().prepareGet("rules", "rule", Integer.toString(ruleId2)).execute().actionGet().getSourceAsMap();
     assertThat((List<String>) rule2Document.get(RuleDocument.FIELD_SYSTEM_TAGS)).hasSize(2);
     assertThat((List<String>) rule2Document.get(RuleDocument.FIELD_ADMIN_TAGS)).hasSize(1);
   }
@@ -277,34 +276,6 @@ public class RuleRegistryTest {
     assertThat(esSetup.exists("rules", "rule", "3")).isFalse();
   }
 
-  @Test
-  public void find_rules_by_name() {
-    // Removed rule should not appear
-    assertThat(registry.find(RuleQuery.builder().build()).results()).hasSize(2);
-
-    // Search is case insensitive
-    assertThat(registry.find(RuleQuery.builder().searchQuery("one issue per line").build()).results()).hasSize(1);
-
-    // Search is ngram based
-    assertThat(registry.find(RuleQuery.builder().searchQuery("with param").build()).results()).hasSize(1);
-
-    // Search works also with key
-    assertThat(registry.find(RuleQuery.builder().searchQuery("OneIssuePerLine").build()).results()).hasSize(1);
-  }
-
-  @Test
-  public void find_rules_by_language() {
-    assertThat(registry.find(RuleQuery.builder().language("xoo").build()).results()).hasSize(2);
-    assertThat(registry.find(RuleQuery.builder().language("unknown").build()).results()).isEmpty();
-  }
-
-  @Test
-  public void find_rules_by_rule_repositories() {
-    assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("xoo")).build()).results()).hasSize(1);
-    assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("xoo", "xoo2")).build()).results()).hasSize(2);
-    assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("unknown")).build()).results()).isEmpty();
-  }
-
   @Test
   public void index_debt_definitions() {
     Map<Integer, CharacteristicDto> characteristics = newHashMap();
@@ -356,20 +327,61 @@ public class RuleRegistryTest {
   }
 
   @Test
-  public void find_rules_by_characteristic_or_sub_characteristic() {
-    Map<Integer, CharacteristicDto> characteristics = newHashMap();
-    characteristics.put(10, new CharacteristicDto().setId(10).setKey("REUSABILITY").setName("Reusability"));
-    characteristics.put(11, new CharacteristicDto().setId(11).setKey("MODULARITY").setName("Modularity").setParentId(10));
+  public void find_rules_by_name() {
+    // Removed rule should not appear
+    assertThat(registry.find(RuleQuery.builder().searchQuery("Removed rule").build()).results()).isEmpty();
 
-    List<RuleDto> rules = ImmutableList.of(new RuleDto().setId(10).setRepositoryKey("repo").setRuleKey("key1").setSeverity(Severity.MINOR)
-      .setDefaultSubCharacteristicId(11).setDefaultRemediationFunction("LINEAR").setDefaultRemediationCoefficient("2h"));
+    // Search is case insensitive
+    assertThat(registry.find(RuleQuery.builder().searchQuery("one issue per line").build()).results()).hasSize(1);
+
+    // Search is ngram based
+    assertThat(registry.find(RuleQuery.builder().searchQuery("with param").build()).results()).hasSize(1);
+
+    // Search works also with key
+    assertThat(registry.find(RuleQuery.builder().searchQuery("OneIssuePerLine").build()).results()).hasSize(1);
+  }
+
+  @Test
+  public void find_rules_by_languages() {
+    assertThat(registry.find(RuleQuery.builder().languages(newArrayList("xoo")).build()).results()).hasSize(2);
+    assertThat(registry.find(RuleQuery.builder().languages(newArrayList("unknown")).build()).results()).isEmpty();
+  }
+
+  @Test
+  public void find_rules_by_repositories() {
+    assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("xoo")).build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("xoo", "xoo2")).build()).results()).hasSize(2);
+    assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("unknown")).build()).results()).isEmpty();
+  }
+
+  @Test
+  public void find_rules_by_severities() {
+    assertThat(registry.find(RuleQuery.builder().severities(newArrayList("MAJOR")).build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().severities(newArrayList("MAJOR", "MINOR")).build()).results()).hasSize(2);
+    assertThat(registry.find(RuleQuery.builder().severities(newArrayList("unknown")).build()).results()).isEmpty();
+  }
 
-    registry.bulkRegisterRules(rules, characteristics, ArrayListMultimap.<Integer, RuleParamDto>create(),
-      ArrayListMultimap.<Integer, RuleRuleTagDto>create());
+  @Test
+  public void find_rules_by_statuses() {
+    assertThat(registry.find(RuleQuery.builder().statuses(newArrayList("READY")).build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().statuses(newArrayList("READY", "BETA")).build()).results()).hasSize(2);
+    assertThat(registry.find(RuleQuery.builder().statuses(newArrayList("unknown")).build()).results()).isEmpty();
+  }
 
-    assertThat(registry.find(RuleQuery.builder().characteristic("MODULARITY").build()).results()).hasSize(1);
-    assertThat(registry.find(RuleQuery.builder().characteristic("REUSABILITY").build()).results()).hasSize(1);
-    assertThat(registry.find(RuleQuery.builder().characteristic("Unknown").build()).results()).isEmpty();
+  @Test
+  public void find_rules_by_tags() {
+    assertThat(registry.find(RuleQuery.builder().tags(newArrayList("has-params")).build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().tags(newArrayList("keep-enabled")).build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().tags(newArrayList("has-params", "keep-enabled")).build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().tags(newArrayList("unknown")).build()).results()).isEmpty();
+  }
+
+  @Test
+  public void find_rules_by_characteristics() {
+    assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("MODULARITY")).build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("REUSABILITY")).build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("MODULARITY", "REUSABILITY")).build()).results()).hasSize(1);
+    assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("unknown")).build()).results()).isEmpty();
   }
 
   private String testFileAsString(String testFile) throws Exception {
index 1782b12bed799064f3f6aee888439b0ffad9e702..46050c53bc91f8879d88bfcf9f7fa741c48046e8 100644 (file)
@@ -59,10 +59,11 @@ public class RuleSearchWsHandlerTest {
     .setName("Avoid cycle")
     .setDescription("Avoid cycle between packages")
     .setLanguage("java")
+    .setStatus("READY")
     .setDebtCharacteristicKey("REUSABILITY")
     .setDebtCharacteristicName("Reusability")
-    .setDebtCharacteristicKey("MODULARITY")
-    .setDebtCharacteristicKey("Modularity")
+    .setDebtSubCharacteristicKey("MODULARITY")
+    .setDebtSubCharacteristicName("Modularity")
     .setDebtRemediationFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET, "1h", "15min"));
 
   WsTester tester;
@@ -86,9 +87,7 @@ public class RuleSearchWsHandlerTest {
 
     MockUserSession.set();
     WsTester.TestRequest request = tester.newRequest("list").setParam("ps", "10").setParam("p", "2");
-    request.execute().assertJson("{'more':false,'total':1,'results':["
-      + "{'key':'squid:AvoidCycle','name':'Avoid cycle','language':'Java'}"
-      + "]}");
+    request.execute().assertJson(getClass(), "search_rules.json");
   }
 
   @Test
@@ -103,46 +102,35 @@ public class RuleSearchWsHandlerTest {
 
     MockUserSession.set();
     WsTester.TestRequest request = tester.newRequest("list").setParam("k", ruleKey);
-    request.execute().assertJson("{'more':false,'total':1,'results':["
-      + "{'key':'squid:AvoidCycle','name':'Avoid cycle','language':'Java'}"
-      + "]}");
+    request.execute().assertJson(getClass(), "search_rules.json");
   }
 
   @Test
-  public void search_rule_by_language() throws Exception {
+  public void verify_rule_query_filters() throws Exception {
     Rule rule = ruleBuilder.build();
 
     when(rules.find(any(RuleQuery.class))).thenReturn(
       new PagedResult<Rule>(ImmutableList.of(rule), PagingResult.create(10, 1, 1)));
 
     MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("list").setParam("language", "java");
-    request.execute().assertJson("{'more':false,'total':1,'results':["
-      + "{'key':'squid:AvoidCycle','name':'Avoid cycle','language':'java'}"
-      + "]}");
+    tester.newRequest("list")
+      .setParam("languages", "java,js")
+      .setParam("repositories", "squid,pmd")
+      .setParam("severities", "MAJOR,MINOR")
+      .setParam("statuses", "READY,BETA")
+      .setParam("tags", "has-params,integration-tests")
+      .setParam("debtCharacteristics", "MODULARITY,REUSABILITY")
+      .execute();
 
     ArgumentCaptor<RuleQuery> ruleQueryCaptor = ArgumentCaptor.forClass(RuleQuery.class);
     verify(rules).find(ruleQueryCaptor.capture());
 
-    assertThat(ruleQueryCaptor.getValue().language()).isEqualTo("java");
+    assertThat(ruleQueryCaptor.getValue().languages()).containsOnly("java", "js");
+    assertThat(ruleQueryCaptor.getValue().repositories()).containsOnly("squid", "pmd");
+    assertThat(ruleQueryCaptor.getValue().severities()).containsOnly("MAJOR", "MINOR");
+    assertThat(ruleQueryCaptor.getValue().statuses()).containsOnly("READY", "BETA");
+    assertThat(ruleQueryCaptor.getValue().tags()).containsOnly("has-params", "integration-tests");
+    assertThat(ruleQueryCaptor.getValue().debtCharacteristics()).containsOnly("MODULARITY", "REUSABILITY");
   }
 
-  @Test
-  public void search_rule_by_characteristic() throws Exception {
-    Rule rule = ruleBuilder.build();
-
-    when(rules.find(any(RuleQuery.class))).thenReturn(
-      new PagedResult<Rule>(ImmutableList.of(rule), PagingResult.create(10, 1, 1)));
-
-    MockUserSession.set();
-    WsTester.TestRequest request = tester.newRequest("list").setParam("characteristic", "MODULARITY");
-    request.execute().assertJson("{'more':false,'total':1,'results':["
-      + "{'key':'squid:AvoidCycle','name':'Avoid cycle','language':'java'}"
-      + "]}");
-
-    ArgumentCaptor<RuleQuery> ruleQueryCaptor = ArgumentCaptor.forClass(RuleQuery.class);
-    verify(rules).find(ruleQueryCaptor.capture());
-
-    assertThat(ruleQueryCaptor.getValue().characteristic()).isEqualTo("MODULARITY");
-  }
 }
index f7bc51f168b50123a49cd2d33f31dba9628f1a61..134ddd71a082d83f517810a390d09773923384b4 100644 (file)
       "description": ""
     }
   ],
-  "defaultCharacteristicId": 1,
-  "characteristicId": 2,
-  "defaultRemediationFunction": "LINEAR",
+  "characteristicId": 1,
+  "characteristicKey": "REUSABILITY",
+  "characteristicName": "Reusability",
+  "subCharacteristicId": 2,
+  "subCharacteristicKey": "MODULARITY",
+  "subCharacteristicName": "Modularity",
   "remediationFunction": "LINEAR_OFFSET",
-  "defaultRemediationCoefficient": "2h",
   "remediationCoefficient": "1h",
-  "defaultRemediationOffset": null,
   "remediationOffset": "15min"
 }
index 3879ecccdfc567693616624225f3029276b1bc2d..a29bfa5fde3a8122b7146a7447a6489cfdf510d2 100644 (file)
@@ -6,7 +6,7 @@
   "description": "Generate an issue on each line of a file. It requires the metric \"lines\".",
   "parentKey": null,
   "repositoryKey": "xoo2",
-  "severity": "MAJOR",
+  "severity": "MINOR",
   "status": "BETA",
   "createdAt": "2013-10-28T13:07:26.339Z",
   "updatedAt": "2013-11-08T10:52:53.487Z"
diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/ws/RuleSearchWsHandlerTest/search_rules.json b/sonar-server/src/test/resources/org/sonar/server/rule/ws/RuleSearchWsHandlerTest/search_rules.json
new file mode 100644 (file)
index 0000000..6a29764
--- /dev/null
@@ -0,0 +1,17 @@
+{"more": false, "total": 1,
+  "results": [
+    {
+      "key": "squid:AvoidCycle",
+      "name": "Avoid cycle",
+      "language": "Java",
+      "status": "READY",
+      "debtCharacteristic": "REUSABILITY",
+      "debtCharacteristicName": "Reusability",
+      "debtSubCharacteristic": "MODULARITY",
+      "debtSubCharacteristicName": "Modularity",
+      "debtRemediationFunction": "LINEAR_OFFSET",
+      "debtRemediationCoefficient": "1h",
+      "debtRemediationOffset": "15min"
+    }
+  ]
+}