]> source.dussan.org Git - sonarqube.git/blob
8cfea6a15016d1e05bf7ab6ebddf448864e0929e
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2020 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.organization.ws;
21
22 import com.google.common.collect.ImmutableSet;
23 import com.tngtech.java.junit.dataprovider.DataProvider;
24 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
25 import com.tngtech.java.junit.dataprovider.UseDataProvider;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Random;
30 import java.util.Set;
31 import java.util.stream.IntStream;
32 import org.junit.Rule;
33 import org.junit.Test;
34 import org.junit.rules.ExpectedException;
35 import org.junit.runner.RunWith;
36 import org.sonar.api.resources.ResourceTypes;
37 import org.sonar.api.utils.System2;
38 import org.sonar.core.util.UuidFactoryFast;
39 import org.sonar.db.DbClient;
40 import org.sonar.db.DbSession;
41 import org.sonar.db.DbTester;
42 import org.sonar.db.Pagination;
43 import org.sonar.db.component.ComponentDto;
44 import org.sonar.db.component.ComponentTesting;
45 import org.sonar.db.organization.OrganizationDto;
46 import org.sonar.db.organization.OrganizationQuery;
47 import org.sonar.db.permission.template.PermissionTemplateDto;
48 import org.sonar.db.project.ProjectDto;
49 import org.sonar.db.qualitygate.QGateWithOrgDto;
50 import org.sonar.db.qualitygate.QualityGateDto;
51 import org.sonar.db.user.GroupDto;
52 import org.sonar.db.user.UserDto;
53 import org.sonar.db.webhook.WebhookDto;
54 import org.sonar.server.component.ComponentCleanerService;
55 import org.sonar.server.es.EsClient;
56 import org.sonar.server.es.EsTester;
57 import org.sonar.server.es.ProjectIndexers;
58 import org.sonar.server.es.SearchOptions;
59 import org.sonar.server.organization.BillingValidations;
60 import org.sonar.server.organization.BillingValidationsProxy;
61 import org.sonar.server.project.Project;
62 import org.sonar.server.project.ProjectLifeCycleListeners;
63 import org.sonar.server.qualityprofile.QProfileFactoryImpl;
64 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
65 import org.sonar.server.user.index.UserIndex;
66 import org.sonar.server.user.index.UserIndexer;
67 import org.sonar.server.user.index.UserQuery;
68
69 import static com.google.common.collect.ImmutableList.of;
70 import static java.util.Arrays.asList;
71 import static java.util.Collections.emptySet;
72 import static java.util.Collections.singleton;
73 import static org.assertj.core.api.Assertions.assertThat;
74 import static org.mockito.ArgumentMatchers.any;
75 import static org.mockito.ArgumentMatchers.anyList;
76 import static org.mockito.Mockito.doThrow;
77 import static org.mockito.Mockito.mock;
78 import static org.mockito.Mockito.spy;
79 import static org.mockito.Mockito.verify;
80 import static org.sonar.core.util.stream.MoreCollectors.toSet;
81 import static org.sonar.db.user.UserTesting.newUserDto;
82 import static org.sonar.server.organization.ws.OrganizationDeleter.PAGE_SIZE;
83
84 @RunWith(DataProviderRunner.class)
85 public class OrganizationDeleterTest {
86
87   @Rule
88   public final DbTester db = DbTester.create(new System2()).setDisableDefaultOrganization(true);
89   private final DbClient dbClient = db.getDbClient();
90   private final DbSession dbSession = db.getSession();
91
92   @Rule
93   public ExpectedException exception = ExpectedException.none();
94
95   @Rule
96   public final EsTester es = EsTester.create();
97   private final EsClient esClient = es.client();
98   private ResourceTypes mockResourceTypes = mock(ResourceTypes.class);
99   private final ComponentCleanerService componentCleanerService = spy(new ComponentCleanerService(db.getDbClient(), mockResourceTypes, mock(ProjectIndexers.class)));
100   private final UserIndex userIndex = new UserIndex(esClient, System2.INSTANCE);
101   private final UserIndexer userIndexer = new UserIndexer(dbClient, esClient);
102   private final ProjectLifeCycleListeners projectLifeCycleListeners = mock(ProjectLifeCycleListeners.class);
103   private final BillingValidationsProxy billingValidations = mock(BillingValidationsProxy.class);
104
105   private final OrganizationDeleter underTest = new OrganizationDeleter(dbClient, componentCleanerService, userIndexer,
106     new QProfileFactoryImpl(dbClient, UuidFactoryFast.getInstance(), new System2(), new ActiveRuleIndexer(dbClient, esClient)), projectLifeCycleListeners, billingValidations);
107
108   @Test
109   public void delete_specified_organization() {
110     OrganizationDto organization = db.organizations().insert();
111
112     underTest.delete(dbSession, organization);
113
114     verifyOrganizationDoesNotExist(organization);
115     verify(projectLifeCycleListeners).onProjectsDeleted(emptySet());
116   }
117
118   @Test
119   public void delete_webhooks_of_organization_if_exist() {
120     OrganizationDto organization = db.organizations().insert();
121     db.webhooks().insertWebhook(organization);
122     ProjectDto project = db.components().insertPrivateProjectDto(organization);
123     WebhookDto projectWebhook = db.webhooks().insertWebhook(project);
124     db.webhookDelivery().insert(projectWebhook);
125
126     underTest.delete(dbSession, organization);
127
128     assertThat(db.countRowsOfTable(db.getSession(), "webhooks")).isZero();
129     assertThat(db.countRowsOfTable(db.getSession(), "webhook_deliveries")).isZero();
130   }
131
132   @Test
133   public void clear_user_homepage_on_organization_if_exists() {
134     OrganizationDto organization = db.organizations().insert();
135     UserDto user = dbClient.userDao().insert(dbSession, newUserDto().setHomepageType("ORGANIZATION").setHomepageParameter(organization.getUuid()));
136     dbSession.commit();
137
138     underTest.delete(dbSession, organization);
139
140     UserDto userReloaded = dbClient.userDao().selectByUuid(dbSession, user.getUuid());
141     assertThat(userReloaded.getHomepageType()).isNull();
142     assertThat(userReloaded.getHomepageParameter()).isNull();
143   }
144
145   @Test
146   public void clear_project_homepage_on_organization_if_exists() {
147     OrganizationDto organization = db.organizations().insert();
148     ComponentDto project = db.components().insertPrivateProject(organization);
149     UserDto user = dbClient.userDao().insert(dbSession,
150       newUserDto().setHomepageType("PROJECT").setHomepageParameter(project.uuid()));
151     dbSession.commit();
152
153     underTest.delete(dbSession, organization);
154
155     UserDto userReloaded = dbClient.userDao().selectByUuid(dbSession, user.getUuid());
156     assertThat(userReloaded.getHomepageType()).isNull();
157     assertThat(userReloaded.getHomepageParameter()).isNull();
158     verify(projectLifeCycleListeners).onProjectsDeleted(ImmutableSet.of(Project.from(project)));
159   }
160
161   @Test
162   @UseDataProvider("OneOrMoreIterations")
163   public void delete_components(int numberOfIterations) {
164     OrganizationDto organization = db.organizations().insert();
165     Set<ComponentDto> projects = IntStream.range(0, numberOfIterations).mapToObj(i -> {
166       ComponentDto project = db.components().insertPrivateProject(organization);
167       ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project));
168       ComponentDto directory = db.components().insertComponent(ComponentTesting.newDirectory(module, "a/b" + i));
169       ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(module, directory));
170       ComponentDto branch = db.components().insertProjectBranch(project);
171       return project;
172     }).collect(toSet());
173
174     underTest.delete(dbSession, organization);
175
176     verifyOrganizationDoesNotExist(organization);
177     assertThat(db.countRowsOfTable(db.getSession(), "projects")).isZero();
178     verify(projectLifeCycleListeners).onProjectsDeleted(projects.stream().map(Project::from).collect(toSet()));
179   }
180
181   @DataProvider
182   public static Object[][] OneOrMoreIterations() {
183     return new Object[][] {
184       {1},
185       {1 + new Random().nextInt(10)},
186     };
187   }
188
189   @Test
190   public void delete_branches() {
191     OrganizationDto organization = db.organizations().insert();
192     ComponentDto project = db.components().insertPublicProject(organization);
193     ComponentDto branch = db.components().insertProjectBranch(project);
194
195     underTest.delete(dbSession, organization);
196
197     verifyOrganizationDoesNotExist(organization);
198     assertThat(db.countRowsOfTable(db.getSession(), "projects")).isZero();
199     assertThat(db.countRowsOfTable(db.getSession(), "project_branches")).isZero();
200     verify(projectLifeCycleListeners).onProjectsDeleted(ImmutableSet.of(Project.from(project)));
201   }
202
203   @Test
204   public void delete_members() {
205     OrganizationDto org = db.organizations().insert();
206     OrganizationDto otherOrg = db.organizations().insert();
207     UserDto user1 = db.users().insertUser();
208     UserDto user2 = db.users().insertUser();
209     db.organizations().addMember(org, user1);
210     db.organizations().addMember(otherOrg, user1);
211     db.organizations().addMember(org, user2);
212     userIndexer.commitAndIndex(db.getSession(), asList(user1, user2));
213
214     underTest.delete(dbSession, org);
215
216     verifyOrganizationDoesNotExist(org);
217     assertThat(db.getDbClient().organizationMemberDao().select(db.getSession(), org.getUuid(), user1.getUuid())).isNotPresent();
218     assertThat(db.getDbClient().organizationMemberDao().select(db.getSession(), org.getUuid(), user2.getUuid())).isNotPresent();
219     assertThat(db.getDbClient().organizationMemberDao().select(db.getSession(), otherOrg.getUuid(), user1.getUuid())).isPresent();
220     assertThat(userIndex.search(UserQuery.builder().setOrganizationUuid(org.getUuid()).build(), new SearchOptions()).getTotal()).isEqualTo(0);
221     assertThat(userIndex.search(UserQuery.builder().setOrganizationUuid(otherOrg.getUuid()).build(), new SearchOptions()).getTotal()).isEqualTo(1);
222     verify(projectLifeCycleListeners).onProjectsDeleted(emptySet());
223   }
224
225   @Test
226   public void delete_quality_gates() {
227     QualityGateDto builtInQualityGate = db.qualityGates().insertBuiltInQualityGate();
228     OrganizationDto organization = db.organizations().insert();
229     db.qualityGates().associateQualityGateToOrganization(builtInQualityGate, organization);
230     OrganizationDto otherOrganization = db.organizations().insert();
231     db.qualityGates().associateQualityGateToOrganization(builtInQualityGate, otherOrganization);
232     QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization);
233     QGateWithOrgDto qualityGateInOtherOrg = db.qualityGates().insertQualityGate(otherOrganization);
234
235     underTest.delete(dbSession, organization);
236
237     verifyOrganizationDoesNotExist(organization);
238     assertThat(db.select("select uuid as \"uuid\" from quality_gates"))
239       .extracting(row -> (String) row.get("uuid"))
240       .containsExactlyInAnyOrder(qualityGateInOtherOrg.getUuid(), builtInQualityGate.getUuid());
241     assertThat(db.select("select organization_uuid as \"organizationUuid\" from org_quality_gates"))
242       .extracting(row -> (String) row.get("organizationUuid"))
243       .containsOnly(otherOrganization.getUuid());
244
245     // Check built-in quality gate is still available in other organization
246     assertThat(db.getDbClient().qualityGateDao().selectByOrganizationAndName(db.getSession(), otherOrganization, "Sonar way")).isNotNull();
247     verify(projectLifeCycleListeners).onProjectsDeleted(emptySet());
248   }
249
250   @Test
251   public void projectLifeCycleListener_are_notified_even_if_deletion_of_a_project_throws_an_Exception() {
252     OrganizationDto organization = db.organizations().insert();
253     ComponentDto[] components = new ComponentDto[] {
254       db.components().insertPublicProject(organization),
255       db.components().insertPublicProject(organization),
256       db.components().insertPublicProject(organization)
257     };
258     ProjectDto[] projects = Arrays.stream(components).map(c -> dbClient.projectDao().selectByUuid(dbSession, c.uuid()).get()).toArray(ProjectDto[]::new);
259
260     RuntimeException expectedException = new RuntimeException("Faking deletion of 2nd project throwing an exception");
261     doThrow(expectedException).when(componentCleanerService).delete(any(), anyList());
262     exception.expect(RuntimeException.class);
263     exception.expectMessage(expectedException.getMessage());
264
265     underTest.delete(dbSession, organization);
266
267     verify(projectLifeCycleListeners).onProjectsDeleted(Arrays.stream(projects).map(Project::from).collect(toSet()));
268   }
269
270   @Test
271   public void call_billing_validation_on_delete() {
272     OrganizationDto organization = db.organizations().insert();
273
274     underTest.delete(dbSession, organization);
275
276     verify(billingValidations).onDelete(any(BillingValidations.Organization.class));
277   }
278
279   @Test
280   public void delete_organization_alm_binding() {
281     OrganizationDto organization = db.organizations().insert();
282     db.alm().insertOrganizationAlmBinding(organization, db.alm().insertAlmAppInstall(), true);
283
284     underTest.delete(dbSession, organization);
285
286     assertThat(db.getDbClient().organizationAlmBindingDao().selectByOrganization(db.getSession(), organization)).isNotPresent();
287   }
288
289   @Test
290   @UseDataProvider("queriesAndUnmatchedOrganizationKeys")
291   public void delete_organizations_matched_by_query(OrganizationQuery query, Collection<String> unmatchedOrgKeys) {
292     db.organizations().insert(o -> o.setKey("org1"));
293     db.organizations().insert(o -> o.setKey("org2"));
294     db.organizations().insert(o -> o.setKey("org3"));
295
296     underTest.deleteByQuery(query);
297
298     assertThat(dbClient.organizationDao().selectByQuery(db.getSession(), OrganizationQuery.returnAll(), Pagination.all()))
299       .extracting(OrganizationDto::getKey)
300       .containsExactlyInAnyOrderElementsOf(unmatchedOrgKeys);
301   }
302
303   @DataProvider
304   public static Object[][] queriesAndUnmatchedOrganizationKeys() {
305     return new Object[][] {
306       {OrganizationQuery.returnAll(), Collections.emptyList()},
307       {OrganizationQuery.newOrganizationQueryBuilder().setKeys(singleton("nonexistent")).build(), Arrays.asList("org1", "org2", "org3")},
308       {OrganizationQuery.newOrganizationQueryBuilder().setKeys(singleton("org1")).build(), Arrays.asList("org2", "org3")},
309     };
310   }
311
312   @Test
313   public void delete_organizations_for_all_query_pages() {
314     int orgsCountGreaterThanPageSize = PAGE_SIZE + 1;
315
316     IntStream.range(0, orgsCountGreaterThanPageSize).forEach(ignored -> db.organizations().insert());
317
318     OrganizationQuery query = OrganizationQuery.returnAll();
319     assertThat(dbClient.organizationDao().countByQuery(db.getSession(), query)).isEqualTo(orgsCountGreaterThanPageSize);
320
321     underTest.deleteByQuery(query);
322
323     assertThat(dbClient.organizationDao().countByQuery(db.getSession(), query)).isZero();
324   }
325
326   private void verifyOrganizationDoesNotExist(OrganizationDto organization) {
327     assertThat(db.getDbClient().organizationDao().selectByKey(dbSession, organization.getKey())).isEmpty();
328   }
329 }