3 * Copyright (C) 2009-2024 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.server.platform.ws;
22 import com.google.gson.Gson;
23 import com.tngtech.java.junit.dataprovider.DataProvider;
24 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
25 import java.sql.SQLException;
26 import java.util.List;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 import org.sonar.db.Database;
31 import org.sonar.db.DbTester;
32 import org.sonar.db.component.BranchType;
33 import org.sonar.db.component.ComponentDto;
34 import org.sonar.db.portfolio.PortfolioDto;
35 import org.sonar.db.property.PropertyDto;
36 import org.sonar.server.exceptions.ForbiddenException;
37 import org.sonar.server.platform.db.migration.adhoc.AddMeasuresMigratedColumnToPortfoliosTable;
38 import org.sonar.server.platform.db.migration.adhoc.AddMeasuresMigratedColumnToProjectBranchesTable;
39 import org.sonar.server.platform.db.migration.adhoc.MigrateBranchesLiveMeasuresToMeasures;
40 import org.sonar.server.platform.db.migration.adhoc.MigratePortfoliosLiveMeasuresToMeasures;
41 import org.sonar.server.tester.UserSessionRule;
42 import org.sonar.server.ws.TestRequest;
43 import org.sonar.server.ws.TestResponse;
44 import org.sonar.server.ws.WsActionTester;
46 import static org.assertj.core.api.Assertions.assertThat;
47 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
48 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
49 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
50 import static org.mockito.Mockito.mock;
51 import static org.mockito.Mockito.verify;
52 import static org.mockito.Mockito.verifyNoInteractions;
53 import static org.sonar.db.component.BranchType.BRANCH;
54 import static org.sonar.server.platform.ws.MigrateMeasuresAction.SYSTEM_MEASURES_MIGRATION_ENABLED;
55 import static org.sonar.test.JsonAssert.assertJson;
57 @RunWith(DataProviderRunner.class)
58 public class MigrateMeasuresActionTest {
59 private static final Gson GSON = new Gson();
61 public static final String PARAM_SIZE = "size";
63 public UserSessionRule userSessionRule = UserSessionRule.standalone().logIn().setSystemAdministrator();
66 public DbTester dbTester = DbTester.create();
68 private final MigrateBranchesLiveMeasuresToMeasures measuresMigration = mock();
69 private final MigratePortfoliosLiveMeasuresToMeasures portfoliosMigration = mock();
70 private final MigrateMeasuresAction underTest = new MigrateMeasuresAction(userSessionRule, dbTester.getDbClient(), measuresMigration, portfoliosMigration);
71 private final WsActionTester tester = new WsActionTester(underTest);
74 public void should_throw_if_migration_is_not_enabled() {
75 TestRequest request = tester.newRequest();
77 assertThatIllegalStateException()
78 .isThrownBy(request::execute)
79 .withMessage("Migration is not enabled. Please call the endpoint /api/system/prepare_migration?enable=true and retry.");
83 @DataProvider(value = {"0", "-1", "-100"})
84 public void should_throws_IAE_if_size_in_invalid(int size) throws SQLException {
87 TestRequest request = tester
89 .setParam(PARAM_SIZE, Integer.toString(size));
91 assertThatIllegalArgumentException()
92 .isThrownBy(request::execute)
93 .withMessage("Size must be greater than 0");
97 public void verify_example() throws SQLException {
99 // 3 branches, 2 migrated
100 ComponentDto project = dbTester.components().insertPrivateProject();
101 ComponentDto branch1 = dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
102 dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH));
103 dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), project.branchUuid(), true);
104 dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), branch1.branchUuid(), true);
105 // 2 portfolios, 1 migrated
106 PortfolioDto portfolio1 = dbTester.components().insertPrivatePortfolioDto("name1");
107 dbTester.components().insertPrivatePortfolioDto("name2");
108 dbTester.getDbClient().portfolioDao().updateMeasuresMigrated(dbTester.getSession(), portfolio1.getUuid(), true);
109 dbTester.getSession().commit();
111 TestResponse response = tester.newRequest()
114 assertJson(response.getInput()).isSimilarTo(getClass().getResource("example-migrate_measures.json"));
118 public void does_not_migrate_portfolios_if_measures_are_not_finished() throws SQLException {
121 ComponentDto project = dbTester.components().insertPrivateProject();
122 ComponentDto branch = dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
123 dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH));
125 TestResponse response = tester.newRequest()
126 .setParam(PARAM_SIZE, "2")
129 assertThat(GSON.fromJson(response.getInput(), ActionResponse.class))
130 .isEqualTo(new ActionResponse("success", "2 branches or portfolios migrated", 3, 3, 0, 0));
131 verify(measuresMigration).migrate(List.of(project.uuid(), branch.uuid()));
132 verifyNoInteractions(portfoliosMigration);
136 public void migrate_portfolios_to_reach_the_requested_size() throws SQLException {
140 ComponentDto project = dbTester.components().insertPrivateProject();
142 PortfolioDto portfolio1 = dbTester.components().insertPrivatePortfolioDto("name1");
143 dbTester.components().insertPrivatePortfolioDto("name2");
145 TestResponse response = tester.newRequest()
146 .setParam(PARAM_SIZE, "2")
149 assertThat(GSON.fromJson(response.getInput(), ActionResponse.class))
150 .isEqualTo(new ActionResponse("success", "2 branches or portfolios migrated", 1, 1, 2, 2));
151 verify(measuresMigration).migrate(List.of(project.uuid()));
152 verify(portfoliosMigration).migrate(List.of(portfolio1.getUuid()));
156 public void migrate_portfolios_only_if_measures_are_done() throws SQLException {
158 // 2 branches, all migrated
159 ComponentDto project = dbTester.components().insertPrivateProject();
160 ComponentDto branch1 = dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
161 dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), project.branchUuid(), true);
162 dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), branch1.branchUuid(), true);
163 // 2 portfolios, 1 migrated
164 PortfolioDto portfolio1 = dbTester.components().insertPrivatePortfolioDto("name1");
165 PortfolioDto portfolio2 = dbTester.components().insertPrivatePortfolioDto("name2");
166 dbTester.getDbClient().portfolioDao().updateMeasuresMigrated(dbTester.getSession(), portfolio1.getUuid(), true);
169 TestResponse response = tester.newRequest()
170 .setParam(PARAM_SIZE, "2")
173 assertThat(GSON.fromJson(response.getInput(), ActionResponse.class))
174 .isEqualTo(new ActionResponse("success", "1 branches or portfolios migrated", 0, 2, 1, 2));
175 verifyNoInteractions(measuresMigration);
176 verify(portfoliosMigration).migrate(List.of(portfolio2.getUuid()));
180 public void does_nothing_if_migration_is_finished() throws SQLException {
182 // 2 branches, all migrated
183 ComponentDto project = dbTester.components().insertPrivateProject();
184 ComponentDto branch1 = dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
185 dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), project.branchUuid(), true);
186 dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), branch1.branchUuid(), true);
187 // 2 portfolios, all migrated
188 PortfolioDto portfolio1 = dbTester.components().insertPrivatePortfolioDto("name1");
189 PortfolioDto portfolio2 = dbTester.components().insertPrivatePortfolioDto("name2");
190 dbTester.getDbClient().portfolioDao().updateMeasuresMigrated(dbTester.getSession(), portfolio1.getUuid(), true);
191 dbTester.getDbClient().portfolioDao().updateMeasuresMigrated(dbTester.getSession(), portfolio2.getUuid(), true);
194 TestResponse response = tester.newRequest()
195 .setParam(PARAM_SIZE, "2")
198 assertThat(GSON.fromJson(response.getInput(), ActionResponse.class))
199 .isEqualTo(new ActionResponse("success", "0 branches or portfolios migrated", 0, 2, 0, 2));
200 verifyNoInteractions(measuresMigration, portfoliosMigration);
203 private void enableMigration() throws SQLException {
204 Database database = dbTester.getDbClient().getDatabase();
205 new AddMeasuresMigratedColumnToProjectBranchesTable(database).execute();
206 new AddMeasuresMigratedColumnToPortfoliosTable(database).execute();
207 dbTester.getDbClient().propertiesDao().saveProperty(new PropertyDto().setKey(SYSTEM_MEASURES_MIGRATION_ENABLED).setValue("true"));
211 public void throws_ForbiddenException_if_user_is_not_logged_in() {
212 userSessionRule.anonymous();
214 TestRequest request = tester.newRequest();
216 assertThatExceptionOfType(ForbiddenException.class)
217 .isThrownBy(request::execute);
221 public void throws_ForbiddenException_if_user_is_not_system_admin() {
222 userSessionRule.logIn();
224 TestRequest request = tester.newRequest();
226 assertThatExceptionOfType(ForbiddenException.class)
227 .isThrownBy(request::execute);
230 private record ActionResponse(String status, String message, int remainingBranches, int totalBranches, int remainingPortfolios,
231 int totalPortfolios) {