3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.ce.task.projectanalysis.step;
22 import java.text.SimpleDateFormat;
23 import java.util.Date;
24 import java.util.Random;
25 import java.util.stream.Stream;
26 import javax.annotation.Nullable;
27 import org.junit.Before;
28 import org.junit.Rule;
29 import org.junit.Test;
30 import org.sonar.api.resources.Qualifiers;
31 import org.sonar.api.resources.Scopes;
32 import org.sonar.api.utils.System2;
33 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
34 import org.sonar.ce.task.projectanalysis.component.BranchPersister;
35 import org.sonar.ce.task.projectanalysis.component.Component;
36 import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl;
37 import org.sonar.ce.task.projectanalysis.component.MutableDisabledComponentsHolder;
38 import org.sonar.ce.task.projectanalysis.component.ProjectPersister;
39 import org.sonar.ce.task.projectanalysis.component.ProjectViewAttributes;
40 import org.sonar.ce.task.projectanalysis.component.SubViewAttributes;
41 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
42 import org.sonar.ce.task.projectanalysis.component.ViewAttributes;
43 import org.sonar.ce.task.projectanalysis.component.ViewsComponent;
44 import org.sonar.ce.task.projectanalysis.component.VisitException;
45 import org.sonar.ce.task.step.ComputationStep;
46 import org.sonar.ce.task.step.TestComputationStepContext;
47 import org.sonar.db.DbClient;
48 import org.sonar.db.DbTester;
49 import org.sonar.db.component.ComponentDbTester;
50 import org.sonar.db.component.ComponentDto;
51 import org.sonar.db.component.ComponentTesting;
53 import static org.assertj.core.api.Assertions.assertThat;
54 import static org.assertj.core.api.Assertions.assertThatThrownBy;
55 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
56 import static org.mockito.Mockito.mock;
57 import static org.mockito.Mockito.when;
58 import static org.sonar.ce.task.projectanalysis.component.ViewAttributes.Type.APPLICATION;
59 import static org.sonar.ce.task.projectanalysis.component.ViewAttributes.Type.PORTFOLIO;
60 import static org.sonar.ce.task.projectanalysis.component.ViewsComponent.builder;
61 import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME;
62 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
63 import static org.sonar.db.component.ComponentTesting.newProjectCopy;
64 import static org.sonar.db.component.ComponentTesting.newSubPortfolio;
66 public class ViewsPersistComponentsStepIT extends BaseStepTest {
68 private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
70 private static final String VIEW_KEY = "VIEW_KEY";
71 private static final String VIEW_NAME = "VIEW_NAME";
72 private static final String VIEW_DESCRIPTION = "view description";
73 private static final String VIEW_UUID = "VIEW_UUID";
74 private static final String SUBVIEW_1_KEY = "SUBVIEW_1_KEY";
75 private static final String SUBVIEW_1_NAME = "SUBVIEW_1_NAME";
76 private static final String SUBVIEW_1_DESCRIPTION = "subview 1 description";
77 private static final String SUBVIEW_1_UUID = "SUBVIEW_1_UUID";
78 private static final String PROJECT_VIEW_1_KEY = "PV1_KEY";
79 private static final String PROJECT_VIEW_1_NAME = "PV1_NAME";
80 private static final String PROJECT_VIEW_1_UUID = "PV1_UUID";
83 public DbTester dbTester = DbTester.create(System2.INSTANCE, true);
86 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
88 public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
90 private final System2 system2 = mock(System2.class);
91 private final DbClient dbClient = dbTester.getDbClient();
93 private final ComponentDbTester componentDbTester = new ComponentDbTester(dbTester);
94 private final MutableDisabledComponentsHolder disabledComponentsHolder = mock(MutableDisabledComponentsHolder.class, RETURNS_DEEP_STUBS);
95 private PersistComponentsStep underTest;
98 public void setup() throws Exception {
99 now = DATE_FORMAT.parse("2015-06-02");
100 when(system2.now()).thenReturn(now.getTime());
102 analysisMetadataHolder.setBranch(new DefaultBranchImpl(DEFAULT_MAIN_BRANCH_NAME));
103 BranchPersister branchPersister = mock(BranchPersister.class);
104 ProjectPersister projectPersister = mock(ProjectPersister.class);
105 underTest = new PersistComponentsStep(dbClient, treeRootHolder, system2, disabledComponentsHolder, analysisMetadataHolder, branchPersister, projectPersister);
109 protected ComputationStep step() {
114 public void persist_empty_view() {
115 treeRootHolder.setRoot(createViewBuilder(PORTFOLIO).build());
117 underTest.execute(new TestComputationStepContext());
119 assertRowsCountInTableComponents(1);
121 ComponentDto projectDto = getComponentFromDb(VIEW_KEY);
122 assertDtoIsView(projectDto);
126 public void persist_existing_empty_view() {
127 // most of the time view already exists since its supposed to be created when config is uploaded
128 persistComponents(newViewDto());
130 treeRootHolder.setRoot(createViewBuilder(PORTFOLIO).build());
132 underTest.execute(new TestComputationStepContext());
134 assertRowsCountInTableComponents(1);
136 assertDtoNotUpdated(VIEW_KEY);
140 public void persist_view_with_projectView() {
141 ComponentDto project = ComponentTesting.newPrivateProjectDto();
142 persistComponents(project);
144 treeRootHolder.setRoot(
145 createViewBuilder(PORTFOLIO)
146 .addChildren(createProjectView1Builder(project, null).build())
149 underTest.execute(new TestComputationStepContext());
151 assertRowsCountInTableComponents(3);
153 ComponentDto viewDto = getComponentFromDb(VIEW_KEY);
154 assertDtoIsView(viewDto);
156 ComponentDto pv1Dto = getComponentFromDb(PROJECT_VIEW_1_KEY);
157 assertDtoIsProjectView1(pv1Dto, viewDto, viewDto, project);
161 * Assumption under test here is that application should always exists in the projects table.
162 * We should never have a state where application exists in Components table but not in Projects table
165 public void execute_whenApplicationDoesNotExistsAndTryingToInsertItInComponentsTable_throwException() {
166 ComponentDto project = dbTester.components().insertPrivateProject().getMainBranchComponent();
168 treeRootHolder.setRoot(
169 createViewBuilder(APPLICATION)
170 .addChildren(createProjectView1Builder(project, null).build())
173 assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
174 .isInstanceOf(VisitException.class);
178 public void persist_empty_subview() {
179 treeRootHolder.setRoot(
180 createViewBuilder(PORTFOLIO)
182 createSubView1Builder(null).build())
185 underTest.execute(new TestComputationStepContext());
187 assertRowsCountInTableComponents(2);
189 ComponentDto viewDto = getComponentFromDb(VIEW_KEY);
190 assertDtoIsView(viewDto);
192 ComponentDto sv1Dto = getComponentFromDb(SUBVIEW_1_KEY);
193 assertDtoIsSubView1(viewDto, sv1Dto);
197 public void persist_empty_subview_having_original_view_uuid() {
198 treeRootHolder.setRoot(
199 createViewBuilder(PORTFOLIO)
201 createSubView1Builder("ORIGINAL_UUID").build())
204 underTest.execute(new TestComputationStepContext());
206 assertRowsCountInTableComponents(2);
208 ComponentDto subView = getComponentFromDb(SUBVIEW_1_KEY);
209 assertThat(subView.getCopyComponentUuid()).isEqualTo("ORIGINAL_UUID");
213 public void persist_existing_empty_subview_under_existing_view() {
214 ComponentDto viewDto = newViewDto();
215 persistComponents(viewDto);
216 persistComponents(ComponentTesting.newSubPortfolio(viewDto, SUBVIEW_1_UUID, SUBVIEW_1_KEY).setName(SUBVIEW_1_NAME));
218 treeRootHolder.setRoot(
219 createViewBuilder(PORTFOLIO)
221 createSubView1Builder(null).build())
224 underTest.execute(new TestComputationStepContext());
226 assertRowsCountInTableComponents(2);
228 assertDtoNotUpdated(VIEW_KEY);
229 assertDtoNotUpdated(SUBVIEW_1_KEY);
233 public void persist_empty_subview_under_existing_view() {
234 persistComponents(newViewDto());
236 treeRootHolder.setRoot(
237 createViewBuilder(PORTFOLIO)
239 createSubView1Builder(null).build())
242 underTest.execute(new TestComputationStepContext());
244 assertRowsCountInTableComponents(2);
246 assertDtoNotUpdated(VIEW_KEY);
247 assertDtoIsSubView1(getComponentFromDb(VIEW_KEY), getComponentFromDb(SUBVIEW_1_KEY));
251 public void persist_project_view_under_subview() {
252 ComponentDto project = ComponentTesting.newPrivateProjectDto();
253 persistComponents(project);
255 treeRootHolder.setRoot(
256 createViewBuilder(PORTFOLIO)
258 createSubView1Builder(null)
260 createProjectView1Builder(project, null).build())
264 underTest.execute(new TestComputationStepContext());
266 assertRowsCountInTableComponents(4);
268 ComponentDto viewDto = getComponentFromDb(VIEW_KEY);
269 assertDtoIsView(viewDto);
270 ComponentDto subView1Dto = getComponentFromDb(SUBVIEW_1_KEY);
271 assertDtoIsSubView1(viewDto, subView1Dto);
272 ComponentDto pv1Dto = getComponentFromDb(PROJECT_VIEW_1_KEY);
273 assertDtoIsProjectView1(pv1Dto, viewDto, subView1Dto, project);
277 public void update_view_name_and_longName() {
278 ComponentDto viewDto = newViewDto().setLongName("another long name").setCreatedAt(now);
279 persistComponents(viewDto);
281 treeRootHolder.setRoot(createViewBuilder(PORTFOLIO).build());
283 underTest.execute(new TestComputationStepContext());
285 // commit functional transaction -> copies B-fields to A-fields
286 dbClient.componentDao().applyBChangesForBranchUuid(dbTester.getSession(), viewDto.uuid());
289 assertRowsCountInTableComponents(1);
290 ComponentDto newViewDto = getComponentFromDb(VIEW_KEY);
291 assertDtoIsView(newViewDto);
295 public void update_project_view() {
296 ComponentDto view = newViewDto();
297 ComponentDto project = ComponentTesting.newPrivateProjectDto();
298 persistComponents(view, project);
299 ComponentDto projectView = ComponentTesting.newProjectCopy(PROJECT_VIEW_1_UUID, project, view)
300 .setKey(PROJECT_VIEW_1_KEY)
303 persistComponents(projectView);
304 treeRootHolder.setRoot(
305 createViewBuilder(PORTFOLIO)
306 .addChildren(createProjectView1Builder(project, null).build())
308 underTest.execute(new TestComputationStepContext());
309 // commit functional transaction -> copies B-fields to A-fields
310 dbClient.componentDao().applyBChangesForBranchUuid(dbTester.getSession(), view.uuid());
313 assertRowsCountInTableComponents(3);
314 ComponentDto pv1Dto = getComponentFromDb(PROJECT_VIEW_1_KEY);
315 assertDtoIsProjectView1(pv1Dto, view, view, project);
319 public void update_copy_component_uuid_of_project_view() {
320 ComponentDto view = newViewDto();
321 ComponentDto project1 = newPrivateProjectDto("P1");
322 ComponentDto project2 = newPrivateProjectDto("P2");
323 persistComponents(view, project1, project2);
325 // Project view in DB is associated to project1
326 ComponentDto projectView = ComponentTesting.newProjectCopy(PROJECT_VIEW_1_UUID, project1, view)
327 .setKey(PROJECT_VIEW_1_KEY)
329 persistComponents(projectView);
331 treeRootHolder.setRoot(
332 createViewBuilder(PORTFOLIO)
333 // Project view in the View is linked to the first project2
334 .addChildren(createProjectView1Builder(project2, null).build())
337 underTest.execute(new TestComputationStepContext());
339 // commit functional transaction -> copies B-fields to A-fields
340 dbClient.componentDao().applyBChangesForBranchUuid(dbTester.getSession(), view.uuid());
343 ComponentDto pv1Dto = getComponentFromDb(PROJECT_VIEW_1_KEY);
344 // Project view should now be linked to project2
345 assertDtoIsProjectView1(pv1Dto, view, view, project2);
349 public void update_copy_component_uuid_of_sub_view() {
350 ComponentDto view = newViewDto();
351 ComponentDto subView = newSubViewDto(view).setCopyComponentUuid("OLD_COPY");
352 persistComponents(view, subView);
354 treeRootHolder.setRoot(
355 createViewBuilder(PORTFOLIO)
357 createSubView1Builder("NEW_COPY").build())
360 underTest.execute(new TestComputationStepContext());
362 // commit functional transaction -> copies B-fields to A-fields
363 dbClient.componentDao().applyBChangesForBranchUuid(dbTester.getSession(), view.uuid());
366 ComponentDto subViewReloaded = getComponentFromDb(SUBVIEW_1_KEY);
367 assertThat(subViewReloaded.getCopyComponentUuid()).isEqualTo("NEW_COPY");
371 public void persists_new_components_as_public_if_root_does_not_exist_yet_out_of_functional_transaction() {
372 ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto());
373 treeRootHolder.setRoot(
374 createViewBuilder(PORTFOLIO)
376 createSubView1Builder(null)
378 createProjectView1Builder(project, null).build())
382 underTest.execute(new TestComputationStepContext());
384 Stream.of(VIEW_UUID, SUBVIEW_1_UUID, PROJECT_VIEW_1_UUID)
385 .forEach(uuid -> assertThat(dbClient.componentDao().selectByUuid(dbTester.getSession(), uuid).get().isPrivate()).isFalse());
389 public void persists_new_components_with_visibility_of_root_in_db_out_of_functional_transaction() {
390 boolean isRootPrivate = new Random().nextBoolean();
391 ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto());
392 ComponentDto view = newViewDto().setUuid(VIEW_UUID).setKey(VIEW_KEY).setName("View").setPrivate(isRootPrivate);
393 dbTester.components().insertComponent(view);
394 treeRootHolder.setRoot(
395 createViewBuilder(PORTFOLIO)
397 createSubView1Builder(null)
399 createProjectView1Builder(project, null).build())
403 underTest.execute(new TestComputationStepContext());
405 Stream.of(VIEW_UUID, SUBVIEW_1_UUID, PROJECT_VIEW_1_UUID)
406 .forEach(uuid -> assertThat(dbClient.componentDao().selectByUuid(dbTester.getSession(), uuid).get().isPrivate())
407 .describedAs("for uuid " + uuid)
408 .isEqualTo(isRootPrivate));
412 public void persists_existing_components_with_visibility_of_root_in_db_out_of_functional_transaction() {
413 boolean isRootPrivate = new Random().nextBoolean();
414 ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto());
415 ComponentDto view = newViewDto().setUuid(VIEW_UUID).setKey(VIEW_KEY).setName("View").setPrivate(isRootPrivate);
416 dbTester.components().insertComponent(view);
417 ComponentDto subView = newSubPortfolio(view).setUuid("BCDE").setKey("MODULE").setPrivate(!isRootPrivate);
418 dbTester.components().insertComponent(subView);
419 dbTester.components().insertComponent(newProjectCopy("DEFG", project, view).setKey("DIR").setPrivate(isRootPrivate));
420 treeRootHolder.setRoot(
421 createViewBuilder(PORTFOLIO)
423 createSubView1Builder(null)
425 createProjectView1Builder(project, null).build())
429 underTest.execute(new TestComputationStepContext());
431 Stream.of(VIEW_UUID, SUBVIEW_1_UUID, PROJECT_VIEW_1_UUID, subView.uuid(), "DEFG")
432 .forEach(uuid -> assertThat(dbClient.componentDao().selectByUuid(dbTester.getSession(), uuid).get().isPrivate())
433 .describedAs("for uuid " + uuid)
434 .isEqualTo(isRootPrivate));
437 private static ViewsComponent.Builder createViewBuilder(ViewAttributes.Type viewType) {
438 return builder(Component.Type.VIEW, VIEW_KEY)
441 .setDescription(VIEW_DESCRIPTION)
442 .setViewAttributes(new ViewAttributes(viewType));
445 private ViewsComponent.Builder createSubView1Builder(@Nullable String originalViewUuid) {
446 return builder(Component.Type.SUBVIEW, SUBVIEW_1_KEY)
447 .setUuid(SUBVIEW_1_UUID)
448 .setName(SUBVIEW_1_NAME)
449 .setDescription(SUBVIEW_1_DESCRIPTION)
450 .setSubViewAttributes(new SubViewAttributes(originalViewUuid));
453 private static ViewsComponent.Builder createProjectView1Builder(ComponentDto project, Long analysisDate) {
454 return builder(Component.Type.PROJECT_VIEW, PROJECT_VIEW_1_KEY)
455 .setUuid(PROJECT_VIEW_1_UUID)
456 .setName(PROJECT_VIEW_1_NAME)
457 .setDescription("project view description is not persisted")
458 .setProjectViewAttributes(new ProjectViewAttributes(project.uuid(), project.getKey(), analysisDate, null));
461 private void persistComponents(ComponentDto... componentDtos) {
462 componentDbTester.insertComponents(componentDtos);
465 private ComponentDto getComponentFromDb(String componentKey) {
466 return dbClient.componentDao().selectByKey(dbTester.getSession(), componentKey).get();
469 private void assertRowsCountInTableComponents(int rowCount) {
470 assertThat(dbTester.countRowsOfTable("components")).isEqualTo(rowCount);
473 private void assertDtoNotUpdated(String componentKey) {
474 assertThat(getComponentFromDb(componentKey).getCreatedAt()).isNotEqualTo(now);
477 private ComponentDto newViewDto() {
478 return ComponentTesting.newPortfolio(VIEW_UUID)
483 private ComponentDto newSubViewDto(ComponentDto rootView) {
484 return ComponentTesting.newSubPortfolio(rootView, SUBVIEW_1_UUID, SUBVIEW_1_KEY)
485 .setName(SUBVIEW_1_NAME);
489 * Assertions to verify the DTO created from {@link #createViewBuilder(ViewAttributes.Type)} ()}
491 private void assertDtoIsView(ComponentDto dto) {
492 assertThat(dto.name()).isEqualTo(VIEW_NAME);
493 assertThat(dto.longName()).isEqualTo(VIEW_NAME);
494 assertThat(dto.description()).isEqualTo(VIEW_DESCRIPTION);
495 assertThat(dto.path()).isNull();
496 assertThat(dto.uuid()).isEqualTo(VIEW_UUID);
497 assertThat(dto.branchUuid()).isEqualTo(VIEW_UUID);
498 assertThat(dto.qualifier()).isEqualTo(Qualifiers.VIEW);
499 assertThat(dto.scope()).isEqualTo(Scopes.PROJECT);
500 assertThat(dto.getCopyComponentUuid()).isNull();
501 assertThat(dto.getCreatedAt()).isEqualTo(now);
505 * Assertions to verify the DTO created from {@link #createViewBuilder(ViewAttributes.Type)} ()}
507 private void assertDtoIsApplication(ComponentDto dto) {
508 assertThat(dto.name()).isEqualTo(VIEW_NAME);
509 assertThat(dto.longName()).isEqualTo(VIEW_NAME);
510 assertThat(dto.description()).isEqualTo(VIEW_DESCRIPTION);
511 assertThat(dto.path()).isNull();
512 assertThat(dto.uuid()).isEqualTo(VIEW_UUID);
513 assertThat(dto.branchUuid()).isEqualTo(VIEW_UUID);
514 assertThat(dto.qualifier()).isEqualTo(Qualifiers.APP);
515 assertThat(dto.scope()).isEqualTo(Scopes.PROJECT);
516 assertThat(dto.getCopyComponentUuid()).isNull();
517 assertThat(dto.getCreatedAt()).isEqualTo(now);
521 * Assertions to verify the DTO created from {@link #createProjectView1Builder(ComponentDto, Long)}
523 private void assertDtoIsSubView1(ComponentDto viewDto, ComponentDto sv1Dto) {
524 assertThat(sv1Dto.name()).isEqualTo(SUBVIEW_1_NAME);
525 assertThat(sv1Dto.longName()).isEqualTo(SUBVIEW_1_NAME);
526 assertThat(sv1Dto.description()).isEqualTo(SUBVIEW_1_DESCRIPTION);
527 assertThat(sv1Dto.path()).isNull();
528 assertThat(sv1Dto.uuid()).isEqualTo(SUBVIEW_1_UUID);
529 assertThat(sv1Dto.branchUuid()).isEqualTo(viewDto.uuid());
530 assertThat(sv1Dto.qualifier()).isEqualTo(Qualifiers.SUBVIEW);
531 assertThat(sv1Dto.scope()).isEqualTo(Scopes.PROJECT);
532 assertThat(sv1Dto.getCopyComponentUuid()).isNull();
533 assertThat(sv1Dto.getCreatedAt()).isEqualTo(now);
536 private void assertDtoIsProjectView1(ComponentDto pv1Dto, ComponentDto viewDto, ComponentDto parentViewDto, ComponentDto project) {
537 assertThat(pv1Dto.name()).isEqualTo(PROJECT_VIEW_1_NAME);
538 assertThat(pv1Dto.longName()).isEqualTo(PROJECT_VIEW_1_NAME);
539 assertThat(pv1Dto.description()).isNull();
540 assertThat(pv1Dto.path()).isNull();
541 assertThat(pv1Dto.uuid()).isEqualTo(PROJECT_VIEW_1_UUID);
542 assertThat(pv1Dto.branchUuid()).isEqualTo(viewDto.uuid());
543 assertThat(pv1Dto.qualifier()).isEqualTo(Qualifiers.PROJECT);
544 assertThat(pv1Dto.scope()).isEqualTo(Scopes.FILE);
545 assertThat(pv1Dto.getCopyComponentUuid()).isEqualTo(project.uuid());
546 assertThat(pv1Dto.getCreatedAt()).isEqualTo(now);