You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RuleIndexTest.java 47KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.rule.index;
  21. import com.google.common.collect.ImmutableList;
  22. import com.google.common.collect.Sets;
  23. import java.util.LinkedHashMap;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.function.Consumer;
  27. import org.elasticsearch.search.SearchHit;
  28. import org.junit.Rule;
  29. import org.junit.Test;
  30. import org.junit.rules.ExpectedException;
  31. import org.sonar.api.rule.RuleKey;
  32. import org.sonar.api.rule.RuleStatus;
  33. import org.sonar.api.utils.System2;
  34. import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
  35. import org.sonar.db.DbTester;
  36. import org.sonar.db.organization.OrganizationDto;
  37. import org.sonar.db.qualityprofile.QProfileDto;
  38. import org.sonar.db.rule.RuleDefinitionDto;
  39. import org.sonar.db.rule.RuleMetadataDto;
  40. import org.sonar.server.es.EsTester;
  41. import org.sonar.server.es.Facets;
  42. import org.sonar.server.es.SearchIdResult;
  43. import org.sonar.server.es.SearchOptions;
  44. import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
  45. import static com.google.common.collect.ImmutableSet.of;
  46. import static java.util.Arrays.asList;
  47. import static java.util.Collections.emptyList;
  48. import static java.util.Collections.emptySet;
  49. import static java.util.Collections.singletonList;
  50. import static java.util.stream.IntStream.rangeClosed;
  51. import static org.assertj.core.api.Assertions.assertThat;
  52. import static org.assertj.core.api.Assertions.entry;
  53. import static org.junit.Assert.fail;
  54. import static org.sonar.api.rule.Severity.BLOCKER;
  55. import static org.sonar.api.rule.Severity.CRITICAL;
  56. import static org.sonar.api.rule.Severity.INFO;
  57. import static org.sonar.api.rule.Severity.MAJOR;
  58. import static org.sonar.api.rule.Severity.MINOR;
  59. import static org.sonar.api.rules.RuleType.BUG;
  60. import static org.sonar.api.rules.RuleType.CODE_SMELL;
  61. import static org.sonar.api.rules.RuleType.VULNERABILITY;
  62. import static org.sonar.db.rule.RuleTesting.setCreatedAt;
  63. import static org.sonar.db.rule.RuleTesting.setIsExternal;
  64. import static org.sonar.db.rule.RuleTesting.setIsTemplate;
  65. import static org.sonar.db.rule.RuleTesting.setLanguage;
  66. import static org.sonar.db.rule.RuleTesting.setName;
  67. import static org.sonar.db.rule.RuleTesting.setOrganization;
  68. import static org.sonar.db.rule.RuleTesting.setRepositoryKey;
  69. import static org.sonar.db.rule.RuleTesting.setRuleKey;
  70. import static org.sonar.db.rule.RuleTesting.setSeverity;
  71. import static org.sonar.db.rule.RuleTesting.setStatus;
  72. import static org.sonar.db.rule.RuleTesting.setSystemTags;
  73. import static org.sonar.db.rule.RuleTesting.setTags;
  74. import static org.sonar.db.rule.RuleTesting.setTemplateId;
  75. import static org.sonar.db.rule.RuleTesting.setType;
  76. import static org.sonar.db.rule.RuleTesting.setUpdatedAt;
  77. import static org.sonar.server.qualityprofile.ActiveRuleInheritance.INHERITED;
  78. import static org.sonar.server.qualityprofile.ActiveRuleInheritance.OVERRIDES;
  79. import static org.sonar.server.rule.index.RuleIndex.FACET_LANGUAGES;
  80. import static org.sonar.server.rule.index.RuleIndex.FACET_REPOSITORIES;
  81. import static org.sonar.server.rule.index.RuleIndex.FACET_TAGS;
  82. import static org.sonar.server.rule.index.RuleIndex.FACET_TYPES;
  83. import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;
  84. import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE;
  85. import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE_EXTENSION;
  86. public class RuleIndexTest {
  87. private System2 system2 = new AlwaysIncreasingSystem2();
  88. @Rule
  89. public EsTester es = EsTester.create();
  90. @Rule
  91. public DbTester db = DbTester.create(system2);
  92. @Rule
  93. public ExpectedException expectedException = ExpectedException.none();
  94. private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
  95. private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client());
  96. private RuleIndex underTest = new RuleIndex(es.client(), system2);
  97. @Test
  98. public void search_all_rules() {
  99. createRule();
  100. createRule();
  101. index();
  102. SearchIdResult results = underTest.search(new RuleQuery(), new SearchOptions());
  103. assertThat(results.getTotal()).isEqualTo(2);
  104. assertThat(results.getIds()).hasSize(2);
  105. }
  106. @Test
  107. public void search_by_key() {
  108. RuleDefinitionDto js1 = createRule(
  109. setRepositoryKey("javascript"),
  110. setRuleKey("X001"));
  111. RuleDefinitionDto cobol1 = createRule(
  112. setRepositoryKey("cobol"),
  113. setRuleKey("X001"));
  114. RuleDefinitionDto php2 = createRule(
  115. setRepositoryKey("php"),
  116. setRuleKey("S002"));
  117. index();
  118. // key
  119. RuleQuery query = new RuleQuery().setQueryText("X001");
  120. assertThat(underTest.search(query, new SearchOptions()).getIds()).containsOnly(js1.getId(), cobol1.getId());
  121. // partial key does not match
  122. query = new RuleQuery().setQueryText("X00");
  123. assertThat(underTest.search(query, new SearchOptions()).getIds()).isEmpty();
  124. // repo:key -> nice-to-have !
  125. query = new RuleQuery().setQueryText("javascript:X001");
  126. assertThat(underTest.search(query, new SearchOptions()).getIds()).containsOnly(js1.getId());
  127. }
  128. @Test
  129. public void search_by_case_insensitive_key() {
  130. RuleDefinitionDto ruleDto = createRule(
  131. setRepositoryKey("javascript"),
  132. setRuleKey("X001"));
  133. index();
  134. RuleQuery query = new RuleQuery().setQueryText("x001");
  135. assertThat(underTest.search(query, new SearchOptions()).getIds()).containsOnly(ruleDto.getId());
  136. }
  137. @Test
  138. public void filter_by_key() {
  139. createRule(
  140. setRepositoryKey("javascript"),
  141. setRuleKey("X001"));
  142. createRule(
  143. setRepositoryKey("cobol"),
  144. setRuleKey("X001"));
  145. createRule(
  146. setRepositoryKey("php"),
  147. setRuleKey("S002"));
  148. index();
  149. // key
  150. RuleQuery query = new RuleQuery().setKey(RuleKey.of("javascript", "X001").toString());
  151. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(1);
  152. // partial key does not match
  153. query = new RuleQuery().setKey("X001");
  154. assertThat(underTest.search(query, new SearchOptions()).getIds()).isEmpty();
  155. }
  156. @Test
  157. public void search_name_by_query() {
  158. createRule(setName("testing the partial match and matching of rule"));
  159. index();
  160. // substring
  161. RuleQuery query = new RuleQuery().setQueryText("test");
  162. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(1);
  163. // substring
  164. query = new RuleQuery().setQueryText("partial match");
  165. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(1);
  166. // case-insensitive
  167. query = new RuleQuery().setQueryText("TESTING");
  168. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(1);
  169. // not found
  170. query = new RuleQuery().setQueryText("not present");
  171. assertThat(underTest.search(query, new SearchOptions()).getIds()).isEmpty();
  172. }
  173. @Test
  174. public void search_name_with_protected_chars() {
  175. RuleDefinitionDto rule = createRule(setName("ja#va&sc\"r:ipt"));
  176. index();
  177. RuleQuery protectedCharsQuery = new RuleQuery().setQueryText(rule.getName());
  178. List<Integer> results = underTest.search(protectedCharsQuery, new SearchOptions()).getIds();
  179. assertThat(results).containsOnly(rule.getId());
  180. }
  181. @Test
  182. public void search_content_by_query() {
  183. // it's important to set all the fields being used by the search (name, desc, key, lang, ...),
  184. // otherwise the generated random values may raise false-positives
  185. RuleDefinitionDto rule1 = createJavaRule(rule -> rule.setRuleKey("123")
  186. .setName("rule 123")
  187. .setDescription("My great rule CWE-123 which makes your code 1000 times better!"));
  188. RuleDefinitionDto rule2 = createJavaRule(rule -> rule.setRuleKey("124")
  189. .setName("rule 124")
  190. .setDescription("Another great and shiny rule CWE-124"));
  191. RuleDefinitionDto rule3 = createJavaRule(rule -> rule.setRuleKey("1000")
  192. .setName("rule 1000")
  193. .setDescription("Another great rule CWE-1000"));
  194. RuleDefinitionDto rule4 = createJavaRule(rule -> rule.setRuleKey("404")
  195. .setName("rule 404")
  196. .setDescription("<h1>HTML-Geeks</h1><p style=\"color:blue\">special formatting!</p><table><tr><td>inside</td><td>tables</td></tr></table>"));
  197. RuleDefinitionDto rule5 = createJavaRule(rule -> rule.setRuleKey("405")
  198. .setName("rule 405")
  199. .setDescription("internationalization missunderstandings alsdkjfnadklsjfnadkdfnsksdjfn"));
  200. index();
  201. // partial match at word boundary
  202. assertThat(underTest.search(new RuleQuery().setQueryText("CWE"), new SearchOptions()).getIds()).containsExactlyInAnyOrder(rule1.getId(), rule2.getId(), rule3.getId());
  203. // full match
  204. assertThat(underTest.search(new RuleQuery().setQueryText("CWE-123"), new SearchOptions()).getIds()).containsExactly(rule1.getId());
  205. // match somewhere else in the text
  206. assertThat(underTest.search(new RuleQuery().setQueryText("CWE-1000"), new SearchOptions()).getIds()).containsExactly(rule3.getId());
  207. assertThat(underTest.search(new RuleQuery().setQueryText("CWE 1000"), new SearchOptions()).getIds()).containsExactlyInAnyOrder(rule3.getId(), rule1.getId());
  208. // several words
  209. assertThat(underTest.search(new RuleQuery().setQueryText("great rule"), new SearchOptions()).getIds()).containsExactlyInAnyOrder(rule1.getId(), rule2.getId(), rule3.getId());
  210. assertThat(underTest.search(new RuleQuery().setQueryText("rule Another"), new SearchOptions()).getIds()).containsExactlyInAnyOrder(rule2.getId(), rule3.getId());
  211. // no matches
  212. assertThat(underTest.search(new RuleQuery().setQueryText("unexisting"), new SearchOptions()).getIds()).isEmpty();
  213. assertThat(underTest.search(new RuleQuery().setQueryText("great rule unexisting"), new SearchOptions()).getIds()).isEmpty();
  214. // stopwords
  215. assertThat(underTest.search(new RuleQuery().setQueryText("and"), new SearchOptions()).getIds()).isEmpty();
  216. assertThat(underTest.search(new RuleQuery().setQueryText("great and shiny"), new SearchOptions()).getIds()).isEmpty();
  217. // html
  218. assertThat(underTest.search(new RuleQuery().setQueryText("h1"), new SearchOptions()).getIds()).isEmpty();
  219. assertThat(underTest.search(new RuleQuery().setQueryText("style"), new SearchOptions()).getIds()).isEmpty();
  220. assertThat(underTest.search(new RuleQuery().setQueryText("special"), new SearchOptions()).getIds()).containsExactlyInAnyOrder(rule4.getId());
  221. assertThat(underTest.search(new RuleQuery().setQueryText("geeks formatting inside tables"), new SearchOptions()).getIds()).containsExactlyInAnyOrder(rule4.getId());
  222. // long words
  223. assertThat(underTest.search(new RuleQuery().setQueryText("missunderstand"), new SearchOptions()).getIds()).containsExactlyInAnyOrder(rule5.getId());
  224. assertThat(underTest.search(new RuleQuery().setQueryText("missunderstandings"), new SearchOptions()).getIds()).containsExactlyInAnyOrder(rule5.getId());
  225. assertThat(underTest.search(new RuleQuery().setQueryText("alsdkjfnadklsjfnadkdfnsksdjfn"), new SearchOptions()).getIds()).containsExactlyInAnyOrder(rule5.getId());
  226. assertThat(underTest.search(new RuleQuery().setQueryText("internationalization"), new SearchOptions()).getIds()).containsExactlyInAnyOrder(rule5.getId());
  227. assertThat(underTest.search(new RuleQuery().setQueryText("internationalizationBlaBla"), new SearchOptions()).getIds()).isEmpty();
  228. }
  229. @Test
  230. public void search_by_any_of_repositories() {
  231. RuleDefinitionDto findbugs = createRule(
  232. setRepositoryKey("findbugs"),
  233. setRuleKey("S001"));
  234. RuleDefinitionDto pmd = createRule(
  235. setRepositoryKey("pmd"),
  236. setRuleKey("S002"));
  237. index();
  238. RuleQuery query = new RuleQuery().setRepositories(asList("checkstyle", "pmd"));
  239. SearchIdResult results = underTest.search(query, new SearchOptions());
  240. assertThat(results.getIds()).containsExactly(pmd.getId());
  241. // no results
  242. query = new RuleQuery().setRepositories(singletonList("checkstyle"));
  243. assertThat(underTest.search(query, new SearchOptions()).getIds()).isEmpty();
  244. // empty list => no filter
  245. query = new RuleQuery().setRepositories(emptyList());
  246. assertThat(underTest.search(query, new SearchOptions()).getIds()).containsOnly(findbugs.getId(), pmd.getId());
  247. }
  248. @Test
  249. public void filter_by_tags() {
  250. OrganizationDto organization = db.organizations().insert();
  251. RuleDefinitionDto rule1 = createRule(setSystemTags("tag1s"));
  252. createRuleMetadata(rule1, organization, setTags("tag1"));
  253. RuleDefinitionDto rule2 = createRule(setSystemTags("tag2s"));
  254. createRuleMetadata(rule2, organization, setTags("tag2"));
  255. index();
  256. assertThat(es.countDocuments(TYPE_RULE_EXTENSION)).isEqualTo(4);
  257. // tag2s in filter
  258. RuleQuery query = new RuleQuery().setOrganization(organization).setTags(of("tag2s"));
  259. verifySearch(query, rule2);
  260. // tag2 in filter
  261. query = new RuleQuery().setOrganization(organization).setTags(of("tag2"));
  262. verifySearch(query, rule2);
  263. // empty list => no filter
  264. query = new RuleQuery().setTags(emptySet());
  265. verifySearch(query, rule1, rule2);
  266. // null list => no filter
  267. query = new RuleQuery().setTags(null);
  268. verifySearch(query, rule1, rule2);
  269. }
  270. @Test
  271. public void tags_facet_supports_selected_value_with_regexp_special_characters() {
  272. OrganizationDto organization = db.organizations().insert();
  273. RuleDefinitionDto rule = createRule();
  274. createRuleMetadata(rule, organization, setTags("misra++"));
  275. index();
  276. RuleQuery query = new RuleQuery()
  277. .setOrganization(organization)
  278. .setTags(singletonList("misra["));
  279. SearchOptions options = new SearchOptions().addFacets(FACET_TAGS);
  280. // do not fail
  281. assertThat(underTest.search(query, options).getTotal()).isEqualTo(0);
  282. }
  283. @Test
  284. public void search_by_types() {
  285. RuleDefinitionDto codeSmell = createRule(setType(CODE_SMELL));
  286. RuleDefinitionDto vulnerability = createRule(setType(VULNERABILITY));
  287. RuleDefinitionDto bug1 = createRule(setType(BUG));
  288. RuleDefinitionDto bug2 = createRule(setType(BUG));
  289. index();
  290. // find all
  291. RuleQuery query = new RuleQuery();
  292. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(4);
  293. // type3 in filter
  294. query = new RuleQuery().setTypes(of(VULNERABILITY));
  295. assertThat(underTest.search(query, new SearchOptions()).getIds()).containsOnly(vulnerability.getId());
  296. query = new RuleQuery().setTypes(of(BUG));
  297. assertThat(underTest.search(query, new SearchOptions()).getIds()).containsOnly(bug1.getId(), bug2.getId());
  298. // types in query => nothing
  299. query = new RuleQuery().setQueryText("code smell bug vulnerability");
  300. assertThat(underTest.search(query, new SearchOptions()).getIds()).isEmpty();
  301. // null list => no filter
  302. query = new RuleQuery().setTypes(emptySet());
  303. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(4);
  304. // null list => no filter
  305. query = new RuleQuery().setTypes(null);
  306. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(4);
  307. }
  308. @Test
  309. public void search_by_is_template() {
  310. RuleDefinitionDto ruleNoTemplate = createRule(setIsTemplate(false));
  311. RuleDefinitionDto ruleIsTemplate = createRule(setIsTemplate(true));
  312. index();
  313. // find all
  314. RuleQuery query = new RuleQuery();
  315. SearchIdResult<Integer> results = underTest.search(query, new SearchOptions());
  316. assertThat(results.getIds()).hasSize(2);
  317. // Only template
  318. query = new RuleQuery().setIsTemplate(true);
  319. results = underTest.search(query, new SearchOptions());
  320. assertThat(results.getIds()).containsOnly(ruleIsTemplate.getId());
  321. // Only not template
  322. query = new RuleQuery().setIsTemplate(false);
  323. results = underTest.search(query, new SearchOptions());
  324. assertThat(results.getIds()).containsOnly(ruleNoTemplate.getId());
  325. // null => no filter
  326. query = new RuleQuery().setIsTemplate(null);
  327. results = underTest.search(query, new SearchOptions());
  328. assertThat(results.getIds()).containsOnly(ruleIsTemplate.getId(), ruleNoTemplate.getId());
  329. }
  330. @Test
  331. public void search_by_is_external() {
  332. RuleDefinitionDto ruleIsNotExternal = createRule(setIsExternal(false));
  333. RuleDefinitionDto ruleIsExternal = createRule(setIsExternal(true));
  334. index();
  335. // Only external
  336. RuleQuery query = new RuleQuery().setIncludeExternal(true);
  337. SearchIdResult<Integer> results = underTest.search(query, new SearchOptions());
  338. assertThat(results.getIds()).containsOnly(ruleIsExternal.getId(), ruleIsNotExternal.getId());
  339. // Only not external
  340. query = new RuleQuery().setIncludeExternal(false);
  341. results = underTest.search(query, new SearchOptions());
  342. assertThat(results.getIds()).containsOnly(ruleIsNotExternal.getId());
  343. }
  344. @Test
  345. public void search_by_template_key() {
  346. RuleDefinitionDto template = createRule(setIsTemplate(true));
  347. RuleDefinitionDto customRule = createRule(setTemplateId(template.getId()));
  348. index();
  349. // find all
  350. RuleQuery query = new RuleQuery();
  351. SearchIdResult results = underTest.search(query, new SearchOptions());
  352. assertThat(results.getIds()).hasSize(2);
  353. // Only custom rule
  354. query = new RuleQuery().setTemplateKey(template.getKey().toString());
  355. results = underTest.search(query, new SearchOptions());
  356. assertThat(results.getIds()).containsOnly(customRule.getId());
  357. // null => no filter
  358. query = new RuleQuery().setTemplateKey(null);
  359. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(2);
  360. }
  361. @Test
  362. public void search_by_any_of_languages() {
  363. RuleDefinitionDto java = createRule(setLanguage("java"));
  364. RuleDefinitionDto javascript = createRule(setLanguage("js"));
  365. index();
  366. RuleQuery query = new RuleQuery().setLanguages(asList("cobol", "js"));
  367. SearchIdResult results = underTest.search(query, new SearchOptions());
  368. assertThat(results.getIds()).containsOnly(javascript.getId());
  369. // no results
  370. query = new RuleQuery().setLanguages(singletonList("cpp"));
  371. assertThat(underTest.search(query, new SearchOptions()).getIds()).isEmpty();
  372. // empty list => no filter
  373. query = new RuleQuery().setLanguages(emptyList());
  374. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(2);
  375. // null list => no filter
  376. query = new RuleQuery().setLanguages(null);
  377. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(2);
  378. }
  379. @Test
  380. public void compare_to_another_profile() {
  381. String xoo = "xoo";
  382. QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(xoo));
  383. QProfileDto anotherProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(xoo));
  384. RuleDefinitionDto commonRule = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition();
  385. RuleDefinitionDto profileRule1 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition();
  386. RuleDefinitionDto profileRule2 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition();
  387. RuleDefinitionDto profileRule3 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition();
  388. RuleDefinitionDto anotherProfileRule1 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition();
  389. RuleDefinitionDto anotherProfileRule2 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition();
  390. db.qualityProfiles().activateRule(profile, commonRule);
  391. db.qualityProfiles().activateRule(profile, profileRule1);
  392. db.qualityProfiles().activateRule(profile, profileRule2);
  393. db.qualityProfiles().activateRule(profile, profileRule3);
  394. db.qualityProfiles().activateRule(anotherProfile, commonRule);
  395. db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule1);
  396. db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule2);
  397. index();
  398. verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(anotherProfile), anotherProfileRule1, anotherProfileRule2);
  399. verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(anotherProfile), commonRule);
  400. verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(profile), commonRule, profileRule1, profileRule2, profileRule3);
  401. verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(profile));
  402. }
  403. @SafeVarargs
  404. private final RuleDefinitionDto createRule(Consumer<RuleDefinitionDto>... consumers) {
  405. return db.rules().insert(consumers);
  406. }
  407. private RuleDefinitionDto createJavaRule() {
  408. return createRule(r -> r.setLanguage("java"));
  409. }
  410. private RuleDefinitionDto createJavaRule(Consumer<RuleDefinitionDto> consumer) {
  411. return createRule(r -> r.setLanguage("java"), consumer);
  412. }
  413. @SafeVarargs
  414. private final RuleMetadataDto createRuleMetadata(RuleDefinitionDto rule, OrganizationDto organization, Consumer<RuleMetadataDto>... populaters) {
  415. return db.rules().insertOrUpdateMetadata(rule, organization, populaters);
  416. }
  417. @Test
  418. public void search_by_any_of_severities() {
  419. RuleDefinitionDto blocker = createRule(setSeverity(BLOCKER));
  420. RuleDefinitionDto info = createRule(setSeverity(INFO));
  421. index();
  422. RuleQuery query = new RuleQuery().setSeverities(asList(INFO, MINOR));
  423. SearchIdResult results = underTest.search(query, new SearchOptions());
  424. assertThat(results.getIds()).containsOnly(info.getId());
  425. // no results
  426. query = new RuleQuery().setSeverities(singletonList(MINOR));
  427. assertThat(underTest.search(query, new SearchOptions()).getIds()).isEmpty();
  428. // empty list => no filter
  429. query = new RuleQuery().setSeverities(emptyList());
  430. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(2);
  431. // null list => no filter
  432. query = new RuleQuery().setSeverities();
  433. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(2);
  434. }
  435. @Test
  436. public void search_by_any_of_statuses() {
  437. RuleDefinitionDto beta = createRule(setStatus(RuleStatus.BETA));
  438. RuleDefinitionDto ready = createRule(setStatus(RuleStatus.READY));
  439. index();
  440. RuleQuery query = new RuleQuery().setStatuses(asList(RuleStatus.DEPRECATED, RuleStatus.READY));
  441. SearchIdResult<Integer> results = underTest.search(query, new SearchOptions());
  442. assertThat(results.getIds()).containsOnly(ready.getId());
  443. // no results
  444. query = new RuleQuery().setStatuses(singletonList(RuleStatus.DEPRECATED));
  445. assertThat(underTest.search(query, new SearchOptions()).getIds()).isEmpty();
  446. // empty list => no filter
  447. query = new RuleQuery().setStatuses(emptyList());
  448. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(2);
  449. // null list => no filter
  450. query = new RuleQuery().setStatuses(null);
  451. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(2);
  452. }
  453. @Test
  454. public void activation_parameter_is_ignored_if_profile_is_not_set() {
  455. RuleDefinitionDto rule1 = createJavaRule();
  456. RuleDefinitionDto rule2 = createJavaRule();
  457. QProfileDto profile1 = createJavaProfile();
  458. db.qualityProfiles().activateRule(profile1, rule1);
  459. index();
  460. // all rules are returned
  461. verifySearch(newRuleQuery().setActivation(true), rule1, rule2);
  462. }
  463. @Test
  464. public void search_by_activation() {
  465. RuleDefinitionDto rule1 = createJavaRule();
  466. RuleDefinitionDto rule2 = createJavaRule();
  467. RuleDefinitionDto rule3 = createJavaRule();
  468. QProfileDto profile1 = createJavaProfile();
  469. QProfileDto profile2 = createJavaProfile();
  470. db.qualityProfiles().activateRule(profile1, rule1);
  471. db.qualityProfiles().activateRule(profile2, rule1);
  472. db.qualityProfiles().activateRule(profile1, rule2);
  473. index();
  474. // active rules
  475. verifySearch(newRuleQuery().setActivation(true).setQProfile(profile1), rule1, rule2);
  476. verifySearch(newRuleQuery().setActivation(true).setQProfile(profile2), rule1);
  477. // inactive rules
  478. verifySearch(newRuleQuery().setActivation(false).setQProfile(profile1), rule3);
  479. verifySearch(newRuleQuery().setActivation(false).setQProfile(profile2), rule2, rule3);
  480. }
  481. private void verifyEmptySearch(RuleQuery query) {
  482. verifySearch(query);
  483. }
  484. private void verifySearch(RuleQuery query, RuleDefinitionDto... expectedRules) {
  485. SearchIdResult<Integer> result = underTest.search(query, new SearchOptions());
  486. assertThat(result.getTotal()).isEqualTo((long) expectedRules.length);
  487. assertThat(result.getIds()).hasSize(expectedRules.length);
  488. for (RuleDefinitionDto expectedRule : expectedRules) {
  489. assertThat(result.getIds()).contains(expectedRule.getId());
  490. }
  491. }
  492. private void index() {
  493. ruleIndexer.indexOnStartup(Sets.newHashSet(TYPE_RULE, TYPE_RULE_EXTENSION));
  494. activeRuleIndexer.indexOnStartup(Sets.newHashSet(TYPE_ACTIVE_RULE));
  495. }
  496. private RuleQuery newRuleQuery() {
  497. return new RuleQuery().setOrganization(db.getDefaultOrganization());
  498. }
  499. private QProfileDto createJavaProfile() {
  500. return db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage("java"));
  501. }
  502. @Test
  503. public void search_by_activation_and_inheritance() {
  504. RuleDefinitionDto rule1 = createJavaRule();
  505. RuleDefinitionDto rule2 = createJavaRule();
  506. RuleDefinitionDto rule3 = createJavaRule();
  507. RuleDefinitionDto rule4 = createJavaRule();
  508. QProfileDto parent = createJavaProfile();
  509. QProfileDto child = createJavaProfile();
  510. db.qualityProfiles().activateRule(parent, rule1);
  511. db.qualityProfiles().activateRule(parent, rule2);
  512. db.qualityProfiles().activateRule(parent, rule3);
  513. db.qualityProfiles().activateRule(child, rule1, ar -> ar.setInheritance(INHERITED.name()));
  514. db.qualityProfiles().activateRule(child, rule2, ar -> ar.setInheritance(OVERRIDES.name()));
  515. db.qualityProfiles().activateRule(child, rule3, ar -> ar.setInheritance(INHERITED.name()));
  516. index();
  517. // all rules
  518. verifySearch(newRuleQuery(), rule1, rule2, rule3, rule4);
  519. // inherited/overrides rules on parent
  520. verifyEmptySearch(newRuleQuery().setActivation(true).setQProfile(parent).setInheritance(of(INHERITED.name())));
  521. verifyEmptySearch(newRuleQuery().setActivation(true).setQProfile(parent).setInheritance(of(OVERRIDES.name())));
  522. // inherited/overrides rules on child
  523. verifySearch(newRuleQuery().setActivation(true).setQProfile(child).setInheritance(of(INHERITED.name())), rule1, rule3);
  524. verifySearch(newRuleQuery().setActivation(true).setQProfile(child).setInheritance(of(OVERRIDES.name())), rule2);
  525. // inherited AND overridden on parent
  526. verifyEmptySearch(newRuleQuery().setActivation(true).setQProfile(parent).setInheritance(of(INHERITED.name(), OVERRIDES.name())));
  527. // inherited AND overridden on child
  528. verifySearch(newRuleQuery().setActivation(true).setQProfile(child).setInheritance(of(INHERITED.name(), OVERRIDES.name())), rule1, rule2, rule3);
  529. }
  530. @Test
  531. public void search_by_activation_and_severity() {
  532. RuleDefinitionDto major = createRule(setSeverity(MAJOR));
  533. RuleDefinitionDto minor = createRule(setSeverity(MINOR));
  534. RuleDefinitionDto info = createRule(setSeverity(INFO));
  535. QProfileDto profile1 = createJavaProfile();
  536. QProfileDto profile2 = createJavaProfile();
  537. db.qualityProfiles().activateRule(profile1, major, ar -> ar.setSeverity(BLOCKER));
  538. db.qualityProfiles().activateRule(profile2, major, ar -> ar.setSeverity(BLOCKER));
  539. db.qualityProfiles().activateRule(profile1, minor, ar -> ar.setSeverity(CRITICAL));
  540. index();
  541. // count activation severities of all active rules
  542. RuleQuery query = newRuleQuery().setActivation(true).setQProfile(profile1);
  543. verifySearch(query, major, minor);
  544. verifyFacet(query, RuleIndex.FACET_ACTIVE_SEVERITIES, entry(BLOCKER, 1L), entry(CRITICAL, 1L));
  545. // check stickyness of active severity facet
  546. query = newRuleQuery().setActivation(true).setQProfile(profile1).setActiveSeverities(singletonList(CRITICAL));
  547. verifySearch(query, minor);
  548. verifyFacet(query, RuleIndex.FACET_ACTIVE_SEVERITIES, entry(BLOCKER, 1L), entry(CRITICAL, 1L));
  549. }
  550. @Test
  551. public void facet_by_activation_severity_is_ignored_when_profile_is_not_specified() {
  552. RuleDefinitionDto rule = createJavaRule();
  553. QProfileDto profile = createJavaProfile();
  554. db.qualityProfiles().activateRule(profile, rule);
  555. index();
  556. RuleQuery query = newRuleQuery();
  557. verifyNoFacet(query, RuleIndex.FACET_ACTIVE_SEVERITIES);
  558. }
  559. private void verifyFacet(RuleQuery query, String facet, Map.Entry<String, Long>... expectedBuckets) {
  560. SearchIdResult<Integer> result = underTest.search(query, new SearchOptions().addFacets(facet));
  561. assertThat(result.getFacets().get(facet))
  562. .containsOnly(expectedBuckets);
  563. }
  564. private void verifyNoFacet(RuleQuery query, String facet) {
  565. SearchIdResult<Integer> result = underTest.search(query, new SearchOptions().addFacets(facet));
  566. assertThat(result.getFacets().get(facet)).isNull();
  567. }
  568. @Test
  569. public void listTags_should_return_both_system_tags_and_organization_specific_tags() {
  570. OrganizationDto organization = db.organizations().insert();
  571. RuleDefinitionDto rule1 = createRule(setSystemTags("sys1", "sys2"));
  572. createRuleMetadata(rule1, organization, setOrganization(organization), setTags("tag1"));
  573. RuleDefinitionDto rule2 = createRule(setSystemTags());
  574. createRuleMetadata(rule2, organization, setOrganization(organization), setTags("tag2"));
  575. index();
  576. assertThat(underTest.listTags(organization, null, 10)).containsOnly("tag1", "tag2", "sys1", "sys2");
  577. }
  578. @Test
  579. public void listTags_must_not_return_tags_of_other_organizations() {
  580. OrganizationDto organization1 = db.organizations().insert();
  581. RuleDefinitionDto rule1 = createRule(setSystemTags("sys1"));
  582. createRuleMetadata(rule1, organization1, setOrganization(organization1), setTags("tag1"));
  583. OrganizationDto organization2 = db.organizations().insert();
  584. RuleDefinitionDto rule2 = createRule(setSystemTags("sys2"));
  585. createRuleMetadata(rule2, organization2, setOrganization(organization2), setTags("tag2"));
  586. OrganizationDto organization3 = db.organizations().insert();
  587. index();
  588. assertThat(underTest.listTags(organization1, null, 10)).containsOnly("tag1", "sys1", "sys2");
  589. assertThat(underTest.listTags(organization2, null, 10)).containsOnly("tag2", "sys1", "sys2");
  590. assertThat(underTest.listTags(organization3, null, 10)).containsOnly("sys1", "sys2");
  591. }
  592. @Test
  593. public void fail_to_list_tags_when_size_greater_than_500() {
  594. OrganizationDto organization = db.organizations().insert();
  595. expectedException.expect(IllegalArgumentException.class);
  596. expectedException.expectMessage("Page size must be lower than or equals to 500");
  597. underTest.listTags(organization, null, 501);
  598. }
  599. @Test
  600. public void available_since() {
  601. RuleDefinitionDto ruleOld = createRule(setCreatedAt(1_000L));
  602. RuleDefinitionDto ruleOlder = createRule(setCreatedAt(2_000L));
  603. index();
  604. // 0. find all rules;
  605. verifySearch(new RuleQuery(), ruleOld, ruleOlder);
  606. // 1. find all rules available since a date;
  607. RuleQuery availableSinceQuery = new RuleQuery().setAvailableSince(2000L);
  608. verifySearch(availableSinceQuery, ruleOlder);
  609. // 2. find no new rules since tomorrow.
  610. RuleQuery availableSinceNowQuery = new RuleQuery().setAvailableSince(3000L);
  611. verifyEmptySearch(availableSinceNowQuery);
  612. }
  613. @Test
  614. public void global_facet_on_repositories_and_tags() {
  615. OrganizationDto organization = db.organizations().insert();
  616. createRule(setRepositoryKey("php"), setSystemTags("sysTag"));
  617. RuleDefinitionDto rule1 = createRule(setRepositoryKey("php"), setSystemTags());
  618. createRuleMetadata(rule1, organization, setTags("tag1"));
  619. RuleDefinitionDto rule2 = createRule(setRepositoryKey("javascript"), setSystemTags());
  620. createRuleMetadata(rule2, organization, setTags("tag1", "tag2"));
  621. index();
  622. // should not have any facet!
  623. RuleQuery query = new RuleQuery().setOrganization(organization);
  624. SearchIdResult result1 = underTest.search(query, new SearchOptions());
  625. assertThat(result1.getFacets().getAll()).isEmpty();
  626. // should not have any facet on non matching query!
  627. SearchIdResult result2 = underTest.search(new RuleQuery().setQueryText("aeiou"), new SearchOptions().addFacets(singletonList("repositories")));
  628. assertThat(result2.getFacets().getAll()).hasSize(1);
  629. assertThat(result2.getFacets().getAll().get("repositories")).isEmpty();
  630. // Repositories Facet is preset
  631. SearchIdResult result3 = underTest.search(query, new SearchOptions().addFacets(asList("repositories", "tags")));
  632. assertThat(result3.getFacets()).isNotNull();
  633. // Verify the value of a given facet
  634. Map<String, Long> repoFacets = result3.getFacets().get("repositories");
  635. assertThat(repoFacets).containsOnly(entry("php", 2L), entry("javascript", 1L));
  636. // Check that tag facet has both Tags and SystemTags values
  637. Map<String, Long> tagFacets = result3.getFacets().get("tags");
  638. assertThat(tagFacets).containsOnly(entry("tag1", 2L), entry("sysTag", 1L), entry("tag2", 1L));
  639. // Check that there are no other facets
  640. assertThat(result3.getFacets().getAll()).hasSize(2);
  641. }
  642. private void setupStickyFacets() {
  643. createRule(setRepositoryKey("xoo"), setRuleKey("S001"), setLanguage("java"), setSystemTags(), setType(BUG));
  644. createRule(setRepositoryKey("xoo"), setRuleKey("S002"), setLanguage("java"), setSystemTags(), setType(CODE_SMELL));
  645. createRule(setRepositoryKey("xoo"), setRuleKey("S003"), setLanguage("java"), setSystemTags("T1", "T2"), setType(CODE_SMELL));
  646. createRule(setRepositoryKey("xoo"), setRuleKey("S011"), setLanguage("cobol"), setSystemTags(), setType(CODE_SMELL));
  647. createRule(setRepositoryKey("xoo"), setRuleKey("S012"), setLanguage("cobol"), setSystemTags(), setType(BUG));
  648. createRule(setRepositoryKey("foo"), setRuleKey("S013"), setLanguage("cobol"), setSystemTags("T3", "T4"),
  649. setType(VULNERABILITY));
  650. createRule(setRepositoryKey("foo"), setRuleKey("S111"), setLanguage("cpp"), setSystemTags(), setType(BUG));
  651. createRule(setRepositoryKey("foo"), setRuleKey("S112"), setLanguage("cpp"), setSystemTags(), setType(CODE_SMELL));
  652. createRule(setRepositoryKey("foo"), setRuleKey("S113"), setLanguage("cpp"), setSystemTags("T2", "T3"), setType(CODE_SMELL));
  653. index();
  654. }
  655. @Test
  656. public void sticky_facets_base() {
  657. setupStickyFacets();
  658. RuleQuery query = new RuleQuery();
  659. assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(9);
  660. }
  661. /**
  662. * Facet with no filters at all
  663. */
  664. @Test
  665. public void sticky_facets_no_filters() {
  666. setupStickyFacets();
  667. OrganizationDto organization = db.organizations().insert();
  668. RuleQuery query = new RuleQuery().setOrganization(organization);
  669. SearchIdResult result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES,
  670. FACET_TAGS, FACET_TYPES)));
  671. Map<String, LinkedHashMap<String, Long>> facets = result.getFacets().getAll();
  672. assertThat(facets).hasSize(4);
  673. assertThat(facets.get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java", "cobol");
  674. assertThat(facets.get(FACET_REPOSITORIES).keySet()).containsExactly("xoo", "foo");
  675. assertThat(facets.get(FACET_TAGS).keySet()).containsOnly("T1", "T2", "T3", "T4");
  676. assertThat(facets.get(FACET_TYPES).keySet()).containsOnly("BUG", "CODE_SMELL", "VULNERABILITY");
  677. }
  678. /**
  679. * Facet with a language filter
  680. * -- lang facet should still have all language
  681. */
  682. @Test
  683. public void sticky_facets_with_1_filter() {
  684. setupStickyFacets();
  685. OrganizationDto organization = db.organizations().insert();
  686. RuleQuery query = new RuleQuery().setOrganization(organization).setLanguages(ImmutableList.of("cpp"));
  687. SearchIdResult result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES,
  688. FACET_REPOSITORIES, FACET_TAGS)));
  689. assertThat(result.getIds()).hasSize(3);
  690. assertThat(result.getFacets().getAll()).hasSize(3);
  691. assertThat(result.getFacets().get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java", "cobol");
  692. assertThat(result.getFacets().get(FACET_REPOSITORIES).keySet()).containsOnly("foo");
  693. assertThat(result.getFacets().get(FACET_TAGS).keySet()).containsOnly("T2", "T3");
  694. }
  695. @Test
  696. public void languages_facet_should_return_top_100_items() {
  697. rangeClosed(1, 101).forEach(i -> db.rules().insert(r -> r.setLanguage("lang" + i)));
  698. index();
  699. SearchIdResult result = underTest.search(new RuleQuery(), new SearchOptions().addFacets(singletonList(FACET_LANGUAGES)));
  700. assertThat(result.getFacets().get(FACET_LANGUAGES).size()).isEqualTo(100);
  701. }
  702. @Test
  703. public void repositories_facet_should_return_top_10_items() {
  704. rangeClosed(1, 11).forEach(i -> db.rules().insert(r -> r.setRepositoryKey("repo" + i)));
  705. index();
  706. SearchIdResult result = underTest.search(new RuleQuery(), new SearchOptions().addFacets(singletonList(FACET_REPOSITORIES)));
  707. assertThat(result.getFacets().get(FACET_REPOSITORIES).size()).isEqualTo(10);
  708. }
  709. @Test
  710. public void tags_facet_should_find_tags_of_specified_organization() {
  711. OrganizationDto organization = db.organizations().insert();
  712. RuleDefinitionDto rule = createRule(setSystemTags());
  713. createRuleMetadata(rule, organization, setTags("bla"));
  714. index();
  715. RuleQuery query = new RuleQuery().setOrganization(organization);
  716. SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
  717. SearchIdResult result = underTest.search(query, options);
  718. assertThat(result.getFacets().get(FACET_TAGS)).contains(entry("bla", 1L));
  719. }
  720. @Test
  721. public void tags_facet_should_return_top_100_items() {
  722. // default number of items returned in tag facet = 100
  723. String[] tags = get101Tags();
  724. createRule(setSystemTags(tags));
  725. index();
  726. RuleQuery query = new RuleQuery()
  727. .setOrganization(db.getDefaultOrganization());
  728. SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
  729. SearchIdResult result = underTest.search(query, options);
  730. assertThat(result.getFacets().get(FACET_TAGS).size()).isEqualTo(100);
  731. assertThat(result.getFacets().get(FACET_TAGS)).contains(entry("tag0", 1L), entry("tag25", 1L), entry("tag99", 1L));
  732. assertThat(result.getFacets().get(FACET_TAGS)).doesNotContain(entry("tagA", 1L));
  733. }
  734. @Test
  735. public void tags_facet_should_include_matching_selected_items() {
  736. // default number of items returned in tag facet = 100
  737. String[] tags = get101Tags();
  738. createRule(setSystemTags(tags));
  739. index();
  740. RuleQuery query = new RuleQuery()
  741. .setOrganization(db.getDefaultOrganization())
  742. .setTags(singletonList("tagA"));
  743. SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
  744. SearchIdResult result = underTest.search(query, options);
  745. assertThat(result.getFacets().get(FACET_TAGS).size()).isEqualTo(101);
  746. assertThat(result.getFacets().get(FACET_TAGS).entrySet()).extracting(e -> entry(e.getKey(), e.getValue())).contains(
  747. // check that selected item is added, although there are 100 other items
  748. entry("tagA", 1L),
  749. entry("tag0", 1L), entry("tag25", 1L), entry("tag99", 1L));
  750. }
  751. @Test
  752. public void tags_facet_should_not_find_tags_of_any_other_organization() {
  753. OrganizationDto organization1 = db.organizations().insert();
  754. OrganizationDto organization2 = db.organizations().insert();
  755. RuleDefinitionDto rule = createRule(setSystemTags());
  756. createRuleMetadata(rule, organization1, setTags("bla1"));
  757. createRuleMetadata(rule, organization2, setTags("bla2"));
  758. index();
  759. RuleQuery query = new RuleQuery().setOrganization(organization2);
  760. SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
  761. SearchIdResult result = underTest.search(query, options);
  762. assertThat(result.getFacets().get(FACET_TAGS).entrySet()).extracting(e -> entry(e.getKey(), e.getValue())).containsExactly(
  763. entry("bla2", 1L));
  764. }
  765. @Test
  766. public void tags_facet_should_be_available_if_organization_is_specified() {
  767. OrganizationDto organization = db.organizations().insert();
  768. RuleQuery query = new RuleQuery().setOrganization(organization);
  769. SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
  770. SearchIdResult result = underTest.search(query, options);
  771. assertThat(result.getFacets().get(FACET_TAGS)).isNotNull();
  772. }
  773. @Test
  774. public void tags_facet_should_be_unavailable_if_no_organization_is_specfified() {
  775. RuleQuery query = new RuleQuery();
  776. SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
  777. expectedException.expectMessage("Cannot use tags facet, if no organization is specified.");
  778. underTest.search(query, options);
  779. }
  780. /**
  781. * Facet with 2 filters
  782. * -- lang facet for tag T2
  783. * -- tag facet for lang cpp
  784. * -- repository for cpp & T2
  785. */
  786. @Test
  787. public void sticky_facets_with_2_filters() {
  788. setupStickyFacets();
  789. RuleQuery query = new RuleQuery()
  790. .setOrganization(db.getDefaultOrganization())
  791. .setLanguages(ImmutableList.of("cpp"))
  792. .setTags(ImmutableList.of("T2"));
  793. SearchIdResult result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES,
  794. FACET_TAGS)));
  795. assertThat(result.getIds()).hasSize(1);
  796. Facets facets = result.getFacets();
  797. assertThat(facets.getAll()).hasSize(3);
  798. assertThat(facets.get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java");
  799. assertThat(facets.get(FACET_REPOSITORIES).keySet()).containsOnly("foo");
  800. assertThat(facets.get(FACET_TAGS).keySet()).containsOnly("T2", "T3");
  801. }
  802. /**
  803. * Facet with 3 filters
  804. * -- lang facet for tag T2
  805. * -- tag facet for lang cpp & java
  806. * -- repository for (cpp || java) & T2
  807. * -- type
  808. */
  809. @Test
  810. public void sticky_facets_with_3_filters() {
  811. setupStickyFacets();
  812. RuleQuery query = new RuleQuery()
  813. .setOrganization(db.getDefaultOrganization())
  814. .setLanguages(ImmutableList.of("cpp", "java"))
  815. .setTags(ImmutableList.of("T2"))
  816. .setTypes(asList(BUG, CODE_SMELL));
  817. SearchIdResult result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS,
  818. FACET_TYPES)));
  819. assertThat(result.getIds()).hasSize(2);
  820. assertThat(result.getFacets().getAll()).hasSize(4);
  821. assertThat(result.getFacets().get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java");
  822. assertThat(result.getFacets().get(FACET_REPOSITORIES).keySet()).containsOnly("foo", "xoo");
  823. assertThat(result.getFacets().get(FACET_TAGS).keySet()).containsOnly("T1", "T2", "T3");
  824. assertThat(result.getFacets().get(FACET_TYPES).keySet()).containsOnly("CODE_SMELL");
  825. }
  826. @Test
  827. public void sort_by_name() {
  828. RuleDefinitionDto abcd = createRule(setName("abcd"));
  829. RuleDefinitionDto abc = createRule(setName("ABC"));
  830. RuleDefinitionDto fgh = createRule(setName("FGH"));
  831. index();
  832. // ascending
  833. RuleQuery query = new RuleQuery().setSortField(RuleIndexDefinition.FIELD_RULE_NAME);
  834. SearchIdResult<Integer> results = underTest.search(query, new SearchOptions());
  835. assertThat(results.getIds()).containsExactly(abc.getId(), abcd.getId(), fgh.getId());
  836. // descending
  837. query = new RuleQuery().setSortField(RuleIndexDefinition.FIELD_RULE_NAME).setAscendingSort(false);
  838. results = underTest.search(query, new SearchOptions());
  839. assertThat(results.getIds()).containsExactly(fgh.getId(), abcd.getId(), abc.getId());
  840. }
  841. @Test
  842. public void default_sort_is_by_updated_at_desc() {
  843. RuleDefinitionDto old = createRule(setCreatedAt(1000L), setUpdatedAt(1000L));
  844. RuleDefinitionDto oldest = createRule(setCreatedAt(1000L), setUpdatedAt(3000L));
  845. RuleDefinitionDto older = createRule(setCreatedAt(1000L), setUpdatedAt(2000L));
  846. index();
  847. SearchIdResult<Integer> results = underTest.search(new RuleQuery(), new SearchOptions());
  848. assertThat(results.getIds()).containsExactly(oldest.getId(), older.getId(), old.getId());
  849. }
  850. @Test
  851. public void fail_sort_by_language() {
  852. try {
  853. // Sorting on a field not tagged as sortable
  854. new RuleQuery().setSortField(RuleIndexDefinition.FIELD_RULE_LANGUAGE);
  855. fail();
  856. } catch (IllegalStateException e) {
  857. assertThat(e).hasMessage("Field 'lang' is not sortable");
  858. }
  859. }
  860. @Test
  861. public void paging() {
  862. createRule();
  863. createRule();
  864. createRule();
  865. index();
  866. // from 0 to 1 included
  867. SearchOptions options = new SearchOptions();
  868. options.setOffset(0).setLimit(2);
  869. SearchIdResult results = underTest.search(new RuleQuery(), options);
  870. assertThat(results.getTotal()).isEqualTo(3);
  871. assertThat(results.getIds()).hasSize(2);
  872. // from 0 to 9 included
  873. options.setOffset(0).setLimit(10);
  874. results = underTest.search(new RuleQuery(), options);
  875. assertThat(results.getTotal()).isEqualTo(3);
  876. assertThat(results.getIds()).hasSize(3);
  877. // from 2 to 11 included
  878. options.setOffset(2).setLimit(10);
  879. results = underTest.search(new RuleQuery(), options);
  880. assertThat(results.getTotal()).isEqualTo(3);
  881. assertThat(results.getIds()).hasSize(1);
  882. // from 2 to 11 included
  883. options.setOffset(2).setLimit(0);
  884. results = underTest.search(new RuleQuery(), options);
  885. assertThat(results.getTotal()).isEqualTo(3);
  886. assertThat(results.getIds()).hasSize(1);
  887. }
  888. @Test
  889. public void search_all_keys_by_query() {
  890. createRule(setRepositoryKey("javascript"), setRuleKey("X001"));
  891. createRule(setRepositoryKey("cobol"), setRuleKey("X001"));
  892. createRule(setRepositoryKey("php"), setRuleKey("S002"));
  893. index();
  894. // key
  895. assertThat(underTest.searchAll(new RuleQuery().setQueryText("X001"))).toIterable().hasSize(2);
  896. // partial key does not match
  897. assertThat(underTest.searchAll(new RuleQuery().setQueryText("X00"))).toIterable().isEmpty();
  898. // repo:key -> nice-to-have !
  899. assertThat(underTest.searchAll(new RuleQuery().setQueryText("javascript:X001"))).toIterable().hasSize(1);
  900. }
  901. @Test
  902. public void searchAll_keys_by_profile() {
  903. RuleDefinitionDto rule1 = createRule();
  904. RuleDefinitionDto rule2 = createRule();
  905. RuleDefinitionDto rule3 = createRule();
  906. QProfileDto profile1 = createJavaProfile();
  907. QProfileDto profile2 = createJavaProfile();
  908. db.qualityProfiles().activateRule(profile1, rule1);
  909. db.qualityProfiles().activateRule(profile2, rule1);
  910. db.qualityProfiles().activateRule(profile1, rule2);
  911. index();
  912. // inactive rules on profile
  913. List<SearchHit> ruleDocs = es.getDocuments(TYPE_RULE);
  914. List<SearchHit> activeRuleDocs = es.getDocuments(TYPE_ACTIVE_RULE);
  915. assertThat(underTest.searchAll(new RuleQuery().setActivation(false).setQProfile(profile2)))
  916. .toIterable()
  917. .containsOnly(rule2.getId(), rule3.getId());
  918. // active rules on profile
  919. assertThat(underTest.searchAll(new RuleQuery().setActivation(true).setQProfile(profile2)))
  920. .toIterable()
  921. .containsOnly(rule1.getId());
  922. }
  923. private String[] get101Tags() {
  924. String[] tags = new String[101];
  925. for (int i = 0; i < 100; i++) {
  926. tags[i] = "tag" + i;
  927. }
  928. tags[100] = "tagA";
  929. return tags;
  930. }
  931. }