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.

UpdateActionTest.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.rule.ws;
  21. import org.junit.Rule;
  22. import org.junit.Test;
  23. import org.junit.rules.ExpectedException;
  24. import org.sonar.api.resources.Languages;
  25. import org.sonar.api.rule.RuleKey;
  26. import org.sonar.api.rule.RuleStatus;
  27. import org.sonar.api.rule.Severity;
  28. import org.sonar.api.utils.System2;
  29. import org.sonar.db.DbClient;
  30. import org.sonar.db.DbTester;
  31. import org.sonar.db.organization.OrganizationDto;
  32. import org.sonar.db.rule.RuleDefinitionDto;
  33. import org.sonar.db.rule.RuleMetadataDto;
  34. import org.sonar.db.user.UserDto;
  35. import org.sonar.server.es.EsClient;
  36. import org.sonar.server.es.EsTester;
  37. import org.sonar.server.exceptions.ForbiddenException;
  38. import org.sonar.server.exceptions.NotFoundException;
  39. import org.sonar.server.exceptions.UnauthorizedException;
  40. import org.sonar.server.organization.DefaultOrganizationProvider;
  41. import org.sonar.server.organization.TestDefaultOrganizationProvider;
  42. import org.sonar.server.rule.RuleUpdater;
  43. import org.sonar.server.rule.index.RuleIndexer;
  44. import org.sonar.server.tester.UserSessionRule;
  45. import org.sonar.server.text.MacroInterpreter;
  46. import org.sonar.server.ws.TestResponse;
  47. import org.sonar.server.ws.WsAction;
  48. import org.sonar.server.ws.WsActionTester;
  49. import org.sonarqube.ws.Rules;
  50. import static org.assertj.core.api.Assertions.assertThat;
  51. import static org.mockito.AdditionalAnswers.returnsFirstArg;
  52. import static org.mockito.ArgumentMatchers.anyString;
  53. import static org.mockito.Mockito.doAnswer;
  54. import static org.mockito.Mockito.mock;
  55. import static org.sonar.api.server.debt.DebtRemediationFunction.Type.LINEAR;
  56. import static org.sonar.api.server.debt.DebtRemediationFunction.Type.LINEAR_OFFSET;
  57. import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
  58. import static org.sonar.db.rule.RuleTesting.setSystemTags;
  59. import static org.sonar.db.rule.RuleTesting.setTags;
  60. import static org.sonar.server.rule.ws.UpdateAction.PARAM_KEY;
  61. import static org.sonar.server.rule.ws.UpdateAction.PARAM_MARKDOWN_NOTE;
  62. import static org.sonar.server.rule.ws.UpdateAction.PARAM_ORGANIZATION;
  63. import static org.sonar.server.rule.ws.UpdateAction.PARAM_REMEDIATION_FN_BASE_EFFORT;
  64. import static org.sonar.server.rule.ws.UpdateAction.PARAM_REMEDIATION_FN_GAP_MULTIPLIER;
  65. import static org.sonar.server.rule.ws.UpdateAction.PARAM_REMEDIATION_FN_TYPE;
  66. import static org.sonar.server.rule.ws.UpdateAction.PARAM_TAGS;
  67. import static org.sonar.test.JsonAssert.assertJson;
  68. public class UpdateActionTest {
  69. private static final long PAST = 10000L;
  70. @Rule
  71. public ExpectedException expectedException = ExpectedException.none();
  72. @Rule
  73. public DbTester db = DbTester.create();
  74. @Rule
  75. public EsTester es = EsTester.create();
  76. @Rule
  77. public UserSessionRule userSession = UserSessionRule.standalone();
  78. private DbClient dbClient = db.getDbClient();
  79. private EsClient esClient = es.client();
  80. private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
  81. private Languages languages = new Languages();
  82. private RuleMapper mapper = new RuleMapper(languages, createMacroInterpreter());
  83. private RuleIndexer ruleIndexer = new RuleIndexer(esClient, dbClient);
  84. private RuleUpdater ruleUpdater = new RuleUpdater(dbClient, ruleIndexer, System2.INSTANCE);
  85. private WsAction underTest = new UpdateAction(dbClient, ruleUpdater, mapper, userSession, new RuleWsSupport(db.getDbClient(), userSession, defaultOrganizationProvider));
  86. private WsActionTester ws = new WsActionTester(underTest);
  87. @Test
  88. public void check_definition() {
  89. assertThat(ws.getDef().isPost()).isTrue();
  90. assertThat(ws.getDef().isInternal()).isFalse();
  91. assertThat(ws.getDef().responseExampleAsString()).isNotNull();
  92. assertThat(ws.getDef().description()).isNotNull();
  93. }
  94. @Test
  95. public void update_custom_rule() {
  96. logInAsQProfileAdministrator();
  97. RuleDefinitionDto templateRule = db.rules().insert(
  98. r -> r.setRuleKey(RuleKey.of("java", "S001")),
  99. r -> r.setIsTemplate(true),
  100. r -> r.setCreatedAt(PAST),
  101. r -> r.setUpdatedAt(PAST));
  102. db.rules().insertRuleParam(templateRule, param -> param.setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(".*"));
  103. RuleDefinitionDto customRule = db.rules().insert(
  104. r -> r.setRuleKey(RuleKey.of("java", "MY_CUSTOM")),
  105. r -> r.setName("Old custom"),
  106. r -> r.setDescription("Old description"),
  107. r -> r.setSeverity(Severity.MINOR),
  108. r -> r.setStatus(RuleStatus.BETA),
  109. r -> r.setTemplateId(templateRule.getId()),
  110. r -> r.setLanguage("js"),
  111. r -> r.setCreatedAt(PAST),
  112. r -> r.setUpdatedAt(PAST));
  113. db.rules().insertRuleParam(customRule, param -> param.setName("regex").setType("a").setDescription("Reg ex"));
  114. TestResponse request = ws.newRequest().setMethod("POST")
  115. .setParam("key", customRule.getKey().toString())
  116. .setParam("name", "My custom rule")
  117. .setParam("markdown_description", "Description")
  118. .setParam("severity", "MAJOR")
  119. .setParam("status", "BETA")
  120. .setParam("params", "regex=a.*")
  121. .execute();
  122. assertJson(request.getInput()).isSimilarTo("{\n" +
  123. " \"rule\": {\n" +
  124. " \"key\": \"java:MY_CUSTOM\",\n" +
  125. " \"repo\": \"java\",\n" +
  126. " \"name\": \"My custom rule\",\n" +
  127. " \"htmlDesc\": \"Description\",\n" +
  128. " \"severity\": \"MAJOR\",\n" +
  129. " \"status\": \"BETA\",\n" +
  130. " \"isTemplate\": false,\n" +
  131. " \"templateKey\": \"java:S001\",\n" +
  132. " \"params\": [\n" +
  133. " {\n" +
  134. " \"key\": \"regex\",\n" +
  135. " \"htmlDesc\": \"Reg ex\",\n" +
  136. " \"defaultValue\": \"a.*\"\n" +
  137. " }\n" +
  138. " ]\n" +
  139. " }\n" +
  140. "}\n");
  141. }
  142. @Test
  143. public void update_tags_for_default_organization() {
  144. logInAsQProfileAdministrator();
  145. RuleDefinitionDto rule = db.rules().insert(setSystemTags("stag1", "stag2"));
  146. db.rules().insertOrUpdateMetadata(rule, db.getDefaultOrganization(), setTags("tag1", "tag2"), m -> m.setNoteData(null).setNoteUserUuid(null));
  147. Rules.UpdateResponse result = ws.newRequest().setMethod("POST")
  148. .setParam(PARAM_KEY, rule.getKey().toString())
  149. .setParam(PARAM_TAGS, "tag2,tag3")
  150. .executeProtobuf(Rules.UpdateResponse.class);
  151. Rules.Rule updatedRule = result.getRule();
  152. assertThat(updatedRule).isNotNull();
  153. assertThat(updatedRule.getKey()).isEqualTo(rule.getKey().toString());
  154. assertThat(updatedRule.getSysTags().getSysTagsList()).containsExactly(rule.getSystemTags().toArray(new String[0]));
  155. assertThat(updatedRule.getTags().getTagsList()).containsExactly("tag2", "tag3");
  156. }
  157. @Test
  158. public void update_tags_for_specific_organization() {
  159. OrganizationDto organization = db.organizations().insert();
  160. logInAsQProfileAdministrator(organization.getUuid());
  161. RuleDefinitionDto rule = db.rules().insert(setSystemTags("stag1", "stag2"));
  162. db.rules().insertOrUpdateMetadata(rule, organization, setTags("tagAlt1", "tagAlt2"), m -> m.setNoteData(null).setNoteUserUuid(null));
  163. Rules.UpdateResponse result = ws.newRequest().setMethod("POST")
  164. .setParam(PARAM_KEY, rule.getKey().toString())
  165. .setParam(PARAM_TAGS, "tag2,tag3")
  166. .setParam(PARAM_ORGANIZATION, organization.getKey())
  167. .executeProtobuf(Rules.UpdateResponse.class);
  168. Rules.Rule updatedRule = result.getRule();
  169. assertThat(updatedRule).isNotNull();
  170. // check response
  171. assertThat(updatedRule.getKey()).isEqualTo(rule.getKey().toString());
  172. assertThat(updatedRule.getSysTags().getSysTagsList()).containsExactly(rule.getSystemTags().toArray(new String[0]));
  173. assertThat(updatedRule.getTags().getTagsList()).containsExactly("tag2", "tag3");
  174. // check database
  175. RuleMetadataDto metadataOfSpecificOrg = db.getDbClient().ruleDao().selectMetadataByKey(db.getSession(), rule.getKey(), organization)
  176. .orElseThrow(() -> new IllegalStateException("Cannot load metadata"));
  177. assertThat(metadataOfSpecificOrg.getTags()).containsExactly("tag2", "tag3");
  178. }
  179. @Test
  180. public void update_rule_remediation_function() {
  181. OrganizationDto organization = db.organizations().insert();
  182. logInAsQProfileAdministrator(organization.getUuid());
  183. RuleDefinitionDto rule = db.rules().insert(
  184. r -> r.setDefRemediationFunction(LINEAR.toString()),
  185. r -> r.setDefRemediationGapMultiplier("10d"),
  186. r -> r.setDefRemediationBaseEffort(null));
  187. String newOffset = LINEAR_OFFSET.toString();
  188. String newMultiplier = "15d";
  189. String newEffort = "5min";
  190. Rules.UpdateResponse result = ws.newRequest().setMethod("POST")
  191. .setParam("key", rule.getKey().toString())
  192. .setParam(PARAM_ORGANIZATION, organization.getKey())
  193. .setParam(PARAM_REMEDIATION_FN_TYPE, newOffset)
  194. .setParam(PARAM_REMEDIATION_FN_GAP_MULTIPLIER, newMultiplier)
  195. .setParam(PARAM_REMEDIATION_FN_BASE_EFFORT, newEffort)
  196. .executeProtobuf(Rules.UpdateResponse.class);
  197. Rules.Rule updatedRule = result.getRule();
  198. assertThat(updatedRule).isNotNull();
  199. assertThat(updatedRule.getKey()).isEqualTo(rule.getKey().toString());
  200. assertThat(updatedRule.getDefaultRemFnType()).isEqualTo(rule.getDefRemediationFunction());
  201. assertThat(updatedRule.getDefaultRemFnGapMultiplier()).isEqualTo(rule.getDefRemediationGapMultiplier());
  202. assertThat(updatedRule.getDefaultRemFnBaseEffort()).isEqualTo("");
  203. assertThat(updatedRule.getGapDescription()).isEqualTo(rule.getGapDescription());
  204. assertThat(updatedRule.getRemFnType()).isEqualTo(newOffset);
  205. assertThat(updatedRule.getRemFnGapMultiplier()).isEqualTo(newMultiplier);
  206. assertThat(updatedRule.getRemFnBaseEffort()).isEqualTo(newEffort);
  207. // check database
  208. RuleMetadataDto metadataOfSpecificOrg = db.getDbClient().ruleDao().selectMetadataByKey(db.getSession(), rule.getKey(), organization)
  209. .orElseThrow(() -> new IllegalStateException("Cannot load metadata"));
  210. assertThat(metadataOfSpecificOrg.getRemediationFunction()).isEqualTo(newOffset);
  211. assertThat(metadataOfSpecificOrg.getRemediationGapMultiplier()).isEqualTo(newMultiplier);
  212. assertThat(metadataOfSpecificOrg.getRemediationBaseEffort()).isEqualTo(newEffort);
  213. }
  214. @Test
  215. public void update_note() {
  216. OrganizationDto organization = db.organizations().insert();
  217. RuleDefinitionDto rule = db.rules().insert();
  218. UserDto userHavingUpdatingNote = db.users().insertUser();
  219. db.rules().insertOrUpdateMetadata(rule, userHavingUpdatingNote, organization, m -> m.setNoteData("old data"));
  220. UserDto userAuthenticated = db.users().insertUser();
  221. userSession.logIn(userAuthenticated).addPermission(ADMINISTER_QUALITY_PROFILES, organization);
  222. Rules.UpdateResponse result = ws.newRequest().setMethod("POST")
  223. .setParam(PARAM_KEY, rule.getKey().toString())
  224. .setParam(PARAM_MARKDOWN_NOTE, "new data")
  225. .setParam(PARAM_ORGANIZATION, organization.getKey())
  226. .executeProtobuf(Rules.UpdateResponse.class);
  227. Rules.Rule updatedRule = result.getRule();
  228. // check response
  229. assertThat(updatedRule.getMdNote()).isEqualTo("new data");
  230. assertThat(updatedRule.getNoteLogin()).isEqualTo(userAuthenticated.getLogin());
  231. // check database
  232. RuleMetadataDto metadataOfSpecificOrg = db.getDbClient().ruleDao().selectMetadataByKey(db.getSession(), rule.getKey(), organization).get();
  233. assertThat(metadataOfSpecificOrg.getNoteData()).isEqualTo("new data");
  234. assertThat(metadataOfSpecificOrg.getNoteUserUuid()).isEqualTo(userAuthenticated.getUuid());
  235. }
  236. @Test
  237. public void fail_to_update_custom_when_description_is_empty() {
  238. logInAsQProfileAdministrator();
  239. RuleDefinitionDto templateRule = db.rules().insert(
  240. r -> r.setRuleKey(RuleKey.of("java", "S001")),
  241. r -> r.setIsTemplate(true),
  242. r -> r.setCreatedAt(PAST),
  243. r -> r.setUpdatedAt(PAST));
  244. RuleDefinitionDto customRule = db.rules().insert(
  245. r -> r.setRuleKey(RuleKey.of("java", "MY_CUSTOM")),
  246. r -> r.setName("Old custom"),
  247. r -> r.setDescription("Old description"),
  248. r -> r.setTemplateId(templateRule.getId()),
  249. r -> r.setCreatedAt(PAST),
  250. r -> r.setUpdatedAt(PAST));
  251. expectedException.expect(IllegalArgumentException.class);
  252. expectedException.expectMessage("The description is missing");
  253. ws.newRequest().setMethod("POST")
  254. .setParam("key", customRule.getKey().toString())
  255. .setParam("name", "My custom rule")
  256. .setParam("markdown_description", "")
  257. .execute();
  258. }
  259. @Test
  260. public void throw_IllegalArgumentException_if_trying_to_update_builtin_rule_description() {
  261. logInAsQProfileAdministrator();
  262. RuleDefinitionDto rule = db.rules().insert();
  263. expectedException.expect(IllegalArgumentException.class);
  264. expectedException.expectMessage("Not a custom rule");
  265. ws.newRequest().setMethod("POST")
  266. .setParam("key", rule.getKey().toString())
  267. .setParam("name", rule.getName())
  268. .setParam("markdown_description", "New description")
  269. .execute();
  270. }
  271. @Test
  272. public void throw_ForbiddenException_if_not_profile_administrator() {
  273. userSession.logIn();
  274. expectedException.expect(ForbiddenException.class);
  275. ws.newRequest().setMethod("POST").execute();
  276. }
  277. @Test
  278. public void throw_UnauthorizedException_if_not_logged_in() {
  279. expectedException.expect(UnauthorizedException.class);
  280. ws.newRequest().setMethod("POST").execute();
  281. }
  282. @Test
  283. public void throw_NotFoundException_if_organization_cannot_be_found() {
  284. logInAsQProfileAdministrator();
  285. RuleDefinitionDto rule = db.rules().insert();
  286. expectedException.expect(NotFoundException.class);
  287. ws.newRequest().setMethod("POST")
  288. .setParam("key", rule.getKey().toString())
  289. .setParam("organization", "foo")
  290. .execute();
  291. }
  292. private void logInAsQProfileAdministrator() {
  293. logInAsQProfileAdministrator(db.getDefaultOrganization().getUuid());
  294. }
  295. private void logInAsQProfileAdministrator(String orgUuid) {
  296. userSession
  297. .logIn()
  298. .addPermission(ADMINISTER_QUALITY_PROFILES, orgUuid);
  299. }
  300. private static MacroInterpreter createMacroInterpreter() {
  301. MacroInterpreter macroInterpreter = mock(MacroInterpreter.class);
  302. doAnswer(returnsFirstArg()).when(macroInterpreter).interpret(anyString());
  303. return macroInterpreter;
  304. }
  305. }