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.

SearchActionIT.java 47KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 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.ws;
  21. import java.time.Clock;
  22. import java.time.Instant;
  23. import java.time.OffsetDateTime;
  24. import java.time.ZoneId;
  25. import java.time.format.DateTimeFormatter;
  26. import java.util.HashSet;
  27. import java.util.List;
  28. import java.util.Optional;
  29. import java.util.Set;
  30. import java.util.function.Consumer;
  31. import java.util.function.Function;
  32. import java.util.stream.Collectors;
  33. import javax.annotation.Nullable;
  34. import org.junit.Before;
  35. import org.junit.Test;
  36. import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
  37. import org.sonar.api.resources.Languages;
  38. import org.sonar.api.rule.RuleKey;
  39. import org.sonar.api.rule.RuleStatus;
  40. import org.sonar.api.rules.RuleType;
  41. import org.sonar.api.server.debt.DebtRemediationFunction;
  42. import org.sonar.api.server.ws.WebService;
  43. import org.sonar.api.utils.System2;
  44. import org.sonar.core.util.UuidFactory;
  45. import org.sonar.core.util.UuidFactoryFast;
  46. import org.sonar.db.DbTester;
  47. import org.sonar.db.qualityprofile.ActiveRuleParamDto;
  48. import org.sonar.db.qualityprofile.QProfileDto;
  49. import org.sonar.db.rule.RuleDescriptionSectionContextDto;
  50. import org.sonar.db.rule.RuleDescriptionSectionDto;
  51. import org.sonar.db.rule.RuleDto;
  52. import org.sonar.db.rule.RuleParamDto;
  53. import org.sonar.db.user.UserDto;
  54. import org.sonar.server.es.EsTester;
  55. import org.sonar.server.exceptions.NotFoundException;
  56. import org.sonar.server.language.LanguageTesting;
  57. import org.sonar.server.pushapi.qualityprofile.QualityProfileChangeEventService;
  58. import org.sonar.server.qualityprofile.ActiveRuleChange;
  59. import org.sonar.server.qualityprofile.QProfileRules;
  60. import org.sonar.server.qualityprofile.QProfileRulesImpl;
  61. import org.sonar.server.qualityprofile.RuleActivation;
  62. import org.sonar.server.qualityprofile.builtin.RuleActivator;
  63. import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
  64. import org.sonar.server.rule.RuleDescriptionFormatter;
  65. import org.sonar.server.rule.index.RuleIndex;
  66. import org.sonar.server.rule.index.RuleIndexer;
  67. import org.sonar.server.tester.UserSessionRule;
  68. import org.sonar.server.text.MacroInterpreter;
  69. import org.sonar.server.util.IntegerTypeValidation;
  70. import org.sonar.server.util.StringTypeValidation;
  71. import org.sonar.server.util.TypeValidations;
  72. import org.sonar.server.ws.TestRequest;
  73. import org.sonar.server.ws.WsActionTester;
  74. import org.sonarqube.ws.Common;
  75. import org.sonarqube.ws.Rules;
  76. import org.sonarqube.ws.Rules.Rule;
  77. import org.sonarqube.ws.Rules.SearchResponse;
  78. import static java.util.Arrays.asList;
  79. import static java.util.Arrays.stream;
  80. import static java.util.Collections.emptySet;
  81. import static java.util.Collections.singleton;
  82. import static java.util.Collections.singletonList;
  83. import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
  84. import static org.assertj.core.api.Assertions.assertThat;
  85. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  86. import static org.assertj.core.api.Assertions.tuple;
  87. import static org.assertj.guava.api.Assertions.entry;
  88. import static org.mockito.ArgumentMatchers.anyString;
  89. import static org.mockito.Mockito.doReturn;
  90. import static org.mockito.Mockito.mock;
  91. import static org.mockito.Mockito.when;
  92. import static org.sonar.api.rule.Severity.BLOCKER;
  93. import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.RESOURCES_SECTION_KEY;
  94. import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection;
  95. import static org.sonar.db.rule.RuleTesting.newRule;
  96. import static org.sonar.db.rule.RuleTesting.newRuleWithoutDescriptionSection;
  97. import static org.sonar.db.rule.RuleTesting.setSystemTags;
  98. import static org.sonar.db.rule.RuleTesting.setTags;
  99. import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION;
  100. import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_COMPARE_TO_PROFILE;
  101. import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_QPROFILE;
  102. import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_RULE_KEY;
  103. import static org.sonarqube.ws.Rules.Rule.DescriptionSection.Context.newBuilder;
  104. public class SearchActionIT {
  105. private static final String JAVA = "java";
  106. @org.junit.Rule
  107. public UserSessionRule userSession = UserSessionRule.standalone();
  108. private final System2 system2 = new AlwaysIncreasingSystem2();
  109. @org.junit.Rule
  110. public DbTester db = DbTester.create(system2);
  111. @org.junit.Rule
  112. public EsTester es = EsTester.create();
  113. private final RuleIndex ruleIndex = new RuleIndex(es.client(), system2);
  114. private final RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
  115. private final ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client());
  116. private final Languages languages = LanguageTesting.newLanguages(JAVA, "js");
  117. private final ActiveRuleCompleter activeRuleCompleter = new ActiveRuleCompleter(db.getDbClient(), languages);
  118. private final RuleQueryFactory ruleQueryFactory = new RuleQueryFactory(db.getDbClient());
  119. private final MacroInterpreter macroInterpreter = mock(MacroInterpreter.class);
  120. private final QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
  121. private final RuleMapper ruleMapper = new RuleMapper(languages, macroInterpreter, new RuleDescriptionFormatter());
  122. private final SearchAction underTest = new SearchAction(ruleIndex, activeRuleCompleter, ruleQueryFactory, db.getDbClient(), ruleMapper,
  123. new RuleWsSupport(db.getDbClient(), userSession));
  124. private final TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation()));
  125. private final RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, db.getDbClient(), typeValidations, userSession);
  126. private final QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer,
  127. qualityProfileChangeEventService);
  128. private final WsActionTester ws = new WsActionTester(underTest);
  129. private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
  130. @Before
  131. public void before() {
  132. doReturn("interpreted").when(macroInterpreter).interpret(anyString());
  133. }
  134. @Test
  135. public void test_definition() {
  136. WebService.Action def = ws.getDef();
  137. assertThat(def.isPost()).isFalse();
  138. assertThat(def.since()).isEqualTo("4.4");
  139. assertThat(def.isInternal()).isFalse();
  140. assertThat(def.responseExampleAsString()).isNotEmpty();
  141. assertThat(def.params()).hasSize(28);
  142. WebService.Param compareToProfile = def.param("compareToProfile");
  143. assertThat(compareToProfile.since()).isEqualTo("6.5");
  144. assertThat(compareToProfile.isRequired()).isFalse();
  145. assertThat(compareToProfile.isInternal()).isTrue();
  146. assertThat(compareToProfile.description()).isEqualTo("Quality profile key to filter rules that are activated. Meant to compare easily to profile set in 'qprofile'");
  147. }
  148. @Test
  149. public void return_empty_result() {
  150. Rules.SearchResponse response = ws.newRequest()
  151. .setParam(WebService.Param.FIELDS, "actives")
  152. .executeProtobuf(Rules.SearchResponse.class);
  153. assertThat(response.getPaging().getTotal()).isZero();
  154. assertThat(response.getPaging().getPageIndex()).isOne();
  155. assertThat(response.getPaging().getPageSize()).isNotZero();
  156. assertThat(response.getRulesCount()).isZero();
  157. }
  158. @Test
  159. public void return_all_rules() {
  160. RuleDto rule1 = db.rules().insert(r1 -> r1.setLanguage("java").setNoteUserUuid(null));
  161. RuleDto rule2 = db.rules().insert(r1 -> r1.setLanguage("java").setNoteUserUuid(null));
  162. indexRules();
  163. verify(r -> {
  164. }, rule1, rule2);
  165. }
  166. @Test
  167. public void return_note_login() {
  168. UserDto user1 = db.users().insertUser();
  169. RuleDto rule1 = db.rules().insert(r -> r.setNoteUserUuid(user1.getUuid()));
  170. UserDto disableUser = db.users().insertDisabledUser();
  171. RuleDto rule2 = db.rules().insert(r -> r.setNoteUserUuid(disableUser.getUuid()));
  172. indexRules();
  173. SearchResponse result = ws.newRequest()
  174. .setParam("f", "noteLogin")
  175. .executeProtobuf(SearchResponse.class);
  176. assertThat(result.getRulesList())
  177. .extracting(Rule::getKey, Rule::getNoteLogin)
  178. .containsExactlyInAnyOrder(
  179. tuple(rule1.getKey().toString(), user1.getLogin()),
  180. tuple(rule2.getKey().toString(), disableUser.getLogin()));
  181. }
  182. @Test
  183. public void dont_fail_if_note_author_no_longer_exists() {
  184. // note: this can only happen due to DB corruption (user deleted)
  185. RuleDto rule1 = db.rules().insert(r -> r.setNoteUserUuid("non-existent"));
  186. indexRules();
  187. SearchResponse result = ws.newRequest()
  188. .setParam("f", "noteLogin")
  189. .executeProtobuf(SearchResponse.class);
  190. assertThat(result.getRulesList())
  191. .extracting(Rule::getKey, Rule::getNoteLogin)
  192. .containsExactlyInAnyOrder(
  193. tuple(rule1.getKey().toString(), ""));
  194. }
  195. @Test
  196. public void filter_by_rule_key() {
  197. RuleDto rule1 = db.rules().insert(r1 -> r1.setLanguage("java").setNoteUserUuid(null));
  198. db.rules().insert(r1 -> r1.setLanguage("java").setNoteUserUuid(null));
  199. indexRules();
  200. verify(r -> r.setParam(PARAM_RULE_KEY, rule1.getKey().toString()), rule1);
  201. verifyNoResults(r -> r.setParam(PARAM_RULE_KEY, "missing"));
  202. }
  203. @Test
  204. public void filter_by_rule_name() {
  205. RuleDto rule1 = db.rules().insert(r1 -> r1.setName("Best rule ever").setNoteUserUuid(null));
  206. RuleDto rule2 = db.rules().insert(r1 -> r1.setName("Some other stuff").setNoteUserUuid(null));
  207. indexRules();
  208. verify(r -> r.setParam("q", "Be"), rule1);
  209. verify(r -> r.setParam("q", "Bes"), rule1);
  210. verify(r -> r.setParam("q", "Best"), rule1);
  211. verify(r -> r.setParam("q", "Best "), rule1);
  212. verify(r -> r.setParam("q", "Best rule"), rule1);
  213. verify(r -> r.setParam("q", "Best rule eve"), rule1);
  214. verify(r -> r.setParam("q", "Best rule ever"), rule1);
  215. verify(r -> r.setParam("q", "ru ev"), rule1);
  216. verify(r -> r.setParam("q", "ru ever"), rule1);
  217. verify(r -> r.setParam("q", "ev ve ver ru le"), rule1);
  218. verify(r -> r.setParam("q", "other"), rule2);
  219. }
  220. @Test
  221. public void filter_by_rule_name_requires_all_words_to_match() {
  222. RuleDto rule1 = db.rules().insert(r1 -> r1.setName("Best rule ever").setNoteUserUuid(null));
  223. RuleDto rule2 = db.rules().insert(r1 -> r1.setName("Some other stuff").setNoteUserUuid(null));
  224. indexRules();
  225. verify(r -> r.setParam("q", "Best other"));
  226. verify(r -> r.setParam("q", "Best rule"), rule1);
  227. verify(r -> r.setParam("q", "rule ever"), rule1);
  228. }
  229. @Test
  230. public void filter_by_rule_name_does_not_interpret_query() {
  231. RuleDto rule1 = db.rules().insert(r1 -> r1.setName("Best rule for-ever").setNoteUserUuid(null));
  232. RuleDto rule2 = db.rules().insert(r1 -> r1.setName("Some other stuff").setNoteUserUuid(null));
  233. indexRules();
  234. // do not interpret "-" as a "not"
  235. verify(r -> r.setParam("q", "-ever"), rule1);
  236. }
  237. @Test
  238. public void filter_by_rule_description() {
  239. RuleDto rule1 = db.rules().insert(
  240. newRule(createDefaultRuleDescriptionSection(uuidFactory.create(), "This is the <bold>best</bold> rule now&amp;for<b>ever</b>"))
  241. .setNoteUserUuid(null));
  242. db.rules().insert(r1 -> r1.setName("Some other stuff").setNoteUserUuid(null));
  243. indexRules();
  244. verify(r -> r.setParam("q", "Best "), rule1);
  245. verify(r -> r.setParam("q", "bold"));
  246. verify(r -> r.setParam("q", "now&forever"), rule1);
  247. }
  248. @Test
  249. public void filter_with_context_specific_rule_description() {
  250. RuleDescriptionSectionDto section1context1 = createRuleDescriptionSectionWithContext(RESOURCES_SECTION_KEY, "<div>I want to fix with Spring</div>", "ctx1");
  251. RuleDescriptionSectionDto section1context2 = createRuleDescriptionSectionWithContext(RESOURCES_SECTION_KEY, "<div>Another context</div>", "ctx2");
  252. RuleDto ruleDto = newRuleWithoutDescriptionSection()
  253. .setNoteUserUuid(null)
  254. .addRuleDescriptionSectionDto(section1context1)
  255. .addRuleDescriptionSectionDto(section1context2);
  256. db.rules().insert(ruleDto);
  257. indexRules();
  258. verify(r -> r.setParam("q", "Spring "), ruleDto);
  259. verify(r -> r.setParam("q", "bold"));
  260. verify(r -> r.setParam("q", "context"), ruleDto);
  261. }
  262. @Test
  263. public void filter_by_rule_name_or_descriptions_requires_all_words_to_match_anywhere() {
  264. RuleDto rule1 = db.rules().insert(newRuleWithoutDescriptionSection().setName("Best rule ever").setNoteUserUuid(null)
  265. .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "This is a good rule")));
  266. db.rules().insert(newRuleWithoutDescriptionSection().setName("Another thing").setNoteUserUuid(null)
  267. .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Another thing")));
  268. indexRules();
  269. verify(r -> r.setParam("q", "Best good"), rule1);
  270. verify(r -> r.setParam("q", "Best Another"));
  271. }
  272. @Test
  273. public void return_all_rule_fields_by_default() {
  274. OffsetDateTime dateTime = OffsetDateTime.now(Clock.fixed(Instant.ofEpochMilli(1687816800000L), ZoneId.systemDefault()));
  275. RuleDto rule = db.rules().insert(
  276. r -> r.setCreatedAt(dateTime.toInstant().toEpochMilli()),
  277. r -> r.setUpdatedAt(dateTime.toInstant().toEpochMilli()),
  278. r -> r.setGapDescription("Gap Description"),
  279. r -> r.setIsTemplate(true),
  280. r -> r.setName("Name"),
  281. r -> r.setRepositoryKey("repo_key"),
  282. r -> r.setSeverity("MINOR"),
  283. r -> r.setLanguage("java")
  284. );
  285. indexRules();
  286. Rules.SearchResponse response = ws.newRequest().executeProtobuf(Rules.SearchResponse.class);
  287. Rules.Rule result = response.getRules(0);
  288. assertThat(result.getCreatedAt()).isEqualTo(dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ")));
  289. assertThat(result.getUpdatedAt()).isEqualTo(dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ")));
  290. assertThat(result.getGapDescription()).isEqualTo("Gap Description");
  291. assertThat(result.hasIsTemplate()).isTrue();
  292. assertThat(result.getName()).isEqualTo("Name");
  293. assertThat(result.getRepo()).isEqualTo("repo_key");
  294. assertThat(result.getSeverity()).isEqualTo("MINOR");
  295. assertThat(result.getType().name()).isEqualTo(RuleType.valueOf(rule.getType()).name());
  296. assertThat(result.getLang()).isEqualTo("java");
  297. }
  298. @Test
  299. public void return_subset_of_fields() {
  300. RuleDto rule = db.rules().insert(r -> r.setLanguage("java"));
  301. indexRules();
  302. Rules.SearchResponse response = ws.newRequest()
  303. .setParam(WebService.Param.FIELDS, "createdAt,langName,educationPrinciples")
  304. .executeProtobuf(Rules.SearchResponse.class);
  305. Rules.Rule result = response.getRules(0);
  306. // mandatory fields
  307. assertThat(result.getKey()).isEqualTo(rule.getKey().toString());
  308. assertThat(result.getType().getNumber()).isEqualTo(rule.getType());
  309. // selected fields
  310. assertThat(result.getCreatedAt()).isNotEmpty();
  311. assertThat(result.getLangName()).isNotEmpty();
  312. assertThat(result.getEducationPrinciples().getEducationPrinciplesList()).containsExactlyElementsOf(rule.getEducationPrinciples());
  313. // not returned fields
  314. assertThat(result.hasGapDescription()).isFalse();
  315. assertThat(result.hasHtmlDesc()).isFalse();
  316. assertThat(result.hasIsTemplate()).isFalse();
  317. assertThat(result.hasLang()).isFalse();
  318. assertThat(result.hasName()).isFalse();
  319. assertThat(result.hasSeverity()).isFalse();
  320. assertThat(result.hasRepo()).isFalse();
  321. assertThat(result.hasUpdatedAt()).isFalse();
  322. }
  323. @Test
  324. public void return_deprecatedKeys_in_response_on_demand() {
  325. RuleDto rule1 = db.rules().insert(r -> r.setLanguage("java"));
  326. db.rules().insertDeprecatedKey(r -> r.setRuleUuid(rule1.getUuid()).setOldRuleKey("oldrulekey").setOldRepositoryKey("oldrepositorykey"));
  327. db.rules().insertDeprecatedKey(r -> r.setRuleUuid(rule1.getUuid()).setOldRuleKey("oldrulekey2").setOldRepositoryKey("oldrepositorykey2"));
  328. RuleDto rule2 = db.rules().insert(r -> r.setLanguage("javascript"));
  329. indexRules();
  330. Rules.SearchResponse response = ws.newRequest()
  331. .setParam(WebService.Param.FIELDS, "deprecatedKeys")
  332. .executeProtobuf(Rules.SearchResponse.class);
  333. System.err.println(response.getRulesList());
  334. assertThat(response.getRulesList()).satisfies(l -> {
  335. assertThat(l).hasSize(2);
  336. assertThat(l).anySatisfy(e -> {
  337. assertThat(e.getKey()).isEqualTo(rule1.getKey().toString());
  338. assertThat(e.getType().getNumber()).isEqualTo(rule1.getType());
  339. assertThat(e.getDeprecatedKeys()).isNotNull();
  340. assertThat(e.getDeprecatedKeys().getDeprecatedKeyList()).contains("oldrepositorykey:oldrulekey", "oldrepositorykey2:oldrulekey2");
  341. });
  342. assertThat(l).anySatisfy(e -> {
  343. assertThat(e.getKey()).isEqualTo(rule2.getKey().toString());
  344. assertThat(e.getType().getNumber()).isEqualTo(rule2.getType());
  345. assertThat(e.getDeprecatedKeys()).isNotNull();
  346. assertThat(e.getDeprecatedKeys().getDeprecatedKeyList()).isEmpty();
  347. });
  348. });
  349. }
  350. @Test
  351. public void should_filter_on_specific_tags() {
  352. RuleDto rule1 = db.rules().insert(r -> r.setLanguage("java").setTags(Set.of("tag1", "tag2")));
  353. db.rules().insert(r -> r.setLanguage("java"));
  354. indexRules();
  355. Consumer<TestRequest> request = r -> r
  356. .setParam("f", "repo,name")
  357. .setParam("tags", rule1.getTags().stream().collect(Collectors.joining(",")));
  358. verify(request, rule1);
  359. }
  360. @Test
  361. public void when_searching_for_several_tags_combine_them_with_OR() {
  362. RuleDto bothTagsRule = db.rules().insert(r -> r.setLanguage("java"), setTags("tag1", "tag2"));
  363. RuleDto oneTagRule = db.rules().insert(r -> r.setLanguage("java"), setTags("tag1"));
  364. RuleDto otherTagRule = db.rules().insert(r -> r.setLanguage("java"), setTags("tag2"));
  365. db.rules().insert(r -> r.setLanguage("java"), setTags());
  366. indexRules();
  367. Consumer<TestRequest> request = r -> r
  368. .setParam("f", "repo,name")
  369. .setParam("tags", "tag1,tag2");
  370. verify(request, bothTagsRule, oneTagRule, otherTagRule);
  371. }
  372. @Test
  373. public void should_list_tags_in_tags_facet() {
  374. String[] tags = get101Tags();
  375. db.rules().insert(setSystemTags("x"), setTags(tags));
  376. indexRules();
  377. SearchResponse result = ws.newRequest()
  378. .setParam("f", "repo,name")
  379. .setParam("facets", "tags")
  380. .executeProtobuf(SearchResponse.class);
  381. assertThat(result.getFacets().getFacets(0).getValuesList()).extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
  382. .contains(tuple("tag0", 1L), tuple("tag25", 1L), tuple("tag99", 1L))
  383. .doesNotContain(tuple("x", 1L));
  384. }
  385. @Test
  386. public void should_list_tags_ordered_by_count_then_by_name_in_tags_facet() {
  387. db.rules().insert(setSystemTags("tag7", "tag5", "tag3", "tag1", "tag9"), setTags("tag2", "tag4", "tag6", "tag8", "tagA"));
  388. db.rules().insert(setSystemTags("tag2"), setTags());
  389. indexRules();
  390. SearchResponse result = ws.newRequest()
  391. .setParam("f", "repo,name")
  392. .setParam("facets", "tags")
  393. .executeProtobuf(SearchResponse.class);
  394. assertThat(result.getFacets().getFacets(0).getValuesList()).extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
  395. .containsExactly(tuple("tag2", 2L), tuple("tag1", 1L), tuple("tag3", 1L), tuple("tag4", 1L), tuple("tag5", 1L), tuple("tag6", 1L), tuple("tag7", 1L), tuple("tag8", 1L),
  396. tuple("tag9", 1L), tuple("tagA", 1L));
  397. }
  398. @Test
  399. public void should_include_selected_matching_tag_in_facet() {
  400. db.rules().insert(
  401. setSystemTags("tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8", "tag9", "tagA", "x"),
  402. r -> r.setNoteUserUuid(null));
  403. indexRules();
  404. SearchResponse result = ws.newRequest()
  405. .setParam("facets", "tags")
  406. .setParam("tags", "x")
  407. .executeProtobuf(SearchResponse.class);
  408. assertThat(result.getFacets().getFacets(0).getValuesList()).extracting(v -> entry(v.getVal(), v.getCount())).contains(entry("x", 1L));
  409. }
  410. @Test
  411. public void should_included_selected_non_matching_tag_in_facet() {
  412. RuleDto rule = db.rules().insert(setSystemTags("tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8", "tag9", "tagA"));
  413. indexRules();
  414. SearchResponse result = ws.newRequest()
  415. .setParam("facets", "tags")
  416. .setParam("tags", "x")
  417. .executeProtobuf(SearchResponse.class);
  418. assertThat(result.getFacets().getFacets(0).getValuesList()).extracting(v -> entry(v.getVal(), v.getCount())).contains(entry("x", 0L));
  419. }
  420. @Test
  421. public void should_return_specific_tags() {
  422. RuleDto rule = db.rules().insert(r -> r.setLanguage("java"), setTags("tag1", "tag2"));
  423. indexRules();
  424. SearchResponse result = ws.newRequest()
  425. .setParam("f", "tags")
  426. .executeProtobuf(SearchResponse.class);
  427. assertThat(result.getRulesList()).extracting(Rule::getKey).containsExactly(rule.getKey().toString());
  428. assertThat(result.getRulesList())
  429. .extracting(Rule::getTags).flatExtracting(Rules.Tags::getTagsList)
  430. .containsExactly(rule.getTags().toArray(new String[0]));
  431. }
  432. @Test
  433. public void should_return_specified_fields() {
  434. when(macroInterpreter.interpret(anyString())).thenAnswer(invocation -> invocation.getArgument(0));
  435. RuleDescriptionSectionDto section1context1 = createRuleDescriptionSectionWithContext(RESOURCES_SECTION_KEY, "<div>I want to fix with Spring</div>", "ctx1");
  436. RuleDescriptionSectionDto section1context2 = createRuleDescriptionSectionWithContext(RESOURCES_SECTION_KEY, "<div>Another context</div>", "ctx2");
  437. RuleDto rule = newRuleWithoutDescriptionSection()
  438. .setLanguage("java")
  439. .addRuleDescriptionSectionDto(section1context1)
  440. .addRuleDescriptionSectionDto(section1context2);
  441. db.rules().insert(rule);
  442. indexRules();
  443. checkField(rule, "repo", Rule::getRepo, rule.getRepositoryKey());
  444. checkField(rule, "name", Rule::getName, rule.getName());
  445. checkField(rule, "severity", Rule::getSeverity, rule.getSeverityString());
  446. checkField(rule, "status", r -> r.getStatus().toString(), rule.getStatus().toString());
  447. checkField(rule, "internalKey", Rule::getInternalKey, rule.getConfigKey());
  448. checkField(rule, "isTemplate", Rule::getIsTemplate, rule.isTemplate());
  449. checkField(rule, "sysTags",
  450. r -> r.getSysTags().getSysTagsList().stream().collect(Collectors.joining(",")),
  451. rule.getSystemTags().stream().collect(Collectors.joining(",")));
  452. checkField(rule, "lang", Rule::getLang, rule.getLanguage());
  453. checkField(rule, "langName", Rule::getLangName, languages.get(rule.getLanguage()).getName());
  454. checkField(rule, "gapDescription", Rule::getGapDescription, rule.getGapDescription());
  455. checkDescriptionSections(rule, rule.getRuleDescriptionSectionDtos().stream()
  456. .map(SearchActionIT::toProtobufDto)
  457. .collect(Collectors.toSet())
  458. );
  459. }
  460. private RuleDescriptionSectionDto createRuleDescriptionSectionWithContext(String key, String content, @Nullable String contextKey) {
  461. RuleDescriptionSectionContextDto contextDto = Optional.ofNullable(contextKey)
  462. .map(c -> RuleDescriptionSectionContextDto.of(contextKey, contextKey + " display name"))
  463. .orElse(null);
  464. return RuleDescriptionSectionDto.builder()
  465. .uuid(uuidFactory.create())
  466. .key(key)
  467. .content(content)
  468. .context(contextDto)
  469. .build();
  470. }
  471. private static Rule.DescriptionSection toProtobufDto(RuleDescriptionSectionDto section) {
  472. Rule.DescriptionSection.Builder builder = Rule.DescriptionSection.newBuilder().setKey(section.getKey()).setContent(section.getContent());
  473. if (section.getContext() != null) {
  474. RuleDescriptionSectionContextDto context = section.getContext();
  475. builder.setContext(newBuilder().setKey(context.getKey()).setDisplayName(context.getDisplayName()).build());
  476. }
  477. return builder.build();
  478. }
  479. @Test
  480. public void return_lang_key_field_when_language_name_is_not_available() {
  481. String unknownLanguage = "unknown_" + randomAlphanumeric(5);
  482. RuleDto rule = db.rules().insert(r -> r.setLanguage(unknownLanguage));
  483. indexRules();
  484. SearchResponse result = ws.newRequest()
  485. .setParam("f", "langName")
  486. .executeProtobuf(SearchResponse.class);
  487. assertThat(result.getPaging().getTotal()).isOne();
  488. assertThat(result.getPaging().getPageIndex()).isOne();
  489. assertThat(result.getRulesCount()).isOne();
  490. Rule searchedRule = result.getRules(0);
  491. assertThat(searchedRule).isNotNull();
  492. assertThat(searchedRule.getLangName()).isEqualTo(unknownLanguage);
  493. }
  494. @Test
  495. public void search_debt_rules_with_default_and_overridden_debt_values() {
  496. db.rules().insert(r -> r.setLanguage("java")
  497. .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
  498. .setDefRemediationGapMultiplier("1h")
  499. .setDefRemediationBaseEffort("15min")
  500. .setRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
  501. .setRemediationGapMultiplier("2h")
  502. .setRemediationBaseEffort("25min"));
  503. indexRules();
  504. SearchResponse result = ws.newRequest()
  505. .setParam("f", "debtRemFn,remFnOverloaded,defaultDebtRemFn")
  506. .executeProtobuf(SearchResponse.class);
  507. assertThat(result.getPaging().getTotal()).isOne();
  508. assertThat(result.getPaging().getPageIndex()).isOne();
  509. assertThat(result.getRulesCount()).isOne();
  510. Rule searchedRule = result.getRules(0);
  511. assertThat(searchedRule).isNotNull();
  512. assertThat(searchedRule.getDefaultRemFnGapMultiplier()).isEqualTo("1h");
  513. assertThat(searchedRule.getDefaultRemFnBaseEffort()).isEqualTo("15min");
  514. assertThat(searchedRule.getDefaultDebtRemFnType()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
  515. assertThat(searchedRule.getDefaultRemFnBaseEffort()).isEqualTo("15min");
  516. assertThat(searchedRule.getDefaultRemFnGapMultiplier()).isEqualTo("1h");
  517. assertThat(searchedRule.getDefaultRemFnType()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
  518. assertThat(searchedRule.getRemFnOverloaded()).isTrue();
  519. assertThat(searchedRule.getRemFnGapMultiplier()).isEqualTo("2h");
  520. assertThat(searchedRule.getRemFnBaseEffort()).isEqualTo("25min");
  521. assertThat(searchedRule.getDebtRemFnType()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
  522. }
  523. @Test
  524. public void search_debt_rules_with_default_linear_offset_and_overridden_constant_debt() {
  525. db.rules().insert(r -> r.setLanguage("java")
  526. .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
  527. .setDefRemediationGapMultiplier("1h")
  528. .setDefRemediationBaseEffort("15min")
  529. .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name())
  530. .setRemediationGapMultiplier(null)
  531. .setRemediationBaseEffort("5min"));
  532. indexRules();
  533. SearchResponse result = ws.newRequest()
  534. .setParam("f", "debtRemFn,remFnOverloaded,defaultDebtRemFn")
  535. .executeProtobuf(SearchResponse.class);
  536. assertThat(result.getPaging().getTotal()).isOne();
  537. assertThat(result.getPaging().getPageIndex()).isOne();
  538. assertThat(result.getRulesCount()).isOne();
  539. Rule searchedRule = result.getRules(0);
  540. assertThat(searchedRule).isNotNull();
  541. assertThat(searchedRule.getDefaultRemFnGapMultiplier()).isEqualTo("1h");
  542. assertThat(searchedRule.getDefaultRemFnBaseEffort()).isEqualTo("15min");
  543. assertThat(searchedRule.getDefaultDebtRemFnType()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
  544. assertThat(searchedRule.getDefaultRemFnBaseEffort()).isEqualTo("15min");
  545. assertThat(searchedRule.getDefaultRemFnGapMultiplier()).isEqualTo("1h");
  546. assertThat(searchedRule.getDefaultRemFnType()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
  547. assertThat(searchedRule.getRemFnOverloaded()).isTrue();
  548. assertThat(searchedRule.getRemFnGapMultiplier()).isEmpty();
  549. assertThat(searchedRule.getRemFnBaseEffort()).isEqualTo("5min");
  550. assertThat(searchedRule.getDebtRemFnType()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name());
  551. }
  552. @Test
  553. public void search_debt_rules_with_default_linear_offset_and_overridden_linear_debt() {
  554. db.rules().insert(r -> r.setLanguage("java")
  555. .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
  556. .setDefRemediationGapMultiplier("1h")
  557. .setDefRemediationBaseEffort("15min")
  558. .setRemediationFunction(DebtRemediationFunction.Type.LINEAR.name())
  559. .setRemediationGapMultiplier("1h")
  560. .setRemediationBaseEffort(null));
  561. indexRules();
  562. SearchResponse result = ws.newRequest()
  563. .setParam("f", "debtRemFn,remFnOverloaded,defaultDebtRemFn")
  564. .executeProtobuf(SearchResponse.class);
  565. assertThat(result.getPaging().getTotal()).isOne();
  566. assertThat(result.getPaging().getPageIndex()).isOne();
  567. assertThat(result.getRulesCount()).isOne();
  568. Rule searchedRule = result.getRules(0);
  569. assertThat(searchedRule).isNotNull();
  570. assertThat(searchedRule.getDefaultRemFnGapMultiplier()).isEqualTo("1h");
  571. assertThat(searchedRule.getDefaultRemFnBaseEffort()).isEqualTo("15min");
  572. assertThat(searchedRule.getDefaultDebtRemFnType()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
  573. assertThat(searchedRule.getDefaultRemFnBaseEffort()).isEqualTo("15min");
  574. assertThat(searchedRule.getDefaultRemFnGapMultiplier()).isEqualTo("1h");
  575. assertThat(searchedRule.getDefaultRemFnType()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET.name());
  576. assertThat(searchedRule.getRemFnOverloaded()).isTrue();
  577. assertThat(searchedRule.getRemFnGapMultiplier()).isEqualTo("1h");
  578. assertThat(searchedRule.getRemFnBaseEffort()).isEmpty();
  579. assertThat(searchedRule.getDebtRemFnType()).isEqualTo(DebtRemediationFunction.Type.LINEAR.name());
  580. }
  581. @Test
  582. public void search_template_rules() {
  583. RuleDto templateRule = db.rules().insert(r -> r.setLanguage("java")
  584. .setIsTemplate(true));
  585. RuleDto rule = db.rules().insert(r -> r.setLanguage("java")
  586. .setTemplateUuid(templateRule.getUuid()));
  587. indexRules();
  588. SearchResponse result = ws.newRequest()
  589. .setParam("f", "isTemplate")
  590. .setParam("is_template", "true")
  591. .executeProtobuf(SearchResponse.class);
  592. assertThat(result.getPaging().getTotal()).isOne();
  593. assertThat(result.getPaging().getPageIndex()).isOne();
  594. assertThat(result.getRulesCount()).isOne();
  595. Rule searchedRule = result.getRules(0);
  596. assertThat(searchedRule).isNotNull();
  597. assertThat(searchedRule.getIsTemplate()).isTrue();
  598. assertThat(searchedRule.getKey()).isEqualTo(templateRule.getRepositoryKey() + ":" + templateRule.getRuleKey());
  599. }
  600. @Test
  601. public void search_custom_rules_from_template_key() {
  602. RuleDto templateRule = db.rules().insert(r -> r.setLanguage("java")
  603. .setIsTemplate(true));
  604. RuleDto rule = db.rules().insert(r -> r.setLanguage("java")
  605. .setTemplateUuid(templateRule.getUuid()));
  606. indexRules();
  607. SearchResponse result = ws.newRequest()
  608. .setParam("f", "templateKey")
  609. .setParam("template_key", templateRule.getRepositoryKey() + ":" + templateRule.getRuleKey())
  610. .executeProtobuf(SearchResponse.class);
  611. assertThat(result.getPaging().getTotal()).isOne();
  612. assertThat(result.getPaging().getPageIndex()).isOne();
  613. assertThat(result.getRulesCount()).isOne();
  614. Rule searchedRule = result.getRules(0);
  615. assertThat(searchedRule).isNotNull();
  616. assertThat(searchedRule.getKey()).isEqualTo(rule.getRepositoryKey() + ":" + rule.getRuleKey());
  617. assertThat(searchedRule.getTemplateKey()).isEqualTo(templateRule.getRepositoryKey() + ":" + templateRule.getRuleKey());
  618. }
  619. @Test
  620. public void do_not_return_external_rule() {
  621. db.rules().insert(r -> r.setIsExternal(true));
  622. indexRules();
  623. SearchResponse result = ws.newRequest().executeProtobuf(SearchResponse.class);
  624. assertThat(result.getPaging().getTotal()).isZero();
  625. assertThat(result.getPaging().getPageIndex()).isOne();
  626. assertThat(result.getRulesCount()).isZero();
  627. }
  628. @Test
  629. public void search_all_active_rules() {
  630. QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage("java"));
  631. RuleDto rule = db.rules().insert(r -> r.setLanguage("java").setNoteUserUuid(null));
  632. RuleActivation activation = RuleActivation.create(rule.getUuid(), BLOCKER, null);
  633. qProfileRules.activateAndCommit(db.getSession(), profile, singleton(activation));
  634. indexRules();
  635. SearchResponse result = ws.newRequest()
  636. .setParam("q", rule.getName())
  637. .setParam("activation", "true")
  638. .executeProtobuf(SearchResponse.class);
  639. assertThat(result.getPaging().getTotal()).isOne();
  640. assertThat(result.getPaging().getPageIndex()).isOne();
  641. assertThat(result.getRulesCount()).isOne();
  642. Rule searchedRule = result.getRules(0);
  643. assertThat(searchedRule).isNotNull();
  644. assertThat(searchedRule.getKey()).isEqualTo(rule.getRepositoryKey() + ":" + rule.getRuleKey());
  645. assertThat(searchedRule.getName()).isEqualTo(rule.getName());
  646. }
  647. @Test
  648. public void search_profile_active_rules() {
  649. QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage("java"));
  650. QProfileDto waterproofProfile = db.qualityProfiles().insert(p -> p.setLanguage("java"));
  651. RuleDto rule = db.rules().insert(r -> r.setLanguage("java"));
  652. RuleParamDto ruleParam1 = db.rules().insertRuleParam(rule, p -> p.setDefaultValue("some value")
  653. .setType("STRING")
  654. .setDescription("My small description")
  655. .setName("my_var"));
  656. RuleParamDto ruleParam2 = db.rules().insertRuleParam(rule, p -> p.setDefaultValue("1")
  657. .setType("INTEGER")
  658. .setDescription("My small description")
  659. .setName("the_var"));
  660. // SONAR-7083
  661. RuleParamDto ruleParam3 = db.rules().insertRuleParam(rule, p -> p.setDefaultValue(null)
  662. .setType("STRING")
  663. .setDescription("Empty Param")
  664. .setName("empty_var"));
  665. RuleActivation activation = RuleActivation.create(rule.getUuid());
  666. List<ActiveRuleChange> activeRuleChanges1 = qProfileRules.activateAndCommit(db.getSession(), profile, singleton(activation));
  667. qProfileRules.activateAndCommit(db.getSession(), waterproofProfile, singleton(activation));
  668. assertThat(activeRuleChanges1).hasSize(1);
  669. indexRules();
  670. indexActiveRules();
  671. SearchResponse result = ws.newRequest()
  672. .setParam("f", "actives")
  673. .setParam("q", rule.getName())
  674. .setParam("activation", "true")
  675. .setParam("qprofile", profile.getKee())
  676. .executeProtobuf(SearchResponse.class);
  677. assertThat(result.getPaging().getTotal()).isOne();
  678. assertThat(result.getPaging().getPageIndex()).isOne();
  679. assertThat(result.getRulesCount()).isOne();
  680. assertThat(result.getActives()).isNotNull();
  681. assertThat(result.getActives().getActives().get(rule.getKey().toString())).isNotNull();
  682. assertThat(result.getActives().getActives().get(rule.getKey().toString()).getActiveListList()).hasSize(1);
  683. // The rule without value is not inserted
  684. Rules.Active activeList = result.getActives().getActives().get(rule.getKey().toString()).getActiveList(0);
  685. assertThat(activeList.getParamsCount()).isEqualTo(2);
  686. assertThat(activeList.getParamsList()).extracting("key", "value").containsExactlyInAnyOrder(
  687. tuple(ruleParam1.getName(), ruleParam1.getDefaultValue()),
  688. tuple(ruleParam2.getName(), ruleParam2.getDefaultValue()));
  689. String unknownProfile = "unknown_profile" + randomAlphanumeric(5);
  690. assertThatThrownBy(() -> {
  691. ws.newRequest()
  692. .setParam("activation", "true")
  693. .setParam("qprofile", unknownProfile)
  694. .executeProtobuf(SearchResponse.class);
  695. })
  696. .isInstanceOf(NotFoundException.class)
  697. .hasMessage("The specified qualityProfile '" + unknownProfile + "' does not exist");
  698. }
  699. @Test
  700. public void search_for_active_rules_when_parameter_value_is_null() {
  701. QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage("java"));
  702. RuleDto rule = db.rules().insert(r -> r.setLanguage("java"));
  703. RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setDefaultValue("some value")
  704. .setType("STRING")
  705. .setDescription("My small description")
  706. .setName("my_var"));
  707. RuleActivation activation = RuleActivation.create(rule.getUuid());
  708. List<ActiveRuleChange> activeRuleChanges = qProfileRules.activateAndCommit(db.getSession(), profile, singleton(activation));
  709. // Insert directly in database a rule parameter with a null value
  710. ActiveRuleParamDto activeRuleParam = ActiveRuleParamDto.createFor(ruleParam).setValue(null);
  711. db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRuleChanges.get(0).getActiveRule(), activeRuleParam);
  712. db.commit();
  713. indexRules();
  714. indexActiveRules();
  715. SearchResponse result = ws.newRequest()
  716. .setParam("f", "actives")
  717. .setParam("q", rule.getName())
  718. .setParam("activation", "true")
  719. .setParam("qprofile", profile.getKee())
  720. .executeProtobuf(SearchResponse.class);
  721. assertThat(result.getPaging().getTotal()).isOne();
  722. assertThat(result.getPaging().getPageIndex()).isOne();
  723. assertThat(result.getRulesCount()).isOne();
  724. assertThat(result.getActives()).isNotNull();
  725. assertThat(result.getActives().getActives().get(rule.getKey().toString())).isNotNull();
  726. assertThat(result.getActives().getActives().get(rule.getKey().toString()).getActiveListList()).hasSize(1);
  727. Rules.Active activeList = result.getActives().getActives().get(rule.getKey().toString()).getActiveList(0);
  728. assertThat(activeList.getParamsCount()).isEqualTo(2);
  729. assertThat(activeList.getParamsList()).extracting("key", "value").containsExactlyInAnyOrder(
  730. tuple(ruleParam.getName(), ruleParam.getDefaultValue()),
  731. tuple(activeRuleParam.getKey(), ""));
  732. }
  733. /**
  734. * When the user searches for inactive rules (for example for to "activate more"), then
  735. * only rules of the quality profiles' language are relevant
  736. */
  737. @Test
  738. public void facet_filtering_when_searching_for_inactive_rules() {
  739. QProfileDto profile = db.qualityProfiles().insert(q -> q.setLanguage("language1"));
  740. // on same language, not activated => match
  741. RuleDto rule1 = db.rules().insert(r -> r
  742. .setLanguage(profile.getLanguage())
  743. .setRepositoryKey("repositoryKey1")
  744. .setSystemTags(new HashSet<>(singletonList("tag1")))
  745. .setTags(emptySet())
  746. .setSeverity("CRITICAL")
  747. .setNoteUserUuid(null)
  748. .setStatus(RuleStatus.BETA)
  749. .setType(RuleType.CODE_SMELL));
  750. // on same language, activated => no match
  751. RuleDto rule2 = db.rules().insert(r -> r
  752. .setLanguage(profile.getLanguage())
  753. .setRepositoryKey("repositoryKey2")
  754. .setSystemTags(new HashSet<>(singletonList("tag2")))
  755. .setTags(emptySet())
  756. .setSeverity("MAJOR")
  757. .setNoteUserUuid(null)
  758. .setStatus(RuleStatus.DEPRECATED)
  759. .setType(RuleType.VULNERABILITY));
  760. RuleActivation activation = RuleActivation.create(rule2.getUuid(), null, null);
  761. qProfileRules.activateAndCommit(db.getSession(), profile, singleton(activation));
  762. // on other language, not activated => no match
  763. RuleDto rule3 = db.rules().insert(r -> r
  764. .setLanguage("language3")
  765. .setRepositoryKey("repositoryKey3")
  766. .setSystemTags(new HashSet<>(singletonList("tag3")))
  767. .setTags(emptySet())
  768. .setNoteUserUuid(null)
  769. .setSeverity("BLOCKER")
  770. .setStatus(RuleStatus.READY)
  771. .setType(RuleType.BUG));
  772. indexRules();
  773. indexActiveRules();
  774. SearchResponse result = ws.newRequest()
  775. .setParam("facets", "languages,repositories,tags,severities,statuses,types")
  776. .setParam("activation", "false")
  777. .setParam("qprofile", profile.getKee())
  778. .executeProtobuf(SearchResponse.class);
  779. assertThat(result.getRulesList())
  780. .extracting(Rule::getKey)
  781. .containsExactlyInAnyOrder(
  782. rule1.getKey().toString());
  783. assertThat(result.getFacets().getFacetsList().stream().filter(f -> "languages".equals(f.getProperty())).findAny().get().getValuesList())
  784. .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
  785. .as("Facet languages")
  786. .containsExactlyInAnyOrder(
  787. tuple(rule1.getLanguage(), 1L),
  788. // known limitation: irrelevant languages are shown in this case (SONAR-9683)
  789. tuple(rule3.getLanguage(), 1L));
  790. assertThat(result.getFacets().getFacetsList().stream().filter(f -> "tags".equals(f.getProperty())).findAny().get().getValuesList())
  791. .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
  792. .as("Facet tags")
  793. .containsExactlyInAnyOrder(
  794. tuple(rule1.getSystemTags().iterator().next(), 1L));
  795. assertThat(result.getFacets().getFacetsList().stream().filter(f -> "repositories".equals(f.getProperty())).findAny().get().getValuesList())
  796. .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
  797. .as("Facet repositories")
  798. .containsExactlyInAnyOrder(
  799. tuple(rule1.getRepositoryKey(), 1L));
  800. assertThat(result.getFacets().getFacetsList().stream().filter(f -> "severities".equals(f.getProperty())).findAny().get().getValuesList())
  801. .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
  802. .as("Facet severities")
  803. .containsExactlyInAnyOrder(
  804. tuple("BLOCKER" /* rule2 */, 0L),
  805. tuple("CRITICAL"/* rule1 */, 1L),
  806. tuple("MAJOR", 0L),
  807. tuple("MINOR", 0L),
  808. tuple("INFO", 0L));
  809. assertThat(result.getFacets().getFacetsList().stream().filter(f -> "statuses".equals(f.getProperty())).findAny().get().getValuesList())
  810. .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
  811. .as("Facet statuses")
  812. .containsExactlyInAnyOrder(
  813. tuple("READY"/* rule2 */, 0L),
  814. tuple("BETA" /* rule1 */, 1L),
  815. tuple("DEPRECATED", 0L));
  816. assertThat(result.getFacets().getFacetsList().stream().filter(f -> "types".equals(f.getProperty())).findAny().get().getValuesList())
  817. .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
  818. .as("Facet types")
  819. .containsExactlyInAnyOrder(
  820. tuple("BUG" /* rule2 */, 0L),
  821. tuple("CODE_SMELL"/* rule1 */, 1L),
  822. tuple("VULNERABILITY", 0L),
  823. tuple("SECURITY_HOTSPOT", 0L));
  824. }
  825. @Test
  826. public void statuses_facet_should_be_sticky() {
  827. RuleDto rule1 = db.rules().insert(r -> r.setLanguage("java"));
  828. RuleDto rule2 = db.rules().insert(r -> r.setLanguage("java").setStatus(RuleStatus.BETA));
  829. RuleDto rule3 = db.rules().insert(r -> r.setLanguage("java").setStatus(RuleStatus.DEPRECATED));
  830. indexRules();
  831. SearchResponse result = ws.newRequest()
  832. .setParam("f", "status")
  833. .setParam("status", "DEPRECATED")
  834. .executeProtobuf(SearchResponse.class);
  835. assertThat(result.getRulesCount()).isEqualTo(3);
  836. assertThat(result.getRulesList()).extracting(Rule::getKey, r -> r.getStatus().name()).containsExactlyInAnyOrder(
  837. tuple(rule1.getKey().toString(), rule1.getStatus().name()),
  838. tuple(rule2.getKey().toString(), rule2.getStatus().name()),
  839. tuple(rule3.getKey().toString(), rule3.getStatus().name()));
  840. }
  841. @Test
  842. public void paging() {
  843. for (int i = 0; i < 12; i++) {
  844. db.rules().insert(r -> r.setLanguage("java"));
  845. }
  846. indexRules();
  847. ws.newRequest()
  848. .setParam(WebService.Param.PAGE, "2")
  849. .setParam(WebService.Param.PAGE_SIZE, "9")
  850. .execute()
  851. .assertJson(this.getClass(), "paging.json");
  852. }
  853. @Test
  854. public void compare_to_another_profile() {
  855. QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(JAVA));
  856. QProfileDto anotherProfile = db.qualityProfiles().insert(p -> p.setLanguage(JAVA));
  857. RuleDto commonRule = db.rules().insertRule(r -> r.setLanguage(JAVA));
  858. RuleDto profileRule1 = db.rules().insertRule(r -> r.setLanguage(JAVA));
  859. RuleDto profileRule2 = db.rules().insertRule(r -> r.setLanguage(JAVA));
  860. RuleDto profileRule3 = db.rules().insertRule(r -> r.setLanguage(JAVA));
  861. RuleDto anotherProfileRule1 = db.rules().insertRule(r -> r.setLanguage(JAVA));
  862. RuleDto anotherProfileRule2 = db.rules().insertRule(r -> r.setLanguage(JAVA));
  863. db.qualityProfiles().activateRule(profile, commonRule);
  864. db.qualityProfiles().activateRule(profile, profileRule1);
  865. db.qualityProfiles().activateRule(profile, profileRule2);
  866. db.qualityProfiles().activateRule(profile, profileRule3);
  867. db.qualityProfiles().activateRule(anotherProfile, commonRule);
  868. db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule1);
  869. db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule2);
  870. indexRules();
  871. indexActiveRules();
  872. SearchResponse result = ws.newRequest()
  873. .setParam(PARAM_QPROFILE, profile.getKee())
  874. .setParam(PARAM_ACTIVATION, "false")
  875. .setParam(PARAM_COMPARE_TO_PROFILE, anotherProfile.getKee())
  876. .executeProtobuf(SearchResponse.class);
  877. assertThat(result.getRulesList())
  878. .extracting(Rule::getKey)
  879. .containsExactlyInAnyOrder(anotherProfileRule1.getKey().toString(), anotherProfileRule2.getKey().toString());
  880. }
  881. @SafeVarargs
  882. private <T> void checkField(RuleDto rule, String fieldName, Function<Rule, T> responseExtractor, T... expected) {
  883. SearchResponse result = ws.newRequest()
  884. .setParam("f", fieldName)
  885. .executeProtobuf(SearchResponse.class);
  886. assertThat(result.getRulesList()).extracting(Rule::getKey).containsExactly(rule.getKey().toString());
  887. assertThat(result.getRulesList()).extracting(responseExtractor).containsExactly(expected);
  888. }
  889. private void checkDescriptionSections(RuleDto rule, Set<Rule.DescriptionSection> expected) {
  890. SearchResponse result = ws.newRequest()
  891. .setParam("f", "descriptionSections")
  892. .executeProtobuf(SearchResponse.class);
  893. assertThat(result.getRulesList()).extracting(Rule::getKey).containsExactly(rule.getKey().toString());
  894. List<Rule.DescriptionSection> actualSections = result.getRules(0).getDescriptionSections().getDescriptionSectionsList();
  895. assertThat(actualSections).hasSameElementsAs(expected);
  896. }
  897. private void verifyNoResults(Consumer<TestRequest> requestPopulator) {
  898. verify(requestPopulator);
  899. }
  900. private void verify(Consumer<TestRequest> requestPopulator, RuleDto... expectedRules) {
  901. TestRequest request = ws.newRequest();
  902. requestPopulator.accept(request);
  903. Rules.SearchResponse response = request.executeProtobuf(Rules.SearchResponse.class);
  904. assertThat(response.getPaging().getPageIndex()).isOne();
  905. assertThat(response.getPaging().getPageSize()).isNotZero();
  906. RuleKey[] expectedRuleKeys = stream(expectedRules).map(RuleDto::getKey).toList().toArray(new RuleKey[0]);
  907. assertThat(response.getRulesList())
  908. .extracting(r -> RuleKey.parse(r.getKey()))
  909. .containsExactlyInAnyOrder(expectedRuleKeys);
  910. assertThat(response.getPaging().getTotal()).isEqualTo(expectedRules.length);
  911. assertThat(response.getRulesCount()).isEqualTo(expectedRules.length);
  912. }
  913. private void indexRules() {
  914. ruleIndexer.indexAll();
  915. }
  916. private void indexActiveRules() {
  917. activeRuleIndexer.indexAll();
  918. }
  919. private String[] get101Tags() {
  920. String[] tags = new String[101];
  921. for (int i = 0; i < 100; i++) {
  922. tags[i] = "tag" + i;
  923. }
  924. tags[100] = "tagA";
  925. return tags;
  926. }
  927. }