From b7ded5c1d2dab0359213d3bb9b4ad78eaef874dc Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 1 May 2014 13:48:41 +0200 Subject: [PATCH] Drop UnsuportedException and improve rule search --- .../sonar/core/db/UnsuportedException.java | 28 --- .../java/org/sonar/server/rule2/RuleDao.java | 6 +- .../sonar/server/rule2/RuleNormalizer.java | 2 +- .../org/sonar/server/rule2/RuleQuery.java | 33 ++- .../sonar/server/rule2/ws/SearchAction.java | 88 ++++++- .../org/sonar/server/search/QueryOptions.java | 52 ++-- .../org/sonar/server/ws/WebServiceEngine.java | 4 +- .../sonar/server/rule2/RuleMediumTest.java | 227 ------------------ .../server/rule2/RuleSearchMediumTest.java | 186 ++++++++++++++ .../server/rule2/RuleServiceMediumTest.java | 107 +++++++++ .../sonar/server/ws/WebServiceEngineTest.java | 2 +- 11 files changed, 439 insertions(+), 296 deletions(-) delete mode 100644 sonar-core/src/main/java/org/sonar/core/db/UnsuportedException.java delete mode 100644 sonar-server/src/test/java/org/sonar/server/rule2/RuleMediumTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/rule2/RuleSearchMediumTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java diff --git a/sonar-core/src/main/java/org/sonar/core/db/UnsuportedException.java b/sonar-core/src/main/java/org/sonar/core/db/UnsuportedException.java deleted file mode 100644 index 6e593f2380b..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/db/UnsuportedException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.core.db; - -public class UnsuportedException extends RuntimeException { - - public UnsuportedException(String cause) { - super(cause); - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleDao.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleDao.java index 87be472f55a..7a7fa10f9b0 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule2/RuleDao.java +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleDao.java @@ -26,7 +26,6 @@ import org.apache.ibatis.session.SqlSession; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; import org.sonar.api.rule.RuleKey; -import org.sonar.core.db.UnsuportedException; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.core.rule.RuleConstants; @@ -37,7 +36,6 @@ import org.sonar.core.rule.RuleRuleTagDto; import org.sonar.server.db.BaseDao; import javax.annotation.CheckForNull; - import java.sql.Timestamp; import java.util.Collection; import java.util.List; @@ -77,12 +75,12 @@ public class RuleDao extends BaseDao @Override protected void doDelete(RuleDto item, SqlSession session) { - throw new UnsuportedException("Rules cannot be deleted"); + throw new UnsupportedOperationException("Rules cannot be deleted"); } @Override protected void doDeleteByKey(RuleKey key, SqlSession session) { - throw new UnsuportedException("Rules cannot be deleted"); + throw new UnsupportedOperationException("Rules cannot be deleted"); } public List selectAll() { diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleNormalizer.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleNormalizer.java index 27e8a348673..894984a8b3b 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule2/RuleNormalizer.java +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleNormalizer.java @@ -37,7 +37,7 @@ public class RuleNormalizer extends BaseNormalizer { private RuleDao ruleDao; - public enum RuleField { + public static enum RuleField { KEY("key"), REPOSITORY("repo"), NAME("name"), diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleQuery.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleQuery.java index e60fe20540d..258b07d36bf 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule2/RuleQuery.java +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleQuery.java @@ -30,7 +30,38 @@ import java.util.Collection; public class RuleQuery { public static enum SortField { - NAME, CREATED_AT + KEY(RuleNormalizer.RuleField.KEY), + REPOSITORY(RuleNormalizer.RuleField.REPOSITORY), + NAME(RuleNormalizer.RuleField.NAME), + CREATED_AT(RuleNormalizer.RuleField.CREATED_AT), + DESCRIPTION(RuleNormalizer.RuleField.DESCRIPTION), + SEVERITY(RuleNormalizer.RuleField.SEVERITY), + STATUS(RuleNormalizer.RuleField.STATUS), + LANGUAGE(RuleNormalizer.RuleField.LANGUAGE); + + private final RuleNormalizer.RuleField field; + + private SortField(RuleNormalizer.RuleField field) { + this.field = field; + } + + RuleNormalizer.RuleField field() { + return field; + } + + @Override + public String toString() { + return field.toString(); + } + + /** + * Same than {@link #valueOf(String)} but returns null if parameter + * is null + */ + @CheckForNull + public static SortField valueOfOrNull(@Nullable String s) { + return s == null ? null : valueOf(s); + } } private String key; diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/ws/SearchAction.java b/sonar-server/src/main/java/org/sonar/server/rule2/ws/SearchAction.java index 25fa57c060a..e78ec9f345a 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule2/ws/SearchAction.java +++ b/sonar-server/src/main/java/org/sonar/server/rule2/ws/SearchAction.java @@ -52,9 +52,19 @@ public class SearchAction implements RequestHandler { private static final String PARAM_SEVERITIES = "severities"; private static final String PARAM_STATUSES = "statuses"; private static final String PARAM_LANGUAGES = "languages"; + private static final String PARAM_DEBT_CHARACTERISTICS = "debtCharacteristics"; + private static final String PARAM_HAS_DEBT_CHARACTERISTIC = "hasDebtCharacteristic"; private static final String PARAM_TAGS = "tags"; + private static final String PARAM_ALL_OF_TAGS = "allOfTags"; + // generic search parameters + private static final String PARAM_PAGE = "p"; + private static final String PARAM_PAGE_SIZE = "ps"; + private static final String PARAM_FIELDS = "f"; + private static final String PARAM_SORT = "s"; + private static final String PARAM_ASCENDING = "asc"; + private final RuleService service; public SearchAction(RuleService service) { @@ -84,6 +94,11 @@ public class SearchAction implements RequestHandler { .setPossibleValues(Severity.ALL) .setExampleValue("CRITICAL,BLOCKER"); + action + .createParam(PARAM_LANGUAGES) + .setDescription("Comma-separated list of languages") + .setExampleValue("java,js"); + action .createParam(PARAM_STATUSES) .setDescription("Comma-separated list of status codes") @@ -91,7 +106,22 @@ public class SearchAction implements RequestHandler { .setExampleValue(RuleStatus.READY.toString()); action - .createParam("tags") + .createParam(PARAM_DEBT_CHARACTERISTICS) + .setDescription("Comma-separated list of technical debt characteristics or sub-characteristics") + .setExampleValue("RELIABILITY"); + + action + .createParam(PARAM_HAS_DEBT_CHARACTERISTIC) + .setDescription("Filter rules that have a technical debt characteristic") + .setPossibleValues("false", "true"); + + action + .createParam(PARAM_TAGS) + .setDescription("Comma-separated list of tags. Returned rules match any of the tags (OR operator)") + .setExampleValue("security,java8"); + + action + .createParam(PARAM_ALL_OF_TAGS) .setDescription("Comma-separated list of tags. Returned rules match all the tags (AND operator)") .setExampleValue("security,java8"); @@ -106,10 +136,34 @@ public class SearchAction implements RequestHandler { .setExampleValue("java:Sonar way"); action - .createParam("fields") + .createParam(PARAM_FIELDS) .setDescription("Comma-separated list of the fields to be returned in response. All the fields are returned by default.") .setPossibleValues(RuleIndex.PUBLIC_FIELDS) .setExampleValue(String.format("%s,%s,%s", RuleNormalizer.RuleField.KEY, RuleNormalizer.RuleField.REPOSITORY, RuleNormalizer.RuleField.LANGUAGE)); + + action + .createParam(PARAM_PAGE) + .setDescription("1-based page number") + .setExampleValue("42") + .setDefaultValue("1"); + + action + .createParam(PARAM_PAGE_SIZE) + .setDescription("Page size. Must be greater than 0.") + .setExampleValue("10") + .setDefaultValue("25"); + + // TODO limit the fields to sort on + document possible values + default value ? + action + .createParam(PARAM_SORT) + .setDescription("Sort field") + .setExampleValue(RuleNormalizer.RuleField.LANGUAGE.key()); + + action + .createParam(PARAM_ASCENDING) + .setDescription("Ascending sort") + .setPossibleValues("false", "true") + .setDefaultValue("true"); } @Override @@ -120,9 +174,33 @@ public class SearchAction implements RequestHandler { query.setRepositories(request.paramAsStrings(PARAM_REPOSITORIES)); query.setStatuses(toStatuses(request.paramAsStrings(PARAM_STATUSES))); query.setLanguages(request.paramAsStrings(PARAM_LANGUAGES)); + query.setDebtCharacteristics(request.paramAsStrings(PARAM_DEBT_CHARACTERISTICS)); + query.setHasDebtCharacteristic(request.paramAsBoolean(PARAM_HAS_DEBT_CHARACTERISTIC)); + + // TODO move to QueryOptions ? + query.setSortField(RuleQuery.SortField.valueOfOrNull(request.param(PARAM_SORT))); + query.setAscendingSort(request.paramAsBoolean(PARAM_ASCENDING, true)); + + QueryOptions options = new QueryOptions(); + options.setFieldsToReturn(request.paramAsStrings(PARAM_FIELDS)); + options.setPage( + request.paramAsInt(PARAM_PAGE, 1), + request.paramAsInt(PARAM_PAGE_SIZE, 25)); + + Results results = service.search(query, options); + JsonWriter json = response.newJsonWriter().beginObject(); + writeStatistics(results, json); + writeHits(results, json); + json.close(); + } + + private void writeStatistics(Results results, JsonWriter json) { + json.prop("total", results.getTotal()); + json.prop("time", results.getTime()); + } - Results results = service.search(query, new QueryOptions()); - JsonWriter json = response.newJsonWriter().beginObject().name("hits").beginArray(); + private void writeHits(Results results, JsonWriter json) { + json.name("hits").beginArray(); for (Hit hit : results.getHits()) { json.beginObject(); for (Map.Entry entry : hit.getFields().entrySet()) { @@ -132,7 +210,7 @@ public class SearchAction implements RequestHandler { json.endObject(); } json.endArray(); - json.endObject().close(); + json.endObject(); } @CheckForNull diff --git a/sonar-server/src/main/java/org/sonar/server/search/QueryOptions.java b/sonar-server/src/main/java/org/sonar/server/search/QueryOptions.java index 19d2719538c..c4a4f242a89 100644 --- a/sonar-server/src/main/java/org/sonar/server/search/QueryOptions.java +++ b/sonar-server/src/main/java/org/sonar/server/search/QueryOptions.java @@ -25,6 +25,7 @@ import com.google.common.collect.Sets; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -36,15 +37,12 @@ public class QueryOptions { public static final int DEFAULT_OFFSET = 0; public static final int DEFAULT_LIMIT = 10; - public static final boolean DEFAULT_ASCENDING = true; private int offset = DEFAULT_OFFSET; private int limit = DEFAULT_LIMIT; - private boolean ascending = DEFAULT_ASCENDING; - private String sortField; private Set fieldsToReturn; - public QueryOptions(){ + public QueryOptions() { fieldsToReturn = new HashSet(); } @@ -64,6 +62,17 @@ public class QueryOptions { return this; } + /** + * Set offset and limit according to page approach + */ + public QueryOptions setPage(int page, int pageSize) { + Preconditions.checkArgument(page > 0, "Page must be positive"); + Preconditions.checkArgument(pageSize >= 0, "Page size must be positive or greater than 0"); + this.offset = (page * pageSize) - pageSize; + this.limit = pageSize; + return this; + } + /** * Limit on the number of results to return. Defaults to {@link #DEFAULT_LIMIT}. */ @@ -79,35 +88,13 @@ public class QueryOptions { return this; } - /** - * Is ascending sort ? Defaults to {@link #DEFAULT_ASCENDING} - */ - public boolean isAscending() { - return ascending; - } - - public QueryOptions setAscending(boolean ascending) { - this.ascending = ascending; - return this; - } - - @CheckForNull - public String getSortField() { - return sortField; - } - - public QueryOptions setSortField(@Nullable String sortField) { - this.sortField = sortField; - return this; - } - @CheckForNull public Set getFieldsToReturn() { return fieldsToReturn; } - public QueryOptions setFieldsToReturn(@Nullable Set c) { - this.fieldsToReturn = c; + public QueryOptions setFieldsToReturn(@Nullable Collection c) { + this.fieldsToReturn = (c == null ? null : Sets.newHashSet(c)); return this; } @@ -122,6 +109,15 @@ public class QueryOptions { return this; } + public QueryOptions addFieldsToReturn(String... c) { + if (fieldsToReturn == null) { + fieldsToReturn = Sets.newHashSet(c); + } else { + fieldsToReturn.addAll(Arrays.asList(c)); + } + return this; + } + public QueryOptions filterFieldsToReturn(final Set keep) { if (fieldsToReturn == null) { fieldsToReturn = keep; diff --git a/sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java b/sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java index f566c4d75c8..b46bcd8134e 100644 --- a/sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java +++ b/sonar-server/src/main/java/org/sonar/server/ws/WebServiceEngine.java @@ -126,7 +126,9 @@ public class WebServiceEngine implements ServerComponent, Startable { public String param(String key) { Param paramDef = action.param(key); if (paramDef == null) { - throw new BadRequestException(String.format("Parameter '%s' is undefined for action '%s'", key, action.key())); + String message = String.format("BUG - parameter '%s' is undefined for action '%s'", key, action.key()); + LoggerFactory.getLogger(getClass()).error(message); + throw new IllegalStateException(message); } String value = StringUtils.defaultString(super.param(key), paramDef.defaultValue()); diff --git a/sonar-server/src/test/java/org/sonar/server/rule2/RuleMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule2/RuleMediumTest.java deleted file mode 100644 index 6a1895150b0..00000000000 --- a/sonar-server/src/test/java/org/sonar/server/rule2/RuleMediumTest.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.rule2; - -import com.google.common.collect.Iterables; -import com.sun.org.apache.xerces.internal.util.SynchronizedSymbolTable; -import org.junit.After; -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.utils.DateUtils; -import org.sonar.check.Cardinality; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.MyBatis; -import org.sonar.core.rule.RuleDto; -import org.sonar.server.search.Hit; -import org.sonar.server.search.QueryOptions; -import org.sonar.server.search.Results; -import org.sonar.server.tester.ServerTester; - -import java.util.Arrays; - -import static org.fest.assertions.Assertions.assertThat; - -public class RuleMediumTest { - - @ClassRule - public static ServerTester tester = new ServerTester(); - - @After - public void clear_data_store() { - tester.clearDataStores(); - } - - @Test - public void insert_in_db_and_index_in_es() { - // insert db - RuleKey ruleKey = RuleKey.of("javascript", "S001"); - RuleDao dao = tester.get(RuleDao.class); - dao.insert(newRuleDto(ruleKey)); - - // verify that rule is persisted in db - RuleDto persistedDto = dao.selectByKey(ruleKey); - assertThat(persistedDto).isNotNull(); - assertThat(persistedDto.getId()).isGreaterThanOrEqualTo(0); - assertThat(persistedDto.getRuleKey()).isEqualTo(ruleKey.rule()); - assertThat(persistedDto.getLanguage()).isEqualTo("js"); - - // verify that rule is indexed in es - RuleIndex index = tester.get(RuleIndex.class); - index.refresh(); - - Hit hit = index.getByKey(ruleKey); - assertThat(hit).isNotNull(); - assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.REPOSITORY.key())).isEqualTo(ruleKey.repository()); - assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.KEY.key())).isEqualTo(ruleKey.rule()); - assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key())).isEqualTo("js"); - assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.NAME.key())).isEqualTo("Rule S001"); - assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.DESCRIPTION.key())).isEqualTo("Description S001"); - assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.STATUS.key())).isEqualTo(RuleStatus.READY.toString()); - } - - @Test - public void search_rule_field_allowed(){ - String lang = "java"; - - RuleDao dao = tester.get(RuleDao.class); - dao.insert(newRuleDto(RuleKey.of("javascript", "S001")) - .setLanguage(lang)); - - RuleService service = tester.get(RuleService.class).refresh(); - - RuleQuery query = service.newRuleQuery(); - - QueryOptions options = new QueryOptions(); - options.getFieldsToReturn().add(RuleNormalizer.RuleField.LANGUAGE.key()); - - Results results = service.search(query, options); - - assertThat(results.getTotal()).isEqualTo(1); - assertThat(results.getHits()).hasSize(1); - assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S001"); - assertThat(Iterables.getFirst(results.getHits(), null) - .getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key())) - .isNotNull(); - assertThat(Iterables.getFirst(results.getHits(), null) - .getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key())) - .isEqualTo(lang); - } - - @Test - public void search_rule_no_field_selected() { - String lang = "java"; - - RuleDao dao = tester.get(RuleDao.class); - dao.insert(newRuleDto(RuleKey.of("javascript", "S001")) - .setLanguage(lang)); - - RuleService service = tester.get(RuleService.class).refresh(); - - RuleQuery query = service.newRuleQuery(); - - QueryOptions options = new QueryOptions(); - - Results results = service.search(query, options); - - assertThat(results.getTotal()).isEqualTo(1); - assertThat(results.getHits()).hasSize(1); - assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S001"); - assertThat(Iterables.getFirst(results.getHits(), null) - .getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key())) - .isNull(); - } - - @Test - public void search_rule_field_not_allowed(){ - RuleDao dao = tester.get(RuleDao.class); - dao.insert(newRuleDto(RuleKey.of("javascript", "S001")) - .setConfigKey("I should not see this")); - - RuleService service = tester.get(RuleService.class).refresh(); - - RuleQuery query = service.newRuleQuery(); - QueryOptions options = new QueryOptions(); - options.getFieldsToReturn().add(RuleNormalizer.RuleField.INTERNAL_KEY.key()); - - Results results = service.search(query, options); - - assertThat(results.getTotal()).isEqualTo(1); - assertThat(results.getHits()).hasSize(1); - assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S001"); - assertThat(Iterables.getFirst(results.getHits(), null) - .getFieldAsString(RuleNormalizer.RuleField.INTERNAL_KEY.key())) - .isNull(); - } - - @Test - public void search_rules_partial_match() throws InterruptedException { - RuleDao dao = tester.get(RuleDao.class); - dao.insert(newRuleDto(RuleKey.of("javascript", "S001")) - .setName("testing the partial match and matching of rule")); - - RuleService service = tester.get(RuleService.class).refresh(); - - RuleQuery query = service.newRuleQuery().setQueryText("test"); - Results results = service.search(query, new QueryOptions()); - - assertThat(results.getTotal()).isEqualTo(1); - assertThat(results.getHits()).hasSize(1); - assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S001"); - - } - - - @Test - public void search_rules_no_filters() throws InterruptedException { - RuleDao dao = tester.get(RuleDao.class); - dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))); - - RuleService service = tester.get(RuleService.class).refresh(); - - RuleQuery query = service.newRuleQuery(); - Results results = service.search(query, new QueryOptions()); - - assertThat(results.getTotal()).isEqualTo(1); - assertThat(results.getHits()).hasSize(1); - assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S001"); - - } - - @Test - public void search_rules_by_repositories() throws InterruptedException { - RuleDao dao = tester.get(RuleDao.class); - dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))); - dao.insert(newRuleDto(RuleKey.of("java", "S002"))); - - RuleService service = tester.get(RuleService.class).refresh(); - - RuleQuery query = service.newRuleQuery().setRepositories(Arrays.asList("findbugs", "java")); - Results results = service.search(query, new QueryOptions()); - - assertThat(results.getTotal()).isEqualTo(1); - assertThat(results.getHits()).hasSize(1); - assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S002"); - } - - private RuleDto newRuleDto(RuleKey ruleKey) { - return new RuleDto() - .setRuleKey(ruleKey.rule()) - .setRepositoryKey(ruleKey.repository()) - .setName("Rule " + ruleKey.rule()) - .setDescription("Description " + ruleKey.rule()) - .setStatus(RuleStatus.READY.toString()) - .setConfigKey("InternalKey" + ruleKey.rule()) - .setSeverity(Severity.INFO) - .setCardinality(Cardinality.SINGLE) - .setLanguage("js") - .setRemediationFunction("linear") - .setDefaultRemediationFunction("linear_offset") - .setRemediationCoefficient("1h") - .setDefaultRemediationCoefficient("5d") - .setRemediationOffset("5min") - .setDefaultRemediationOffset("10h") - .setEffortToFixDescription(ruleKey.repository() + "." + ruleKey.rule() + ".effortToFix") - .setCreatedAt(DateUtils.parseDate("2013-12-16")) - .setUpdatedAt(DateUtils.parseDate("2013-12-17")); - } -} diff --git a/sonar-server/src/test/java/org/sonar/server/rule2/RuleSearchMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule2/RuleSearchMediumTest.java new file mode 100644 index 00000000000..98cbd5595b8 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/rule2/RuleSearchMediumTest.java @@ -0,0 +1,186 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.rule2; + +import com.google.common.collect.Iterables; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Ignore; +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.utils.DateUtils; +import org.sonar.check.Cardinality; +import org.sonar.core.rule.RuleDto; +import org.sonar.server.search.Hit; +import org.sonar.server.search.QueryOptions; +import org.sonar.server.search.Results; +import org.sonar.server.tester.ServerTester; + +import java.util.Arrays; + +import static org.fest.assertions.Assertions.assertThat; + +public class RuleSearchMediumTest { + + @ClassRule + public static ServerTester tester = new ServerTester(); + + RuleDao dao = tester.get(RuleDao.class); + RuleIndex index = tester.get(RuleIndex.class); + + @Before + public void clear_data_store() { + tester.clearDataStores(); + } + + @Test + @Ignore("TODO") + public void return_all_doc_fields_by_default() { + dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))); + index.refresh(); + + QueryOptions options = new QueryOptions(); + Results results = index.search(new RuleQuery(), options); + + assertThat(results.getHits()).hasSize(1); + Hit hit = Iterables.getFirst(results.getHits(), null); + assertThat(hit.getFields()).hasSize(RuleNormalizer.RuleField.values().length); + } + + @Test + @Ignore("TODO permanent 'repo' is missing") + public void select_doc_fields_to_load() { + dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))); + index.refresh(); + + QueryOptions options = new QueryOptions(); + options.addFieldsToReturn(RuleNormalizer.RuleField.LANGUAGE.key(), RuleNormalizer.RuleField.STATUS.key()); + Results results = index.search(new RuleQuery(), options); + + assertThat(results.getHits()).hasSize(1); + Hit hit = Iterables.getFirst(results.getHits(), null); + assertThat(hit.getFields()).hasSize(4); + + // request fields + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key())).isEqualTo("js"); + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.STATUS.key())).isEqualTo(RuleStatus.READY.name()); + + // permanent fields + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.REPOSITORY.key())).isEqualTo("javascript"); + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.KEY.key())).isEqualTo("S001"); + } + + @Test + public void search_by_name() throws InterruptedException { + dao.insert(newRuleDto(RuleKey.of("javascript", "S001")) + .setName("testing the partial match and matching of rule")); + index.refresh(); + + // substring + RuleQuery query = new RuleQuery().setQueryText("test"); + assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1); + + // substring + query = new RuleQuery().setQueryText("partial match"); + assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1); + + // case-insensitive + query = new RuleQuery().setQueryText("TESTING"); + assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1); + + // not found + query = new RuleQuery().setQueryText("not present"); + assertThat(index.search(query, new QueryOptions()).getHits()).isEmpty(); + } + + @Test + @Ignore("TODO") + public void search_by_key_through_query_text() throws InterruptedException { + dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))); + dao.insert(newRuleDto(RuleKey.of("cobol", "S001"))); + dao.insert(newRuleDto(RuleKey.of("php", "S002"))); + index.refresh(); + + // key + RuleQuery query = new RuleQuery().setQueryText("S001"); + assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(2); + + // partial key does not match + query = new RuleQuery().setQueryText("S00"); + assertThat(index.search(query, new QueryOptions()).getHits()).isEmpty(); + + // repo:key -> nice-to-have ! + query = new RuleQuery().setQueryText("javascript:S001"); + assertThat(index.search(query, new QueryOptions()).getHits()).hasSize(1); + } + + @Test + public void search_all_rules() throws InterruptedException { + dao.insert(newRuleDto(RuleKey.of("javascript", "S001"))); + dao.insert(newRuleDto(RuleKey.of("java", "S002"))); + index.refresh(); + + Results results = index.search(new RuleQuery(), new QueryOptions()); + + assertThat(results.getTotal()).isEqualTo(2); + assertThat(results.getHits()).hasSize(2); + } + + @Test + public void search_rules_by_any_of_repositories() throws InterruptedException { + dao.insert(newRuleDto(RuleKey.of("findbugs", "S001"))); + dao.insert(newRuleDto(RuleKey.of("pmd", "S002"))); + index.refresh(); + + RuleQuery query = new RuleQuery().setRepositories(Arrays.asList("checkstyle", "pmd")); + Results results = index.search(query, new QueryOptions()); + assertThat(results.getHits()).hasSize(1); + assertThat(Iterables.getFirst(results.getHits(), null).getFieldAsString("key")).isEqualTo("S002"); + + // no results + query = new RuleQuery().setRepositories(Arrays.asList("checkstyle")); + assertThat(index.search(query, new QueryOptions()).getHits()).isEmpty(); + } + + private RuleDto newRuleDto(RuleKey ruleKey) { + return new RuleDto() + .setRuleKey(ruleKey.rule()) + .setRepositoryKey(ruleKey.repository()) + .setName("Rule " + ruleKey.rule()) + .setDescription("Description " + ruleKey.rule()) + .setStatus(RuleStatus.READY.toString()) + .setConfigKey("InternalKey" + ruleKey.rule()) + .setSeverity(Severity.INFO) + .setCardinality(Cardinality.SINGLE) + .setLanguage("js") + .setRemediationFunction("linear") + .setDefaultRemediationFunction("linear_offset") + .setRemediationCoefficient("1h") + .setDefaultRemediationCoefficient("5d") + .setRemediationOffset("5min") + .setDefaultRemediationOffset("10h") + .setEffortToFixDescription(ruleKey.repository() + "." + ruleKey.rule() + ".effortToFix") + .setCreatedAt(DateUtils.parseDate("2013-12-16")) + .setUpdatedAt(DateUtils.parseDate("2013-12-17")); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java new file mode 100644 index 00000000000..3bbbd9117a0 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java @@ -0,0 +1,107 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.rule2; + +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.utils.DateUtils; +import org.sonar.check.Cardinality; +import org.sonar.core.rule.RuleDto; +import org.sonar.server.search.Hit; +import org.sonar.server.tester.ServerTester; + +import java.util.Collection; + +import static org.fest.assertions.Assertions.assertThat; + +public class RuleServiceMediumTest { + + @ClassRule + public static ServerTester tester = new ServerTester(); + + @Before + public void clear_data_store() { + tester.clearDataStores(); + } + + @Test + public void insert_in_db_and_index_in_es() { + // insert db + RuleKey ruleKey = RuleKey.of("javascript", "S001"); + RuleDao dao = tester.get(RuleDao.class); + dao.insert(newRuleDto(ruleKey)); + + // verify that rule is persisted in db + RuleDto persistedDto = dao.selectByKey(ruleKey); + assertThat(persistedDto).isNotNull(); + assertThat(persistedDto.getId()).isGreaterThanOrEqualTo(0); + assertThat(persistedDto.getRuleKey()).isEqualTo(ruleKey.rule()); + assertThat(persistedDto.getLanguage()).isEqualTo("js"); + + // verify that rule is indexed in es + RuleIndex index = tester.get(RuleIndex.class); + index.refresh(); + + Hit hit = index.getByKey(ruleKey); + assertThat(hit).isNotNull(); + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.REPOSITORY.key())).isEqualTo(ruleKey.repository()); + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.KEY.key())).isEqualTo(ruleKey.rule()); + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.LANGUAGE.key())).isEqualTo("js"); + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.NAME.key())).isEqualTo("Rule S001"); + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.DESCRIPTION.key())).isEqualTo("Description S001"); + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.STATUS.key())).isEqualTo(RuleStatus.READY.toString()); + assertThat(hit.getField(RuleNormalizer.RuleField.CREATED_AT.key())).isNotNull(); + assertThat(hit.getField(RuleNormalizer.RuleField.UPDATED_AT.key())).isNotNull(); + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.INTERNAL_KEY.key())).isEqualTo("InternalKeyS001"); + assertThat(hit.getFieldAsString(RuleNormalizer.RuleField.SEVERITY.key())).isEqualTo("INFO"); +//TODO assertThat((Collection) hit.getField(RuleNormalizer.RuleField.SYSTEM_TAGS.key())).isEmpty(); +//TODO assertThat((Collection) hit.getField(RuleNormalizer.RuleField.TAGS.key())).isEmpty(); + assertThat((Boolean) hit.getField(RuleNormalizer.RuleField.TEMPLATE.key())).isFalse(); + } + + //TODO test delete, update, tags, params + + private RuleDto newRuleDto(RuleKey ruleKey) { + return new RuleDto() + .setRuleKey(ruleKey.rule()) + .setRepositoryKey(ruleKey.repository()) + .setName("Rule " + ruleKey.rule()) + .setDescription("Description " + ruleKey.rule()) + .setStatus(RuleStatus.READY.toString()) + .setConfigKey("InternalKey" + ruleKey.rule()) + .setSeverity(Severity.INFO) + .setCardinality(Cardinality.SINGLE) + .setLanguage("js") + .setRemediationFunction("linear") + .setDefaultRemediationFunction("linear_offset") + .setRemediationCoefficient("1h") + .setDefaultRemediationCoefficient("5d") + .setRemediationOffset("5min") + .setDefaultRemediationOffset("10h") + .setEffortToFixDescription(ruleKey.repository() + "." + ruleKey.rule() + ".effortToFix") + .setCreatedAt(DateUtils.parseDate("2013-12-16")) + .setUpdatedAt(DateUtils.parseDate("2013-12-17")); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java b/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java index 4434f4705d6..299581ebd21 100644 --- a/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java +++ b/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java @@ -164,7 +164,7 @@ public class WebServiceEngineTest { ServletResponse response = new ServletResponse(); engine.execute(request, response, "api/system", "fail_with_undeclared_parameter"); - assertThat(response.stream().outputAsString()).isEqualTo("{\"errors\":[{\"msg\":\"Parameter 'unknown' is undefined for action 'fail_with_undeclared_parameter'\"}]}"); + assertThat(response.stream().outputAsString()).isEqualTo("{\"errors\":[{\"msg\":\"BUG - parameter 'unknown' is undefined for action 'fail_with_undeclared_parameter'\"}]}"); } @Test -- 2.39.5