您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

SearchActionIT.java 49KB

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