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.

ComponentKeyUpdaterDaoTest.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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.db.component;
  21. import com.google.common.base.Strings;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.function.Predicate;
  25. import org.assertj.core.groups.Tuple;
  26. import org.junit.Rule;
  27. import org.junit.Test;
  28. import org.junit.rules.ExpectedException;
  29. import org.sonar.api.resources.Qualifiers;
  30. import org.sonar.api.utils.System2;
  31. import org.sonar.db.DbClient;
  32. import org.sonar.db.DbSession;
  33. import org.sonar.db.DbTester;
  34. import org.sonar.db.audit.AuditPersister;
  35. import org.sonar.db.audit.model.ComponentKeyNewValue;
  36. import org.sonar.db.component.ComponentKeyUpdaterDao.RekeyedResource;
  37. import static org.assertj.core.api.Assertions.assertThat;
  38. import static org.mockito.ArgumentMatchers.any;
  39. import static org.mockito.ArgumentMatchers.anyString;
  40. import static org.mockito.Mockito.mock;
  41. import static org.mockito.Mockito.times;
  42. import static org.mockito.Mockito.verify;
  43. import static org.sonar.db.component.BranchType.PULL_REQUEST;
  44. import static org.sonar.db.component.ComponentDto.BRANCH_KEY_SEPARATOR;
  45. import static org.sonar.db.component.ComponentDto.generateBranchKey;
  46. import static org.sonar.db.component.ComponentKeyUpdaterDao.computeNewKey;
  47. import static org.sonar.db.component.ComponentTesting.newDirectory;
  48. import static org.sonar.db.component.ComponentTesting.newFileDto;
  49. import static org.sonar.db.component.ComponentTesting.newModuleDto;
  50. import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
  51. public class ComponentKeyUpdaterDaoTest {
  52. @Rule
  53. public ExpectedException thrown = ExpectedException.none();
  54. @Rule
  55. public DbTester db = DbTester.create(System2.INSTANCE);
  56. private AuditPersister auditPersister = mock(AuditPersister.class);
  57. private DbClient dbClient = db.getDbClient();
  58. private DbSession dbSession = db.getSession();
  59. private ComponentKeyUpdaterDao underTest = db.getDbClient().componentKeyUpdaterDao();
  60. private ComponentKeyUpdaterDao underTestWithAuditPersister = new ComponentKeyUpdaterDao(auditPersister);
  61. @Test
  62. public void updateKey_changes_the_key_of_tree_of_components() {
  63. populateSomeData();
  64. underTest.updateKey(dbSession, "B", "struts:core");
  65. dbSession.commit();
  66. assertThat(db.select("select uuid as \"UUID\", kee as \"KEE\" from components"))
  67. .extracting(t -> t.get("UUID"), t -> t.get("KEE"))
  68. .containsOnly(
  69. Tuple.tuple("A", "org.struts:struts"),
  70. Tuple.tuple("B", "struts:core"),
  71. Tuple.tuple("C", "struts:core:/src/org/struts"),
  72. Tuple.tuple("D", "struts:core:/src/org/struts/RequestContext.java"),
  73. Tuple.tuple("E", "org.struts:struts-ui"),
  74. Tuple.tuple("F", "org.struts:struts-ui:/src/org/struts"),
  75. Tuple.tuple("G", "org.struts:struts-ui:/src/org/struts/RequestContext.java"),
  76. Tuple.tuple("H", "foo:struts-core"));
  77. }
  78. @Test
  79. public void updateKey_updates_disabled_components() {
  80. ComponentDto project = db.components().insertComponent(
  81. newPrivateProjectDto("A")
  82. .setDbKey("my_project"));
  83. ComponentDto directory = db.components().insertComponent(
  84. newDirectory(project, "B")
  85. .setDbKey("my_project:directory"));
  86. db.components().insertComponent(newFileDto(project, directory).setDbKey("my_project:directory/file"));
  87. ComponentDto inactiveDirectory = db.components().insertComponent(newDirectory(project, "/inactive_directory").setDbKey("my_project:inactive_directory").setEnabled(false));
  88. db.components().insertComponent(newFileDto(project, inactiveDirectory).setDbKey("my_project:inactive_directory/file").setEnabled(false));
  89. underTest.updateKey(dbSession, "A", "your_project");
  90. dbSession.commit();
  91. List<ComponentDto> result = dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, "your_project");
  92. assertThat(result)
  93. .hasSize(5)
  94. .extracting(ComponentDto::getDbKey)
  95. .containsOnlyOnce("your_project", "your_project:directory", "your_project:directory/file", "your_project:inactive_directory", "your_project:inactive_directory/file");
  96. }
  97. @Test
  98. public void update_application_branch_key() {
  99. ComponentDto app = db.components().insertPublicProject();
  100. ComponentDto appBranch = db.components().insertProjectBranch(app);
  101. ComponentDto appBranchProj1 = appBranch.copy()
  102. .setDbKey(appBranch.getDbKey().replace(BRANCH_KEY_SEPARATOR, "") + "appBranchProj1:BRANCH:1").setUuid("appBranchProj1").setScope(Qualifiers.FILE);
  103. ComponentDto appBranchProj2 = appBranch.copy()
  104. .setDbKey(appBranch.getDbKey().replace(BRANCH_KEY_SEPARATOR, "") + "appBranchProj2:BRANCH:2").setUuid("appBranchProj2").setScope(Qualifiers.FILE);
  105. db.components().insertComponent(appBranchProj1);
  106. db.components().insertComponent(appBranchProj2);
  107. int branchComponentCount = 3;
  108. String oldBranchKey = appBranch.getDbKey();
  109. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldBranchKey)).hasSize(branchComponentCount);
  110. String newBranchName = "newKey";
  111. String newAppBranchKey = ComponentDto.generateBranchKey(app.getDbKey(), newBranchName);
  112. String newAppBranchFragment = app.getDbKey() + newBranchName;
  113. underTest.updateApplicationBranchKey(dbSession, appBranch.uuid(), app.getDbKey(), newBranchName);
  114. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldBranchKey)).isEmpty();
  115. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newAppBranchKey)).hasSize(branchComponentCount);
  116. List<Map<String, Object>> result = db.select(dbSession, String.format("select kee from components where root_uuid = '%s' and scope != 'PRJ'", appBranch.uuid()));
  117. assertThat(result).hasSize(2);
  118. result.forEach(map -> map.values().forEach(k -> assertThat(k.toString()).startsWith(newAppBranchFragment)));
  119. }
  120. @Test
  121. public void update_application_branch_key_will_fail_if_newKey_exist() {
  122. ComponentDto app = db.components().insertPublicProject();
  123. ComponentDto appBranch = db.components().insertProjectBranch(app);
  124. db.components().insertProjectBranch(app, b -> b.setKey("newName"));
  125. thrown.expect(IllegalArgumentException.class);
  126. thrown.expectMessage(String.format("Impossible to update key: a component with key \"%s\" already exists.", generateBranchKey(app.getDbKey(), "newName")));
  127. underTest.updateApplicationBranchKey(dbSession, appBranch.uuid(), app.getDbKey(), "newName");
  128. }
  129. @Test
  130. public void updateApplicationBranchKey_callsAuditPersister() {
  131. ComponentDto app = db.components().insertPublicProject();
  132. ComponentDto appBranch = db.components().insertProjectBranch(app);
  133. db.components().insertProjectBranch(app, b -> b.setKey("newName"));
  134. underTestWithAuditPersister.updateApplicationBranchKey(dbSession, appBranch.uuid(), app.getDbKey(), "newName2");
  135. verify(auditPersister, times(1))
  136. .componentKeyBranchUpdate(any(DbSession.class), any(ComponentKeyNewValue.class), anyString());
  137. }
  138. @Test
  139. public void updateKey_updates_branches_too() {
  140. ComponentDto project = db.components().insertPublicProject();
  141. ComponentDto branch = db.components().insertProjectBranch(project);
  142. db.components().insertComponent(newFileDto(branch));
  143. db.components().insertComponent(newFileDto(branch));
  144. int branchComponentCount = 3;
  145. String oldProjectKey = project.getKey();
  146. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).hasSize(1);
  147. String oldBranchKey = branch.getDbKey();
  148. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldBranchKey)).hasSize(branchComponentCount);
  149. String newProjectKey = "newKey";
  150. String newBranchKey = ComponentDto.generateBranchKey(newProjectKey, branch.getBranch());
  151. underTest.updateKey(dbSession, project.uuid(), newProjectKey);
  152. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).isEmpty();
  153. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldBranchKey)).isEmpty();
  154. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newProjectKey)).hasSize(1);
  155. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newBranchKey)).hasSize(branchComponentCount);
  156. db.select(dbSession, "select kee from components")
  157. .forEach(map -> map.values().forEach(k -> assertThat(k.toString()).startsWith(newProjectKey)));
  158. }
  159. @Test
  160. public void updateKey_updates_pull_requests_too() {
  161. ComponentDto project = db.components().insertPublicProject();
  162. ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
  163. db.components().insertComponent(newFileDto(pullRequest));
  164. db.components().insertComponent(newFileDto(pullRequest));
  165. int branchComponentCount = 3;
  166. String oldProjectKey = project.getKey();
  167. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).hasSize(1);
  168. String oldBranchKey = pullRequest.getDbKey();
  169. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldBranchKey)).hasSize(branchComponentCount);
  170. String newProjectKey = "newKey";
  171. String newBranchKey = ComponentDto.generatePullRequestKey(newProjectKey, pullRequest.getPullRequest());
  172. underTest.updateKey(dbSession, project.uuid(), newProjectKey);
  173. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).isEmpty();
  174. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldBranchKey)).isEmpty();
  175. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newProjectKey)).hasSize(1);
  176. assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newBranchKey)).hasSize(branchComponentCount);
  177. db.select(dbSession, "select kee from components")
  178. .forEach(map -> map.values().forEach(k -> assertThat(k.toString()).startsWith(newProjectKey)));
  179. }
  180. private ComponentDto prefixDbKeyWithKey(ComponentDto componentDto, String key) {
  181. return componentDto.setDbKey(key + ":" + componentDto.getDbKey());
  182. }
  183. @Test
  184. public void updateKey_throws_IAE_if_component_with_specified_key_does_not_exist() {
  185. populateSomeData();
  186. thrown.expect(IllegalArgumentException.class);
  187. thrown.expectMessage("Impossible to update key: a component with key \"org.struts:struts-ui\" already exists.");
  188. underTest.updateKey(dbSession, "B", "org.struts:struts-ui");
  189. }
  190. @Test
  191. public void updateKey_throws_IAE_when_sub_component_key_is_too_long() {
  192. ComponentDto project = newPrivateProjectDto("project-uuid").setDbKey("old-project-key");
  193. db.components().insertComponent(project);
  194. db.components().insertComponent(newFileDto(project, null).setDbKey("old-project-key:file"));
  195. String newLongProjectKey = Strings.repeat("a", 400);
  196. thrown.expect(IllegalArgumentException.class);
  197. thrown.expectMessage("Component key length (405) is longer than the maximum authorized (400). '" + newLongProjectKey + ":file' was provided.");
  198. underTest.updateKey(dbSession, project.uuid(), newLongProjectKey);
  199. }
  200. @Test
  201. public void compute_new_key() {
  202. assertThat(computeNewKey("my_project", "my_", "your_")).isEqualTo("your_project");
  203. assertThat(computeNewKey("my_project", "my_", "$()_")).isEqualTo("$()_project");
  204. }
  205. @Test
  206. public void updateKey_callsAuditPersister() {
  207. db.components().insertComponent(newPrivateProjectDto("A").setDbKey("my_project"));
  208. underTestWithAuditPersister.updateKey(dbSession, "A", "your_project");
  209. verify(auditPersister, times(1))
  210. .componentKeyUpdate(any(DbSession.class), any(ComponentKeyNewValue.class), anyString());
  211. }
  212. private Predicate<RekeyedResource> doNotReturnAnyRekeyedResource() {
  213. return a -> false;
  214. }
  215. private void populateSomeData() {
  216. ComponentDto project1 = db.components().insertPrivateProject(t -> t.setDbKey("org.struts:struts").setUuid("A"));
  217. ComponentDto module1 = db.components().insertComponent(newModuleDto(project1).setDbKey("org.struts:struts-core").setUuid("B"));
  218. ComponentDto directory1 = db.components().insertComponent(newDirectory(module1, "/src/org/struts").setUuid("C"));
  219. db.components().insertComponent(ComponentTesting.newFileDto(module1, directory1).setDbKey("org.struts:struts-core:/src/org/struts/RequestContext.java").setUuid("D"));
  220. ComponentDto module2 = db.components().insertComponent(newModuleDto(project1).setDbKey("org.struts:struts-ui").setUuid("E"));
  221. ComponentDto directory2 = db.components().insertComponent(newDirectory(module2, "/src/org/struts").setUuid("F"));
  222. db.components().insertComponent(ComponentTesting.newFileDto(module2, directory2).setDbKey("org.struts:struts-ui:/src/org/struts/RequestContext.java").setUuid("G"));
  223. ComponentDto project2 = db.components().insertPublicProject(t -> t.setDbKey("foo:struts-core").setUuid("H"));
  224. }
  225. }