]> source.dussan.org Git - sonarqube.git/blob
82a5d63698ce29ad08b539a3b94696e9d92ca8c8
[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);
56
57   private ComponentDbTester componentDb = new ComponentDbTester(db);
58   private DbClient dbClient = db.getDbClient();
59   private DbSession dbSession = db.getSession();
60   private TestProjectIndexers projectIndexers = new TestProjectIndexers();
61   private ProjectLifeCycleListeners projectLifeCycleListeners = mock(ProjectLifeCycleListeners.class);
62   private ComponentService underTest = new ComponentService(dbClient, userSession, projectIndexers, projectLifeCycleListeners);
63
64   @Test
65   public void update_project_key() {
66     ComponentDto project = insertSampleProject();
67     ComponentDto file = componentDb.insertComponent(ComponentTesting.newFileDto(project, null).setKey("sample:root:src/File.xoo"));
68     ComponentDto inactiveFile = componentDb.insertComponent(ComponentTesting.newFileDto(project, null).setKey("sample:root:src/InactiveFile.xoo").setEnabled(false));
69
70     dbSession.commit();
71
72     logInAsProjectAdministrator(project);
73     underTest.updateKey(dbSession, componentDb.getProjectDto(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.uuid(), ProjectIndexer.Cause.PROJECT_KEY_UPDATE)).isTrue();
88
89     Deque<PushEventDto> pushEvents = db.getDbClient().pushEventDao().selectChunkByProjectUuids(dbSession, Set.of(project.uuid()), 0L, "id", 20);
90
91     assertThat(pushEvents).isNotEmpty();
92
93     Optional<PushEventDto> event = pushEvents.stream().filter(e -> e.getProjectUuid().equals(project.uuid()) && 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     ComponentDto provisionedProject = insertProject("provisionedProject");
104
105     dbSession.commit();
106
107     logInAsProjectAdministrator(provisionedProject);
108     underTest.updateKey(dbSession, componentDb.getProjectDto(provisionedProject), "provisionedProject2");
109     dbSession.commit();
110
111     assertComponentKeyHasBeenUpdated(provisionedProject.getKey(), "provisionedProject2");
112     assertThat(projectIndexers.hasBeenCalled(provisionedProject.uuid(), ProjectIndexer.Cause.PROJECT_KEY_UPDATE)).isTrue();
113   }
114
115   @Test
116   public void fail_to_update_project_key_without_admin_permission() {
117     ComponentDto project = insertSampleProject();
118     userSession.logIn("john").addProjectPermission(UserRole.USER, project);
119
120     ProjectDto projectDto = componentDb.getProjectDto(project);
121     assertThatThrownBy(() -> underTest.updateKey(dbSession, projectDto, "sample2:root"))
122       .isInstanceOf(ForbiddenException.class);
123   }
124
125   @Test
126   public void fail_if_old_key_and_new_key_are_the_same() {
127     ComponentDto project = insertSampleProject();
128     ComponentDto anotherProject = componentDb.insertPrivateProject();
129     logInAsProjectAdministrator(project);
130
131     ProjectDto projectDto = componentDb.getProjectDto(project);
132     String anotherProjectDbKey = anotherProject.getKey();
133     assertThatThrownBy(() -> underTest.updateKey(dbSession, projectDto,
134       anotherProjectDbKey))
135       .isInstanceOf(IllegalArgumentException.class)
136       .hasMessage("Impossible to update key: a component with key \"" + anotherProjectDbKey + "\" already exists.");
137   }
138
139   @Test
140   public void fail_if_new_key_is_empty() {
141     ComponentDto project = insertSampleProject();
142     logInAsProjectAdministrator(project);
143
144     ProjectDto projectDto = componentDb.getProjectDto(project);
145     assertThatThrownBy(() -> underTest.updateKey(dbSession, projectDto, ""))
146       .isInstanceOf(IllegalArgumentException.class)
147       .hasMessage("Malformed key for ''. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
148   }
149
150   @Test
151   public void fail_if_new_key_is_not_formatted_correctly() {
152     ComponentDto project = insertSampleProject();
153     logInAsProjectAdministrator(project);
154
155     ProjectDto projectDto = componentDb.getProjectDto(project);
156     assertThatThrownBy(() -> underTest.updateKey(dbSession, projectDto, "sample?root"))
157       .isInstanceOf(IllegalArgumentException.class)
158       .hasMessage("Malformed key for 'sample?root'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
159   }
160
161   private ComponentDto insertSampleProject() {
162     return insertProject("sample:root");
163   }
164
165   private ComponentDto insertProject(String key) {
166     return componentDb.insertPrivateProject(c -> c.setKey(key));
167   }
168
169   private void assertComponentKeyHasBeenUpdated(String oldKey, String newKey) {
170     assertThat(dbClient.componentDao().selectByKey(dbSession, oldKey)).isEmpty();
171     assertThat(dbClient.componentDao().selectByKey(dbSession, newKey)).isPresent();
172   }
173
174   private void logInAsProjectAdministrator(ComponentDto provisionedProject) {
175     userSession.logIn("john").addProjectPermission(UserRole.ADMIN, provisionedProject);
176   }
177 }