123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- /*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.db.migration.step;
-
- import java.sql.SQLException;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.Iterator;
- import java.util.List;
- import org.junit.Rule;
- import org.junit.Test;
- import org.sonar.api.utils.log.LogTester;
- import org.sonar.api.utils.log.LoggerLevel;
- import org.sonar.server.platform.db.migration.engine.MigrationContainer;
- import org.sonar.server.platform.db.migration.engine.SimpleMigrationContainer;
- import org.sonar.server.platform.db.migration.history.MigrationHistory;
-
- import static com.google.common.base.Preconditions.checkState;
- import static java.util.Arrays.asList;
- import static org.assertj.core.api.Assertions.assertThat;
- import static org.assertj.core.api.Assertions.fail;
- import static org.mockito.Mockito.mock;
-
- public class MigrationStepsExecutorImplTest {
- @Rule
- public LogTester logTester = new LogTester();
-
- private MigrationContainer migrationContainer = new SimpleMigrationContainer();
- private MigrationHistory migrationHistor = mock(MigrationHistory.class);
- private MigrationStepsExecutorImpl underTest = new MigrationStepsExecutorImpl(migrationContainer, migrationHistor);
-
- @Test
- public void execute_does_not_fail_when_stream_is_empty_and_log_start_stop_INFO() {
- underTest.execute(Collections.emptyList());
-
- assertThat(logTester.logs()).hasSize(2);
- assertLogLevel(LoggerLevel.INFO, "Executing DB migrations...", "Executed DB migrations: success | time=");
- }
-
- @Test
- public void execute_fails_with_ISE_if_no_instance_of_computation_step_exist_in_container() {
- List<RegisteredMigrationStep> steps = asList(registeredStepOf(1, MigrationStep1.class));
-
- try {
- underTest.execute(steps);
- fail("execute should have thrown a IllegalStateException");
- } catch (IllegalStateException e) {
- assertThat(e).hasMessage("Can not find instance of " + MigrationStep1.class);
- } finally {
- assertThat(logTester.logs()).hasSize(2);
- assertLogLevel(LoggerLevel.INFO, "Executing DB migrations...");
- assertLogLevel(LoggerLevel.ERROR, "Executed DB migrations: failure | time=");
- }
- }
-
- private void assertLogLevel(LoggerLevel level, String... expected) {
- List<String> logs = logTester.logs(level);
- assertThat(logs).hasSize(expected.length);
- Iterator<String> iterator = logs.iterator();
- Arrays.stream(expected).forEachOrdered(log -> {
- if (log.endsWith(" | time=")) {
- assertThat(iterator.next()).startsWith(log);
- } else {
- assertThat(iterator.next()).isEqualTo(log);
- }
- });
- }
-
- @Test
- public void execute_execute_the_instance_of_type_specified_in_step_in_stream_order() {
- migrationContainer.add(MigrationStep1.class, MigrationStep2.class, MigrationStep3.class);
-
- underTest.execute(asList(
- registeredStepOf(1, MigrationStep2.class),
- registeredStepOf(2, MigrationStep1.class),
- registeredStepOf(3, MigrationStep3.class)));
-
- assertThat(SingleCallCheckerMigrationStep.calledSteps)
- .containsExactly(MigrationStep2.class, MigrationStep1.class, MigrationStep3.class);
- assertThat(logTester.logs()).hasSize(8);
- assertLogLevel(LoggerLevel.INFO,
- "Executing DB migrations...",
- "#1 '1-MigrationStep2'...",
- "#1 '1-MigrationStep2': success | time=",
- "#2 '2-MigrationStep1'...",
- "#2 '2-MigrationStep1': success | time=",
- "#3 '3-MigrationStep3'...",
- "#3 '3-MigrationStep3': success | time=",
- "Executed DB migrations: success | time=");
-
- assertThat(migrationContainer.getComponentByType(MigrationStep1.class).isCalled()).isTrue();
- assertThat(migrationContainer.getComponentByType(MigrationStep2.class).isCalled()).isTrue();
- assertThat(migrationContainer.getComponentByType(MigrationStep3.class).isCalled()).isTrue();
- }
-
- @Test
- public void execute_throws_MigrationStepExecutionException_on_first_failing_step_execution_throws_SQLException() {
- migrationContainer.add(MigrationStep2.class, SqlExceptionFailingMigrationStep.class, MigrationStep3.class);
- List<RegisteredMigrationStep> steps = asList(
- registeredStepOf(1, MigrationStep2.class),
- registeredStepOf(2, SqlExceptionFailingMigrationStep.class),
- registeredStepOf(3, MigrationStep3.class));
-
- try {
- underTest.execute(steps);
- fail("a MigrationStepExecutionException should have been thrown");
- } catch (MigrationStepExecutionException e) {
- assertThat(e).hasMessage("Execution of migration step #2 '2-SqlExceptionFailingMigrationStep' failed");
- assertThat(e).hasCause(SqlExceptionFailingMigrationStep.THROWN_EXCEPTION);
- } finally {
- assertThat(logTester.logs()).hasSize(6);
- assertLogLevel(LoggerLevel.INFO,
- "Executing DB migrations...",
- "#1 '1-MigrationStep2'...",
- "#1 '1-MigrationStep2': success | time=",
- "#2 '2-SqlExceptionFailingMigrationStep'...");
- assertLogLevel(LoggerLevel.ERROR,
- "#2 '2-SqlExceptionFailingMigrationStep': failure | time=",
- "Executed DB migrations: failure | time=");
- }
- }
-
- @Test
- public void execute_throws_MigrationStepExecutionException_on_first_failing_step_execution_throws_any_exception() {
- migrationContainer.add(MigrationStep2.class, RuntimeExceptionFailingMigrationStep.class, MigrationStep3.class);
-
- List<RegisteredMigrationStep> steps = asList(
- registeredStepOf(1, MigrationStep2.class),
- registeredStepOf(2, RuntimeExceptionFailingMigrationStep.class),
- registeredStepOf(3, MigrationStep3.class));
-
- try {
- underTest.execute(steps);
- fail("should throw MigrationStepExecutionException");
- } catch (MigrationStepExecutionException e) {
- assertThat(e).hasMessage("Execution of migration step #2 '2-RuntimeExceptionFailingMigrationStep' failed");
- assertThat(e.getCause()).isSameAs(RuntimeExceptionFailingMigrationStep.THROWN_EXCEPTION);
- }
- }
-
- private static RegisteredMigrationStep registeredStepOf(int migrationNumber, Class<? extends MigrationStep> migrationStep1Class) {
- return new RegisteredMigrationStep(migrationNumber, migrationNumber + "-" + migrationStep1Class.getSimpleName(), migrationStep1Class);
- }
-
- private static abstract class SingleCallCheckerMigrationStep implements MigrationStep {
- private static List<Class<? extends MigrationStep>> calledSteps = new ArrayList<>();
- private boolean called = false;
-
- @Override
- public void execute() {
- checkState(!called, "execute must not be called twice");
- this.called = true;
- calledSteps.add(getClass());
- }
-
- public boolean isCalled() {
- return called;
- }
-
- public static List<Class<? extends MigrationStep>> getCalledSteps() {
- return calledSteps;
- }
- }
-
- public static final class MigrationStep1 extends SingleCallCheckerMigrationStep {
-
- }
-
- public static final class MigrationStep2 extends SingleCallCheckerMigrationStep {
-
- }
-
- public static final class MigrationStep3 extends SingleCallCheckerMigrationStep {
-
- }
-
- public static class SqlExceptionFailingMigrationStep implements MigrationStep {
- private static final SQLException THROWN_EXCEPTION = new SQLException("Faking SQL exception in MigrationStep#execute()");
-
- @Override
- public void execute() throws SQLException {
- throw THROWN_EXCEPTION;
- }
- }
-
- public static class RuntimeExceptionFailingMigrationStep implements MigrationStep {
- private static final RuntimeException THROWN_EXCEPTION = new RuntimeException("Faking failing migration step");
-
- @Override
- public void execute() {
- throw THROWN_EXCEPTION;
- }
- }
-
- }
|