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.

BulkUpdateKeyActionTest.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 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.project.ws;
  21. import javax.annotation.Nullable;
  22. import org.junit.Rule;
  23. import org.junit.Test;
  24. import org.junit.rules.ExpectedException;
  25. import org.sonar.api.server.ws.WebService;
  26. import org.sonar.api.utils.System2;
  27. import org.sonar.api.web.UserRole;
  28. import org.sonar.db.DbClient;
  29. import org.sonar.db.DbSession;
  30. import org.sonar.db.DbTester;
  31. import org.sonar.db.component.ComponentDbTester;
  32. import org.sonar.db.component.ComponentDto;
  33. import org.sonar.server.component.ComponentFinder;
  34. import org.sonar.server.component.ComponentService;
  35. import org.sonar.server.component.TestComponentFinder;
  36. import org.sonar.server.es.EsTester;
  37. import org.sonar.server.exceptions.BadRequestException;
  38. import org.sonar.server.exceptions.ForbiddenException;
  39. import org.sonar.server.exceptions.NotFoundException;
  40. import org.sonar.server.tester.UserSessionRule;
  41. import org.sonar.server.ws.TestRequest;
  42. import org.sonar.server.ws.WsActionTester;
  43. import org.sonarqube.ws.Projects.BulkUpdateKeyWsResponse;
  44. import org.sonarqube.ws.Projects.BulkUpdateKeyWsResponse.Key;
  45. import static org.assertj.core.api.Assertions.assertThat;
  46. import static org.assertj.core.api.Assertions.tuple;
  47. import static org.mockito.ArgumentMatchers.any;
  48. import static org.mockito.ArgumentMatchers.eq;
  49. import static org.mockito.Mockito.mock;
  50. import static org.mockito.Mockito.verify;
  51. import static org.sonar.db.component.ComponentTesting.newFileDto;
  52. import static org.sonar.db.component.ComponentTesting.newModuleDto;
  53. import static org.sonar.test.JsonAssert.assertJson;
  54. import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_DRY_RUN;
  55. import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_FROM;
  56. import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;
  57. import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_TO;
  58. public class BulkUpdateKeyActionTest {
  59. private static final String MY_PROJECT_KEY = "my_project";
  60. private static final String FROM = "my_";
  61. private static final String TO = "your_";
  62. private System2 system2 = System2.INSTANCE;
  63. @Rule
  64. public ExpectedException expectedException = ExpectedException.none();
  65. @Rule
  66. public UserSessionRule userSession = UserSessionRule.standalone().logIn().setRoot();
  67. @Rule
  68. public EsTester es = EsTester.create();
  69. @Rule
  70. public DbTester db = DbTester.create(system2);
  71. private ComponentDbTester componentDb = new ComponentDbTester(db);
  72. private DbClient dbClient = db.getDbClient();
  73. private ComponentFinder componentFinder = TestComponentFinder.from(db);
  74. private ComponentService componentService = mock(ComponentService.class);
  75. private WsActionTester ws = new WsActionTester(
  76. new BulkUpdateKeyAction(dbClient, componentFinder, componentService, userSession));
  77. @Test
  78. public void json_example() {
  79. ComponentDto project = componentDb.insertPrivateProject(c -> c.setDbKey("my_project"));
  80. componentDb.insertComponent(newModuleDto(project).setDbKey("my_project:module_1"));
  81. ComponentDto anotherProject = componentDb.insertPrivateProject(c -> c.setDbKey("another_project"));
  82. componentDb.insertComponent(newModuleDto(anotherProject).setDbKey("my_new_project:module_1"));
  83. ComponentDto module2 = componentDb.insertComponent(newModuleDto(project).setDbKey("my_project:module_2"));
  84. componentDb.insertComponent(newFileDto(module2, null));
  85. String result = ws.newRequest()
  86. .setParam(PARAM_PROJECT, "my_project")
  87. .setParam(PARAM_FROM, "my_")
  88. .setParam(PARAM_TO, "my_new_")
  89. .setParam(PARAM_DRY_RUN, String.valueOf(true))
  90. .execute().getInput();
  91. assertJson(result).withStrictArrayOrder().isSimilarTo(getClass().getResource("bulk_update_key-example.json"));
  92. }
  93. @Test
  94. public void dry_run_by_key() {
  95. insertMyProject();
  96. BulkUpdateKeyWsResponse result = callDryRunByKey(MY_PROJECT_KEY, FROM, TO);
  97. assertThat(result.getKeysCount()).isEqualTo(1);
  98. assertThat(result.getKeys(0).getNewKey()).isEqualTo("your_project");
  99. }
  100. @Test
  101. public void bulk_update_project_key() {
  102. ComponentDto project = insertMyProject();
  103. ComponentDto module = componentDb.insertComponent(newModuleDto(project).setDbKey("my_project:root:module"));
  104. ComponentDto inactiveModule = componentDb.insertComponent(newModuleDto(project).setDbKey("my_project:root:inactive_module").setEnabled(false));
  105. ComponentDto file = componentDb.insertComponent(newFileDto(module, null).setDbKey("my_project:root:module:src/File.xoo"));
  106. ComponentDto inactiveFile = componentDb.insertComponent(newFileDto(module, null).setDbKey("my_project:root:module:src/InactiveFile.xoo").setEnabled(false));
  107. BulkUpdateKeyWsResponse result = callByKey(project.getDbKey(), FROM, TO);
  108. assertThat(result.getKeysCount()).isEqualTo(2);
  109. assertThat(result.getKeysList()).extracting(Key::getKey, Key::getNewKey, Key::getDuplicate)
  110. .containsExactly(
  111. tuple(project.getDbKey(), "your_project", false),
  112. tuple(module.getDbKey(), "your_project:root:module", false));
  113. verify(componentService).bulkUpdateKey(any(DbSession.class), eq(componentDb.getProjectDto(project)), eq(FROM), eq(TO));
  114. }
  115. @Test
  116. public void bulk_update_provisioned_project_key() {
  117. String newKey = "provisionedProject2";
  118. ComponentDto provisionedProject = componentDb.insertPrivateProject();
  119. callByKey(provisionedProject.getDbKey(), provisionedProject.getDbKey(), newKey);
  120. verify(componentService).bulkUpdateKey(any(DbSession.class), eq(componentDb.getProjectDto(provisionedProject)), eq(provisionedProject.getDbKey()), eq(newKey));
  121. }
  122. @Test
  123. public void fail_to_bulk_update_key_using_branch_db_key() {
  124. ComponentDto project = db.components().insertPrivateProject();
  125. ComponentDto branch = db.components().insertProjectBranch(project);
  126. userSession.addProjectPermission(UserRole.USER, project);
  127. expectedException.expect(NotFoundException.class);
  128. expectedException.expectMessage(String.format("Project '%s' not found", branch.getDbKey()));
  129. callByKey(branch.getDbKey(), FROM, TO);
  130. }
  131. @Test
  132. public void fail_to_bulk_if_a_component_already_exists_with_the_same_key() {
  133. componentDb.insertPrivateProject(c -> c.setDbKey("my_project"));
  134. componentDb.insertPrivateProject(c -> c.setDbKey("your_project"));
  135. expectedException.expect(BadRequestException.class);
  136. expectedException.expectMessage("Impossible to update key: a component with key \"your_project\" already exists.");
  137. callByKey("my_project", "my_", "your_");
  138. }
  139. @Test
  140. public void fail_to_bulk_update_with_invalid_new_key() {
  141. insertMyProject();
  142. expectedException.expect(IllegalArgumentException.class);
  143. expectedException.expectMessage("Malformed key for 'my?project'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
  144. callByKey(MY_PROJECT_KEY, FROM, "my?");
  145. }
  146. @Test
  147. public void fail_to_dry_bulk_update_with_invalid_new_key() {
  148. insertMyProject();
  149. expectedException.expect(IllegalArgumentException.class);
  150. expectedException.expectMessage("Malformed key for 'my?project'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
  151. callDryRunByKey(MY_PROJECT_KEY, FROM, "my?");
  152. }
  153. @Test
  154. public void fail_to_bulk_update_if_not_project_or_module() {
  155. ComponentDto project = insertMyProject();
  156. ComponentDto file = componentDb.insertComponent(newFileDto(project, null));
  157. expectedException.expect(NotFoundException.class);
  158. expectedException.expectMessage(String.format("Project '%s' not found", file.getDbKey()));
  159. callByKey(file.getDbKey(), FROM, TO);
  160. }
  161. @Test
  162. public void fail_if_from_string_is_not_provided() {
  163. expectedException.expect(IllegalArgumentException.class);
  164. ComponentDto project = insertMyProject();
  165. callDryRunByKey(project.getDbKey(), null, TO);
  166. }
  167. @Test
  168. public void fail_if_to_string_is_not_provided() {
  169. expectedException.expect(IllegalArgumentException.class);
  170. ComponentDto project = insertMyProject();
  171. callDryRunByKey(project.getDbKey(), FROM, null);
  172. }
  173. @Test
  174. public void fail_if_key_not_provided() {
  175. expectedException.expect(IllegalArgumentException.class);
  176. call(null, FROM, TO, false);
  177. }
  178. @Test
  179. public void fail_if_project_does_not_exist() {
  180. expectedException.expect(NotFoundException.class);
  181. callDryRunByKey("UNKNOWN_KEY", FROM, TO);
  182. }
  183. @Test
  184. public void throw_ForbiddenException_if_not_project_administrator() {
  185. userSession.logIn();
  186. ComponentDto project = insertMyProject();
  187. expectedException.expect(ForbiddenException.class);
  188. callDryRunByKey(project.getDbKey(), FROM, TO);
  189. }
  190. @Test
  191. public void fail_when_using_branch_db_key() {
  192. ComponentDto project = db.components().insertPrivateProject();
  193. userSession.logIn().addProjectPermission(UserRole.USER, project);
  194. ComponentDto branch = db.components().insertProjectBranch(project);
  195. expectedException.expect(NotFoundException.class);
  196. expectedException.expectMessage(String.format("Project '%s' not found", branch.getDbKey()));
  197. callByKey(branch.getDbKey(), FROM, TO);
  198. }
  199. @Test
  200. public void api_definition() {
  201. WebService.Action definition = ws.getDef();
  202. assertThat(definition.isPost()).isTrue();
  203. assertThat(definition.since()).isEqualTo("6.1");
  204. assertThat(definition.key()).isEqualTo("bulk_update_key");
  205. assertThat(definition.params())
  206. .hasSize(4)
  207. .extracting(WebService.Param::key)
  208. .containsOnlyOnce("project", "from", "to", "dryRun");
  209. }
  210. private ComponentDto insertMyProject() {
  211. return componentDb.insertPublicProject(c -> c.setDbKey(MY_PROJECT_KEY));
  212. }
  213. private BulkUpdateKeyWsResponse callDryRunByKey(@Nullable String key, @Nullable String from, @Nullable String to) {
  214. return call(key, from, to, true);
  215. }
  216. private BulkUpdateKeyWsResponse callByKey(@Nullable String key, @Nullable String from, @Nullable String to) {
  217. return call(key, from, to, false);
  218. }
  219. private BulkUpdateKeyWsResponse call(@Nullable String key, @Nullable String from, @Nullable String to, @Nullable Boolean dryRun) {
  220. TestRequest request = ws.newRequest();
  221. if (key != null) {
  222. request.setParam(PARAM_PROJECT, key);
  223. }
  224. if (from != null) {
  225. request.setParam(PARAM_FROM, from);
  226. }
  227. if (to != null) {
  228. request.setParam(PARAM_TO, to);
  229. }
  230. if (dryRun != null) {
  231. request.setParam(PARAM_DRY_RUN, String.valueOf(dryRun));
  232. }
  233. return request.executeProtobuf(BulkUpdateKeyWsResponse.class);
  234. }
  235. }