]> source.dussan.org Git - sonarqube.git/blob
e9d88885b27dcf471b9e84e6212b966275593ca9
[sonarqube.git] /
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.component;
21
22 import java.nio.charset.StandardCharsets;
23 import java.util.Deque;
24 import java.util.Optional;
25 import java.util.Set;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.sonar.api.utils.System2;
29 import org.sonar.api.web.UserRole;
30 import org.sonar.db.DbClient;
31 import org.sonar.db.DbSession;
32 import org.sonar.db.DbTester;
33 import org.sonar.db.component.ComponentDbTester;
34 import org.sonar.db.component.ComponentDto;
35 import org.sonar.db.component.ComponentTesting;
36 import org.sonar.db.project.ProjectDto;
37 import org.sonar.db.pushevent.PushEventDto;
38 import org.sonar.server.es.ProjectIndexer;
39 import org.sonar.server.es.TestProjectIndexers;
40 import org.sonar.server.exceptions.ForbiddenException;
41 import org.sonar.server.project.ProjectLifeCycleListeners;
42 import org.sonar.server.tester.UserSessionRule;
43
44 import static org.assertj.core.api.Assertions.assertThat;
45 import static org.assertj.core.api.Assertions.assertThatThrownBy;
46 import static org.mockito.Mockito.mock;
47
48 public class ComponentServiceUpdateKeyIT {
49
50   private final System2 system2 = System2.INSTANCE;
51
52   @Rule
53   public final UserSessionRule userSession = UserSessionRule.standalone();
54   @Rule
55   public final DbTester db = DbTester.create(system2, true);
56
57   private DbClient dbClient = db.getDbClient();
58   private DbSession dbSession = db.getSession();
59   private TestProjectIndexers projectIndexers = new TestProjectIndexers();
60   private ProjectLifeCycleListeners projectLifeCycleListeners = mock(ProjectLifeCycleListeners.class);
61   private ComponentService underTest = new ComponentService(dbClient, userSession, projectIndexers, projectLifeCycleListeners);
62
63   @Test
64   public void update_project_key() {
65     ProjectDto project = insertSampleProject();
66     ComponentDto mainBranchComponent = db.components().getComponentDto(project);
67     ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(mainBranchComponent).setKey("sample:root:src/File.xoo"));
68     ComponentDto inactiveFile = db.components().insertComponent(ComponentTesting.newFileDto(mainBranchComponent).setKey("sample:root:src/InactiveFile.xoo").setEnabled(false));
69
70     dbSession.commit();
71
72     logInAsProjectAdministrator(project);
73     underTest.updateKey(dbSession, project, "sample2:root");
74     dbSession.commit();
75
76     // Check project key has been updated
77     assertThat(db.getDbClient().componentDao().selectByKey(dbSession, project.getKey())).isEmpty();
78     assertThat(db.getDbClient().componentDao().selectByKey(dbSession, "sample2:root")).isNotNull();
79
80     // Check file key has been updated
81     assertThat(db.getDbClient().componentDao().selectByKey(dbSession, file.getKey())).isEmpty();
82     assertThat(db.getDbClient().componentDao().selectByKey(dbSession, "sample2:root:src/File.xoo")).isNotNull();
83     assertThat(db.getDbClient().componentDao().selectByKey(dbSession, "sample2:root:src/InactiveFile.xoo")).isNotNull();
84
85     assertThat(dbClient.componentDao().selectByKey(dbSession, inactiveFile.getKey())).isEmpty();
86
87     assertThat(projectIndexers.hasBeenCalled(project.getUuid(), ProjectIndexer.Cause.PROJECT_KEY_UPDATE)).isTrue();
88
89     Deque<PushEventDto> pushEvents = db.getDbClient().pushEventDao().selectChunkByProjectUuids(dbSession, Set.of(project.getUuid()), 0L, "id", 20);
90
91     assertThat(pushEvents).isNotEmpty();
92
93     Optional<PushEventDto> event = pushEvents.stream().filter(e -> e.getProjectUuid().equals(project.getUuid()) && e.getName().equals("ProjectKeyChanged")).findFirst();
94     assertThat(event).isNotEmpty();
95
96     String payload = new String(event.get().getPayload(), StandardCharsets.UTF_8);
97
98     assertThat(payload).isEqualTo("{\"oldProjectKey\":\"sample:root\",\"newProjectKey\":\"sample2:root\"}");
99   }
100
101   @Test
102   public void update_provisioned_project_key() {
103     ProjectDto provisionedProject = insertProject("provisionedProject");
104
105     dbSession.commit();
106
107     logInAsProjectAdministrator(provisionedProject);
108     underTest.updateKey(dbSession, provisionedProject, "provisionedProject2");
109     dbSession.commit();
110
111     assertComponentKeyHasBeenUpdated(provisionedProject.getKey(), "provisionedProject2");
112     assertThat(projectIndexers.hasBeenCalled(provisionedProject.getUuid(), ProjectIndexer.Cause.PROJECT_KEY_UPDATE)).isTrue();
113   }
114
115   @Test
116   public void fail_to_update_project_key_without_admin_permission() {
117     ProjectDto project = insertSampleProject();
118     userSession.logIn("john").addProjectPermission(UserRole.USER, project);
119
120     assertThatThrownBy(() -> underTest.updateKey(dbSession, project, "sample2:root"))
121       .isInstanceOf(ForbiddenException.class);
122   }
123
124   @Test
125   public void fail_if_old_key_and_new_key_are_the_same() {
126     ProjectDto project = insertSampleProject();
127     ComponentDto anotherProject = db.components().insertPrivateProject().getMainBranchComponent();
128     logInAsProjectAdministrator(project);
129
130     String anotherProjectDbKey = anotherProject.getKey();
131     assertThatThrownBy(() -> underTest.updateKey(dbSession, project,
132       anotherProjectDbKey))
133       .isInstanceOf(IllegalArgumentException.class)
134       .hasMessage("Impossible to update key: a component with key \"" + anotherProjectDbKey + "\" already exists.");
135   }
136
137   @Test
138   public void fail_if_new_key_is_empty() {
139     ProjectDto project = insertSampleProject();
140     logInAsProjectAdministrator(project);
141
142     assertThatThrownBy(() -> underTest.updateKey(dbSession, project, ""))
143       .isInstanceOf(IllegalArgumentException.class)
144       .hasMessage("Malformed key for ''. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
145   }
146
147   @Test
148   public void fail_if_new_key_is_not_formatted_correctly() {
149     ProjectDto project = insertSampleProject();
150     logInAsProjectAdministrator(project);
151
152     assertThatThrownBy(() -> underTest.updateKey(dbSession, project, "sample?root"))
153       .isInstanceOf(IllegalArgumentException.class)
154       .hasMessage("Malformed key for 'sample?root'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
155   }
156
157   private ProjectDto insertSampleProject() {
158     return insertProject("sample:root");
159   }
160
161   private ProjectDto insertProject(String key) {
162     return db.components().insertPrivateProject(c -> c.setKey(key)).getProjectDto();
163   }
164
165   private void assertComponentKeyHasBeenUpdated(String oldKey, String newKey) {
166     assertThat(dbClient.componentDao().selectByKey(dbSession, oldKey)).isEmpty();
167     assertThat(dbClient.componentDao().selectByKey(dbSession, newKey)).isPresent();
168   }
169
170   private void logInAsProjectAdministrator(ProjectDto provisionedProject) {
171     userSession.logIn("john").addProjectPermission(UserRole.ADMIN, provisionedProject);
172   }
173 }