From 05745b33dda077452d4faf019d1592da7e5b28ca Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Tue, 7 Apr 2015 14:22:48 +0200 Subject: [PATCH] SONAR-6366 add new WS to migrate SonarQube DB WS is available on /api/system/migrate_db --- .../server/platform/ServerComponents.java | 17 +- .../platform/ws/MigrateDbSystemWsAction.java | 183 +++++++++++++++ .../server/platform/ws/ServerWsAction.java | 30 +++ .../java/org/sonar/server/ui/JRubyFacade.java | 24 +- .../platform/ws/example-migrate_db.json | 6 + ...DatabaseMigrationConcurrentAccessTest.java | 13 +- .../ws/MigrateDbSystemWsActionTest.java | 222 ++++++++++++++++++ .../server/platform/ws/ServerWsTest.java | 1 + 8 files changed, 471 insertions(+), 25 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/ws/MigrateDbSystemWsAction.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/ws/ServerWsAction.java create mode 100644 server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-migrate_db.json create mode 100644 server/sonar-server/src/test/java/org/sonar/server/platform/ws/MigrateDbSystemWsActionTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 9f7bb52c222..57ac3c6736f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -19,7 +19,13 @@ */ package org.sonar.server.platform; -import com.google.common.collect.Lists; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Properties; + +import javax.annotation.Nullable; + import org.sonar.api.config.EmailSettings; import org.sonar.api.issue.action.Actions; import org.sonar.api.platform.ComponentContainer; @@ -206,6 +212,7 @@ import org.sonar.server.platform.monitoring.PluginsMonitor; import org.sonar.server.platform.monitoring.SonarQubeMonitor; import org.sonar.server.platform.monitoring.SystemMonitor; import org.sonar.server.platform.ws.L10nWs; +import org.sonar.server.platform.ws.MigrateDbSystemWsAction; import org.sonar.server.platform.ws.ServerWs; import org.sonar.server.platform.ws.SystemInfoWsAction; import org.sonar.server.platform.ws.SystemRestartWsAction; @@ -390,12 +397,7 @@ import org.sonar.server.view.index.ViewIndexer; import org.sonar.server.ws.ListingWs; import org.sonar.server.ws.WebServiceEngine; -import javax.annotation.Nullable; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Properties; +import com.google.common.collect.Lists; class ServerComponents { @@ -889,6 +891,7 @@ class ServerComponents { SystemRestartWsAction.class, SystemInfoWsAction.class, UpgradesSystemWsAction.class, + MigrateDbSystemWsAction.class, SystemWs.class, SystemMonitor.class, SonarQubeMonitor.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/MigrateDbSystemWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/MigrateDbSystemWsAction.java new file mode 100644 index 00000000000..6d477d7e9e4 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/MigrateDbSystemWsAction.java @@ -0,0 +1,183 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import com.google.common.io.Resources; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.core.persistence.Database; +import org.sonar.core.persistence.DatabaseVersion; +import org.sonar.server.db.migrations.DatabaseMigration; + +import static org.sonar.server.db.migrations.DatabaseMigration.Status.NONE; +import static org.sonar.server.db.migrations.DatabaseMigration.Status.RUNNING; +import static org.sonar.server.db.migrations.DatabaseMigration.Status.SUCCEEDED; + +/** + * Implementation of the {@code migrate_db} action for the System WebService. + */ +public class MigrateDbSystemWsAction implements SystemWsAction { + + private static final String UNSUPPORTED_DATABASE_MIGRATION_STATUS = "Unsupported DatabaseMigration status"; + private static final String MESSAGE_STATUS_NONE = "Database is up-to-date, no migration needed."; + private static final String MESSAGE_STATUS_RUNNING = "Database migration is running."; + private static final String MESSAGE_STATUS_SUCCEEDED = "Migration succeeded."; + private static final String MESSAGE_STATUS_FAILED = "Migration failed: %s.
Please check logs."; + + private final DatabaseVersion databaseVersion; + private final DatabaseMigration databaseMigration; + private final Database database; + + public MigrateDbSystemWsAction(DatabaseVersion databaseVersion, + Database database, + DatabaseMigration databaseMigration) { + this.databaseVersion = databaseVersion; + this.database = database; + this.databaseMigration = databaseMigration; + } + + @Override + public void define(WebService.NewController controller) { + controller.createAction("migrate_db") + .setDescription("Migrate the database to match the current version of SonarQube." + + "
" + + "Sending a POST request to this URL starts the DB migration. " + + "It is strongly advised to make a database backup before invoking this WS." + + "
" + + "State values are:" + + "") + .setSince("5.2") + .setPost(true) + .setHandler(this) + .setResponseExample(Resources.getResource(this.getClass(), "example-migrate_db.json")); + } + + @Override + public void handle(Request request, Response response) throws Exception { + Integer currentVersion = databaseVersion.getVersion(); + if (currentVersion == null) { + throw new IllegalStateException("Version can not be retrieved from Database. Database is either blank or corrupted"); + } + + if (currentVersion >= DatabaseVersion.LAST_VERSION) { + writeResponse(response, databaseMigration); + } else if (!database.getDialect().supportsMigration()) { + writeNotSupportedResponse(response); + } else { + switch (databaseMigration.status()) { + case RUNNING: + case FAILED: + case SUCCEEDED: + writeResponse(response, databaseMigration); + break; + case NONE: + databaseMigration.startIt(); + writeNoneResponse(response, databaseMigration); + break; + default: + throw new IllegalArgumentException(UNSUPPORTED_DATABASE_MIGRATION_STATUS); + } + } + } + + private void writeNotSupportedResponse(Response response) { + JsonWriter jsonWriter = response.newJsonWriter(); + jsonWriter.beginObject() + .prop("operational", false) + .prop("state", statusToJson(NONE)) + .prop("message", "Upgrade is not supported. Please use a production-ready database.") + .endObject(); + jsonWriter.close(); + } + + private void writeNoneResponse(Response response, DatabaseMigration databaseMigration) { + JsonWriter jsonWriter = response.newJsonWriter(); + jsonWriter.beginObject() + .prop("operational", false) + .prop("state", statusToJson(RUNNING)) + .prop("message", MESSAGE_STATUS_RUNNING) + .propDateTime("startedAt", databaseMigration.startedAt()) + .endObject(); + jsonWriter.close(); + } + + private void writeResponse(Response response, DatabaseMigration databaseMigration) { + JsonWriter jsonWriter = response.newJsonWriter(); + jsonWriter.beginObject() + .prop("operational", isOperational(databaseMigration)) + .prop("state", statusToJson(databaseMigration.status())) + .prop("message", buildMessage(databaseMigration)) + .propDateTime("startedAt", databaseMigration.startedAt()) + .endObject(); + jsonWriter.close(); + } + + private String statusToJson(DatabaseMigration.Status status) { + switch (status) { + case NONE: + return "NO_MIGRATION"; + case RUNNING: + return "MIGRATION_RUNNING"; + case FAILED: + return "MIGRATION_FAILED"; + case SUCCEEDED: + return "MIGRATION_SUCCEEDED"; + default: + throw new IllegalArgumentException( + "Unsupported DatabaseMigration.Status " + status + " can not be converted to JSON value"); + } + } + + private static boolean isOperational(DatabaseMigration databaseMigration) { + return databaseMigration.status() == NONE || databaseMigration.status() == SUCCEEDED; + } + + private static String buildMessage(DatabaseMigration databaseMigration) { + switch (databaseMigration.status()) { + case NONE: + return MESSAGE_STATUS_NONE; + case RUNNING: + return MESSAGE_STATUS_RUNNING; + case SUCCEEDED: + return MESSAGE_STATUS_SUCCEEDED; + case FAILED: + return String.format(MESSAGE_STATUS_FAILED, failureMessage(databaseMigration)); + default: + return UNSUPPORTED_DATABASE_MIGRATION_STATUS; + } + } + + private static String failureMessage(DatabaseMigration databaseMigration) { + Throwable failureError = databaseMigration.failureError(); + if (failureError == null) { + return "No failure error"; + } + return failureError.getMessage(); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/ServerWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/ServerWsAction.java new file mode 100644 index 00000000000..2e22c06bde2 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/ServerWsAction.java @@ -0,0 +1,30 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.platform.ws; + +import org.sonar.server.ws.WsAction; + +/** + * Marker interface for actions of the WS on URL api/server. + */ +public interface ServerWsAction extends WsAction { + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index eceb52ec6d3..fd23abd532c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -19,6 +19,14 @@ */ package org.sonar.server.ui; +import java.net.InetAddress; +import java.sql.Connection; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.sonar.api.config.License; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.Settings; @@ -46,23 +54,17 @@ import org.sonar.server.platform.Platform; import org.sonar.server.platform.ServerIdGenerator; import org.sonar.server.platform.ServerSettings; import org.sonar.server.platform.SettingsChangeNotifier; -import org.sonar.server.plugins.*; +import org.sonar.server.plugins.InstalledPluginReferentialFactory; +import org.sonar.server.plugins.PluginDownloader; +import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.plugins.ServerPluginRepository; +import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.server.rule.RuleRepositories; import org.sonar.server.user.NewUserNotifier; import org.sonar.updatecenter.common.PluginReferential; import org.sonar.updatecenter.common.UpdateCenter; import org.sonar.updatecenter.common.Version; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.net.InetAddress; -import java.sql.Connection; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; - import static com.google.common.collect.Lists.newArrayList; public final class JRubyFacade { diff --git a/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-migrate_db.json b/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-migrate_db.json new file mode 100644 index 00000000000..947e9a4d55b --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-migrate_db.json @@ -0,0 +1,6 @@ +{ + "operational": false, + "state": "MIGRATION_RUNNING", + "message": "Database migration is running.", + "startedAt": "2015-02-23T18:54:23+0100" +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/PlatformDatabaseMigrationConcurrentAccessTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/PlatformDatabaseMigrationConcurrentAccessTest.java index 09d00c5695f..d7d212b3c52 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/PlatformDatabaseMigrationConcurrentAccessTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/PlatformDatabaseMigrationConcurrentAccessTest.java @@ -20,16 +20,15 @@ package org.sonar.server.db.migrations; import com.google.common.base.Throwables; -import org.junit.After; -import org.junit.Test; -import org.sonar.server.ruby.RubyBridge; -import org.sonar.server.ruby.RubyDatabaseMigration; - import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.junit.After; +import org.junit.Test; +import org.sonar.server.ruby.RubyBridge; +import org.sonar.server.ruby.RubyDatabaseMigration; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -65,7 +64,7 @@ public class PlatformDatabaseMigrationConcurrentAccessTest { public void trigger() { triggerCount.incrementAndGet(); try { - Thread.currentThread().sleep(200); + Thread.currentThread().sleep(1000); } catch (InterruptedException e) { Throwables.propagate(e); } @@ -86,7 +85,7 @@ public class PlatformDatabaseMigrationConcurrentAccessTest { pool.submit(new CallStartit()); pool.submit(new CallStartit()); - pool.awaitTermination(3, TimeUnit.SECONDS); + pool.awaitTermination(2, TimeUnit.SECONDS); assertThat(triggerCount.get()).isEqualTo(1); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/MigrateDbSystemWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/MigrateDbSystemWsActionTest.java new file mode 100644 index 00000000000..df5902a66af --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/MigrateDbSystemWsActionTest.java @@ -0,0 +1,222 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import com.google.common.collect.ImmutableList; +import java.util.Arrays; +import java.util.Date; +import javax.annotation.Nullable; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.server.ws.Request; +import org.sonar.api.utils.DateUtils; +import org.sonar.core.persistence.Database; +import org.sonar.core.persistence.DatabaseVersion; +import org.sonar.core.persistence.dialect.Dialect; +import org.sonar.server.db.migrations.DatabaseMigration; +import org.sonar.server.db.migrations.DatabaseMigration.Status; +import org.sonar.server.ws.WsTester; + +import static com.google.common.base.Predicates.in; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Iterables.filter; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonar.server.db.migrations.DatabaseMigration.Status.FAILED; +import static org.sonar.server.db.migrations.DatabaseMigration.Status.NONE; +import static org.sonar.server.db.migrations.DatabaseMigration.Status.RUNNING; +import static org.sonar.server.db.migrations.DatabaseMigration.Status.SUCCEEDED; +import static org.sonar.test.JsonAssert.assertJson; + +public class MigrateDbSystemWsActionTest { + + private static final String UPTODATE_MSG = "Database is up-to-date, no migration needed."; + private static final String MIG_NOT_SUPPORTED_MSG = "Upgrade is not supported. Please use a production-ready database."; + private static final String RUNNING_MSG = "Database migration is running."; + private static final Date SOME_DATE = new Date(); + private static final String SOME_THROWABLE_MSG = "blablabla pop !"; + private static final String DEFAULT_ERROR_MSG = "No failure error"; + private static final String MIG_SUCCESS_MSG = "Migration succeeded."; + private static final int CURRENT_VERSION = DatabaseVersion.LAST_VERSION; + private static final int OLD_VERSION = CURRENT_VERSION - 1; + private static final int NEWER_VERSION = CURRENT_VERSION + 1; + private static final String STATUS_NONE = "NO_MIGRATION"; + private static final String STATUS_RUNNING = "MIGRATION_RUNNING"; + private static final String STATUS_SUCCEEDED = "MIGRATION_SUCCEEDED"; + private static final String STATUS_FAILED = "MIGRATION_FAILED"; + + DatabaseVersion databaseVersion = mock(DatabaseVersion.class); + Database database = mock(Database.class); + Dialect dialect = mock(Dialect.class); + DatabaseMigration databaseMigration = mock(DatabaseMigration.class); + MigrateDbSystemWsAction underTest = new MigrateDbSystemWsAction(databaseVersion, database, databaseMigration); + + Request request = mock(Request.class); + WsTester.TestResponse response = new WsTester.TestResponse(); + + @Before + public void wireMocksTogether() throws Exception { + when(database.getDialect()).thenReturn(dialect); + } + + @Test(expected = IllegalStateException.class) + public void ISE_is_thrown_when_version_can_not_be_retrieved_from_database() throws Exception { + when(databaseVersion.getVersion()).thenReturn(null); + + underTest.handle(request, response); + } + + @Test + public void verify_example() throws Exception { + when(databaseVersion.getVersion()).thenReturn(OLD_VERSION); + when(dialect.supportsMigration()).thenReturn(true); + when(databaseMigration.status()).thenReturn(RUNNING); + when(databaseMigration.startedAt()).thenReturn(DateUtils.parseDateTime("2015-02-23T18:54:23+0100")); + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo(getClass().getResource("example-migrate_db.json")); + } + + @Test + public void msg_is_operational_and_state_from_databasemigration_when_databaseversion_is_equal_to_currentversion() throws Exception { + when(databaseVersion.getVersion()).thenReturn(CURRENT_VERSION); + when(databaseMigration.status()).thenReturn(NONE); + + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo(expectedResponse(true, STATUS_NONE, UPTODATE_MSG)); + } + + // this test will raise a IllegalArgumentException when an unsupported value is added to the Status enum + @Test + public void defensive_test_all_values_of_Status_must_be_supported() throws Exception { + for (Status status : filter(Arrays.asList(Status.values()), not(in(ImmutableList.of(NONE, RUNNING, FAILED, SUCCEEDED))))) { + when(databaseVersion.getVersion()).thenReturn(CURRENT_VERSION); + when(databaseMigration.status()).thenReturn(status); + + underTest.handle(request, response); + } + } + + @Test + public void msg_is_operational_and_state_from_databasemigration_when_databaseversion_greater_than_currentversion() throws Exception { + when(databaseVersion.getVersion()).thenReturn(NEWER_VERSION); + when(databaseMigration.status()).thenReturn(NONE); + + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo(expectedResponse(true, STATUS_NONE, UPTODATE_MSG)); + } + + @Test + public void msg_is_not_operational_and_state_is_NONE_with_specific_msg_when_version_is_less_than_current_version_and_dialect_does_not_support_migration() throws Exception { + when(databaseVersion.getVersion()).thenReturn(OLD_VERSION); + when(dialect.supportsMigration()).thenReturn(false); + + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo(expectedResponse(false, STATUS_NONE, MIG_NOT_SUPPORTED_MSG)); + } + + @Test + public void msg_is_not_operational_and_state_from_databasemigration_when_dbmigration_status_is_RUNNING() throws Exception { + when(databaseVersion.getVersion()).thenReturn(OLD_VERSION); + when(dialect.supportsMigration()).thenReturn(true); + when(databaseMigration.status()).thenReturn(RUNNING); + when(databaseMigration.startedAt()).thenReturn(SOME_DATE); + + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo(expectedResponse(false, STATUS_RUNNING, RUNNING_MSG, SOME_DATE)); + } + + @Test + public void msg_is_not_operational_and_state_from_databasemigration_and_msg_includes_error_when_dbmigration_status_is_FAILED() throws Exception { + when(databaseVersion.getVersion()).thenReturn(OLD_VERSION); + when(dialect.supportsMigration()).thenReturn(true); + when(databaseMigration.status()).thenReturn(FAILED); + when(databaseMigration.startedAt()).thenReturn(SOME_DATE); + when(databaseMigration.failureError()).thenReturn(new UnsupportedOperationException(SOME_THROWABLE_MSG)); + + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo(expectedResponse(false, STATUS_FAILED, failedMsg(SOME_THROWABLE_MSG), SOME_DATE)); + } + + @Test + public void msg_is_not_operational_and_state_from_databasemigration_and_msg_has_default_msg_when_dbmigration_status_is_FAILED() throws Exception { + when(databaseVersion.getVersion()).thenReturn(OLD_VERSION); + when(dialect.supportsMigration()).thenReturn(true); + when(databaseMigration.status()).thenReturn(FAILED); + when(databaseMigration.startedAt()).thenReturn(SOME_DATE); + when(databaseMigration.failureError()).thenReturn(null); // no failure throwable caught + + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo(expectedResponse(false, STATUS_FAILED, failedMsg(DEFAULT_ERROR_MSG), SOME_DATE)); + } + + @Test + public void msg_is_operational_and_state_from_databasemigration_and_msg_has_default_msg_when_dbmigration_status_is_FAILED() throws Exception { + when(databaseVersion.getVersion()).thenReturn(OLD_VERSION); + when(dialect.supportsMigration()).thenReturn(true); + when(databaseMigration.status()).thenReturn(SUCCEEDED); + when(databaseMigration.startedAt()).thenReturn(SOME_DATE); + + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo(expectedResponse(true, STATUS_SUCCEEDED, MIG_SUCCESS_MSG, SOME_DATE)); + } + + @Test + public void start_migration_and_return_msg_is_not_operational_and_state_from_databasemigration_when_dbmigration_status_is_NONE() throws Exception { + when(databaseVersion.getVersion()).thenReturn(OLD_VERSION); + when(dialect.supportsMigration()).thenReturn(true); + when(databaseMigration.status()).thenReturn(NONE); + when(databaseMigration.startedAt()).thenReturn(SOME_DATE); + + underTest.handle(request, response); + + verify(databaseMigration).startIt(); + assertJson(response.outputAsString()).isSimilarTo(expectedResponse(false, STATUS_RUNNING, RUNNING_MSG, SOME_DATE)); + } + + private static String failedMsg(@Nullable String t) { + return "Migration failed: " + t + ".
Please check logs."; + } + + private static String expectedResponse(boolean operational, String status, String msg) { + return "{" + + "\"operational\":" + operational + "," + + "\"state\":\"" + status + "\"," + + "\"message\":\"" + msg + "\"" + + "}"; + } + + private static String expectedResponse(boolean operational, String status, String msg, Date date) { + return "{" + + "\"operational\":" + operational + "," + + "\"state\":\"" + status + "\"," + + "\"message\":\"" + msg + "\"," + + "\"startedAt\":\"" + DateUtils.formatDateTime(date) + "\"" + + "}"; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/ServerWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/ServerWsTest.java index 7fcfceda62b..5e86ae42024 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/ServerWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/ServerWsTest.java @@ -61,4 +61,5 @@ public class ServerWsTest { assertThat(action.responseExampleAsString()).isNotEmpty(); assertThat(action.params()).hasSize(1); } + } -- 2.39.5