*/
package org.sonar.server.platform.db.migration;
+import java.time.Instant;
import java.util.Date;
-import javax.annotation.CheckForNull;
+import java.util.Optional;
public interface DatabaseMigrationState {
/**
* The time and day the last migration was started.
* <p>
- * If no migration was ever started, the returned date is {@code null}.
+ * If no migration was ever started, the returned date is empty.
* </p>
*
- * @return a {@link Date} or {@code null}
+ * @return a {@link Date} if present
*/
- @CheckForNull
- Date getStartedAt();
+ Optional<Instant> getStartedAt();
/**
* The error of the last migration if it failed.
*
- * @return a {@link Throwable} or {@code null}
+ * @return a {@link Throwable} if present.
*/
- @CheckForNull
- Throwable getError();
+ Optional<Throwable> getError();
/**
* The amount of migrations already completed.
int getTotalMigrations();
/**
- * The expected finish timestamp of the migration.
+ * The expected finish timestamp of the migration if known.
*/
- Date getExpectedFinishDate();
+ Optional<Instant> getExpectedFinishDate(Instant now);
}
*/
package org.sonar.server.platform.db.migration;
-import java.util.Date;
-import javax.annotation.CheckForNull;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Optional;
import javax.annotation.Nullable;
/**
public class DatabaseMigrationStateImpl implements MutableDatabaseMigrationState {
private Status status = Status.NONE;
@Nullable
- private Date startedAt = null;
+ private Instant startedAt = null;
@Nullable
private Throwable error = null;
private int completedMigrations = 0;
private int totalMigrations = 0;
- @Nullable
- private Date completionExpectedAt = null;
@Override
public Status getStatus() {
}
@Override
- @CheckForNull
- public Date getStartedAt() {
- return startedAt;
+ public Optional<Instant> getStartedAt() {
+ return Optional.ofNullable(startedAt);
}
@Override
- public void setStartedAt(@Nullable Date startedAt) {
+ public void setStartedAt(Instant startedAt) {
this.startedAt = startedAt;
}
@Override
- @CheckForNull
- public Throwable getError() {
- return error;
+ public Optional<Throwable> getError() {
+ return Optional.ofNullable(error);
}
@Override
public void incrementCompletedMigrations() {
completedMigrations++;
- updateExpectedFinishDate();
}
@Override
}
@Override
- public Date getExpectedFinishDate() {
- return completionExpectedAt;
- }
-
- private void updateExpectedFinishDate() {
- // Here the logic is to calculate the expected finish date based on the current time and the number of migrations completed and total
- // migrations
- this.completionExpectedAt = new Date();
+ public Optional<Instant> getExpectedFinishDate(Instant now) {
+ if (startedAt == null || totalMigrations == 0 || completedMigrations == 0) {
+ return Optional.empty();
+ }
+ Duration elapsed = Duration.between(startedAt, now);
+ double progress = (double) completedMigrations / totalMigrations;
+ return Optional.of(startedAt.plusMillis((long) (elapsed.toMillis() / progress)));
}
-
}
*/
package org.sonar.server.platform.db.migration;
-import java.util.Date;
-import javax.annotation.Nullable;
+import java.time.Instant;
public interface MutableDatabaseMigrationState extends DatabaseMigrationState {
void setStatus(Status status);
- void setStartedAt(@Nullable Date startedAt);
+ void setStartedAt(Instant startedAt);
- void setError(@Nullable Throwable error);
+ void setError(Throwable error);
void incrementCompletedMigrations();
*/
package org.sonar.server.platform.db.migration;
-import java.util.Date;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.within;
class DatabaseMigrationStateImplTest {
- private DatabaseMigrationStateImpl underTest = new DatabaseMigrationStateImpl();
+ private final DatabaseMigrationStateImpl underTest = new DatabaseMigrationStateImpl();
@Test
void getStatus_whenComponentIsCreated_shouldReturnNONE() {
}
@Test
- void getStartedAt_whenComponentIsCreated_shouldReturnNull() {
- assertThat(underTest.getStartedAt()).isNull();
+ void getStartedAt_whenComponentIsCreated_shouldNotBePresent() {
+ assertThat(underTest.getStartedAt()).isEmpty();
}
@Test
void getStartedAt_shouldReturnArgumentOfSetStartedAt() {
- Date expected = new Date();
+ Instant expected = Instant.now();
underTest.setStartedAt(expected);
- assertThat(underTest.getStartedAt()).isSameAs(expected);
+ assertThat(underTest.getStartedAt()).get().isSameAs(expected);
}
@Test
- void getError_whenComponentIsCreated_shouldReturnNull() {
- assertThat(underTest.getError()).isNull();
+ void getError_whenComponentIsCreated_shouldNotBePresent() {
+ assertThat(underTest.getError()).isEmpty();
}
@Test
RuntimeException expected = new RuntimeException();
underTest.setError(expected);
- assertThat(underTest.getError()).isSameAs(expected);
+ assertThat(underTest.getError()).get().isSameAs(expected);
}
@Test
}
@Test
- void incrementCompletedMigrations_shouldUpdateExpectedFinishDate() {
- Date startDate = new Date();
+ void when_noStartedMigration_expectedFinishDateShouldBeAbsent() {
+ Instant startDate = Instant.now();
+ Instant later = startDate.plus(1, ChronoUnit.MINUTES);
+ underTest.setTotalMigrations(2);
+
+ assertThat(underTest.getExpectedFinishDate(later)).isEmpty();
+ }
+
+ @Test
+ void when_noStepCompleted_expectedFinishDateShouldBeAbsent() {
+ Instant startDate = Instant.now();
+ Instant later = startDate.plus(1, ChronoUnit.MINUTES);
+
+ underTest.setStartedAt(startDate);
+ underTest.setTotalMigrations(2);
+
+ assertThat(underTest.getExpectedFinishDate(later)).isEmpty();
+ }
+
+ @Test
+ void when_StepCompleted_expectedFinishDateShouldBePresent() {
+ Instant startDate = Instant.now();
+ Instant later = startDate.plus(1, ChronoUnit.MINUTES);
+ Instant expectedEnd = startDate.plus(2, ChronoUnit.MINUTES);
+
+ underTest.setStartedAt(startDate);
+ underTest.setTotalMigrations(2);
underTest.incrementCompletedMigrations();
- // At the moment the expected finish date gets update with the timestamp of the last migration completed
- assertThat(underTest.getExpectedFinishDate()).isAfterOrEqualTo(startDate);
+ assertThat(underTest.getExpectedFinishDate(later)).get(InstanceOfAssertFactories.INSTANT)
+ .isCloseTo(expectedEnd, within(1, ChronoUnit.SECONDS));
}
-
}
*/
package org.sonar.server.platform.db.migration;
-import java.util.Date;
+import java.time.Instant;
import java.util.concurrent.Semaphore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private void doDatabaseMigration() {
migrationState.setStatus(Status.RUNNING);
- migrationState.setStartedAt(new Date());
+ migrationState.setStartedAt(Instant.now());
migrationState.setError(null);
Profiler profiler = Profiler.create(LOGGER);
try {
*/
package org.sonar.server.platform.db.migration;
-import java.util.Date;
+import java.time.Instant;
import org.junit.Test;
import org.mockito.InOrder;
import org.sonar.server.platform.Platform;
underTest.startIt();
assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.SUCCEEDED);
- assertThat(migrationState.getError()).isNull();
+ assertThat(migrationState.getError()).isEmpty();
assertThat(migrationState.getStartedAt()).isNotNull();
}
underTest.startIt();
assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.FAILED);
- assertThat(migrationState.getError()).isSameAs(AN_ERROR);
+ assertThat(migrationState.getError()).get().isSameAs(AN_ERROR);
assertThat(migrationState.getStartedAt()).isNotNull();
}
underTest.startIt();
assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.FAILED);
- assertThat(migrationState.getError()).isSameAs(AN_ERROR);
- Date firstStartDate = migrationState.getStartedAt();
- assertThat(firstStartDate).isNotNull();
+ assertThat(migrationState.getError()).get().isSameAs(AN_ERROR);
+ assertThat(migrationState.getStartedAt()).isPresent();
+ Instant firstStartDate = migrationState.getStartedAt().get();
mockMigrationDoesNothing();
underTest.startIt();
assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.SUCCEEDED);
- assertThat(migrationState.getError()).isNull();
- assertThat(migrationState.getStartedAt()).isNotSameAs(firstStartDate);
+ assertThat(migrationState.getError()).isEmpty();
+ assertThat(migrationState.getStartedAt())
+ .get()
+ .isNotSameAs(firstStartDate);
}
private void mockMigrationThrowsError() {
package org.sonar.server.v2.api.system.controller;
import io.swagger.v3.oas.annotations.Operation;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.Optional;
-import java.util.TimeZone;
import javax.annotation.Nullable;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.DatabaseMigrationState;
private final DatabaseMigrationState databaseMigrationState;
private final Database database;
- private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
-
public DatabaseMigrationsController(DatabaseVersion databaseVersion, DatabaseMigrationState databaseMigrationState, Database database) {
this.databaseVersion = databaseVersion;
this.databaseMigrationState = databaseMigrationState;
this.database = database;
- simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
@Operation(summary = "Gets the status of ongoing database migrations, if any", description = "Return the detailed status of ongoing database migrations" +
this(state.getStatus().toString(),
state.getCompletedMigrations(),
state.getTotalMigrations(),
- state.getStartedAt() != null ? simpleDateFormat.format(state.getStartedAt()) : null,
- state.getError() != null ? state.getError().getMessage() : state.getStatus().getMessage(),
- state.getExpectedFinishDate() != null ? simpleDateFormat.format(state.getExpectedFinishDate()) : null);
+ state.getStartedAt().map(d -> d.atZone(ZoneOffset.UTC)).map(DateTimeFormatter.ISO_DATE_TIME::format).orElse(null),
+ state.getError().map(Throwable::getMessage).orElse(state.getStatus().getMessage()),
+ state.getExpectedFinishDate(Instant.now()).map(d -> d.atZone(ZoneOffset.UTC)).map(DateTimeFormatter.ISO_DATE_TIME::format).orElse(null));
}
public DatabaseMigrationsResponse(DatabaseMigrationState.Status status) {
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.v2.common;
-
-import java.util.Date;
-import javax.annotation.Nullable;
-
-public class DateString {
-
- private DateString() {
- // intentionally empty
- }
-
- public static String from(@Nullable Date date) {
- if (date == null) {
- return null;
- }
- return date.toString();
- }
-}
public BeanFactoryPostProcessor beanFactoryPostProcessor1(SpringDocConfigProperties springDocConfigProperties) {
return beanFactory -> springDocConfigProperties.setDefaultProducesMediaType(MediaType.APPLICATION_JSON_VALUE);
}
-
}
*/
package org.sonar.server.v2.api.system.controller;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.Optional;
-import java.util.TimeZone;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.sonar.db.Database;
import org.sonar.db.dialect.Dialect;
import org.sonar.server.platform.db.migration.DatabaseMigrationState;
+import org.sonar.server.platform.db.migration.DatabaseMigrationStateImpl;
import org.sonar.server.platform.db.migration.version.DatabaseVersion;
import org.sonar.server.v2.api.ControllerTester;
import org.springframework.test.web.servlet.MockMvc;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.FAILED;
class DatabaseMigrationsControllerTest {
- private static final Date SOME_DATE = new Date();
- private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
-
+ private static final Instant SOME_DATE = Instant.now();
+ private static final String SOME_DATE_STRING = DateTimeFormatter.ISO_DATE_TIME.format(SOME_DATE.atZone(ZoneOffset.UTC));
private final DatabaseVersion databaseVersion = mock();
- private final DatabaseMigrationState migrationState = mock();
+ private final DatabaseMigrationStateImpl migrationState = mock();
private final Dialect dialect = mock(Dialect.class);
private final Database database = mock();
private final MockMvc mockMvc = ControllerTester.getMockMvc(new DatabaseMigrationsController(databaseVersion, migrationState, database));
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(RUNNING);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getExpectedFinishDate()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getExpectedFinishDate(any())).thenReturn(Optional.of(SOME_DATE));
when(migrationState.getCompletedMigrations()).thenReturn(1);
when(migrationState.getTotalMigrations()).thenReturn(10);
-
- DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(),
content().json("{\"status\":\"MIGRATION_RUNNING\",\"completedSteps\":1,\"totalSteps\":10," +
- "\"message\":\"Database migration is running.\",\"expectedFinishTimestamp\":\""+DATE_FORMAT.format(SOME_DATE)+"\"}"));
+ "\"message\":\"Database migration is running.\",\"expectedFinishTimestamp\":\"" + SOME_DATE_STRING + "\"}"));
}
@Test
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(DatabaseMigrationState.Status.FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(),
content().json("{\"status\":\"MIGRATION_FAILED\",\"message\":\"Migration failed: %s.<br/> Please check logs.\"}"));
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(DatabaseMigrationState.Status.SUCCEEDED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(),
content().json("{\"status\":\"MIGRATION_SUCCEEDED\",\"message\":\"Migration succeeded.\"}"));
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(NONE);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(),
content().json("{\"status\":\"MIGRATION_REQUIRED\",\"message\":\"Database migration is required. DB migration " +
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.FRESH_INSTALL);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getError()).thenReturn(new UnsupportedOperationException("error message"));
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getError()).thenReturn(Optional.of(new UnsupportedOperationException("error message")));
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(),
content().json("{\"status\":\"MIGRATION_FAILED\",\"message\":\"error message\"}"));
*/
package org.sonar.server.platform.ws;
+import java.util.Date;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.server.platform.db.migration.DatabaseMigrationState;
json.beginObject()
.prop(FIELD_STATE, databaseMigrationState.getStatus().toString())
.prop(FIELD_MESSAGE, writeMessageIncludingError(databaseMigrationState))
- .propDateTime(FIELD_STARTED_AT, databaseMigrationState.getStartedAt())
+ .propDateTime(FIELD_STARTED_AT, databaseMigrationState.getStartedAt().map(Date::from).orElse(null))
.endObject();
}
private static String writeMessageIncludingError(DatabaseMigrationState state) {
if (state.getStatus() == FAILED) {
- Throwable error = state.getError();
+ Throwable error = state.getError().orElse(null);
return String.format(state.getStatus().getMessage(), error != null ? error.getMessage() : "No failure error");
} else {
return state.getStatus().getMessage();
json.beginObject()
.prop(FIELD_STATE, RUNNING.toString())
.prop(FIELD_MESSAGE, RUNNING.getMessage())
- .propDateTime(FIELD_STARTED_AT, databaseMigrationState.getStartedAt())
+ .propDateTime(FIELD_STARTED_AT, databaseMigrationState.getStartedAt().map(Date::from).orElse(null))
.endObject();
}
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.Arrays;
-import java.util.Date;
import java.util.Optional;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.sonar.api.utils.DateUtils;
import org.sonar.db.Database;
import org.sonar.db.dialect.Dialect;
import org.sonar.server.platform.db.migration.DatabaseMigrationState;
@RunWith(DataProviderRunner.class)
public class DbMigrationStatusActionTest {
- private static final Date SOME_DATE = new Date();
+ private static final Instant SOME_DATE = Instant.now();
private static final String SOME_THROWABLE_MSG = "blablabla pop !";
private static final String DEFAULT_ERROR_MSG = "No failure error";
+ public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
private DatabaseVersion databaseVersion = mock(DatabaseVersion.class);
private Database database = mock(Database.class);
public void verify_example() {
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(RUNNING);
- when(migrationState.getStartedAt()).thenReturn(DateUtils.parseDateTime("2015-02-23T18:54:23+0100"));
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(DATE_TIME_FORMATTER.parse("2015-02-23T18:54:23+0100", Instant::from)));
TestResponse response = tester.newRequest().execute();
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(RUNNING);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getError()).thenReturn(new UnsupportedOperationException(SOME_THROWABLE_MSG));
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getError()).thenReturn(Optional.of(new UnsupportedOperationException(SOME_THROWABLE_MSG)));
TestResponse response = tester.newRequest().execute();
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getError()).thenReturn(null); // no failure throwable caught
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getError()).thenReturn(Optional.empty()); // no failure throwable caught
TestResponse response = tester.newRequest().execute();
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(SUCCEEDED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(NONE);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
"}";
}
- private static String expectedResponse(String status, String msg, Date date) {
+ private static String expectedResponse(String status, String msg, Instant date) {
return "{" +
"\"state\":\"" + status + "\"," +
"\"message\":\"" + msg + "\"," +
- "\"startedAt\":\"" + DateUtils.formatDateTime(date) + "\"" +
+ "\"startedAt\":\"" + DATE_TIME_FORMATTER.format(date.atZone(ZoneOffset.systemDefault())) + "\"" +
"}";
}
}
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.Arrays;
-import java.util.Date;
import java.util.Optional;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.sonar.api.utils.DateUtils;
import org.sonar.db.Database;
import org.sonar.db.dialect.Dialect;
import org.sonar.server.platform.db.migration.DatabaseMigration;
@RunWith(DataProviderRunner.class)
public class MigrateDbActionTest {
- private static final Date SOME_DATE = new Date();
+ private static final Instant SOME_DATE = Instant.now();
private static final String SOME_THROWABLE_MSG = "blablabla pop !";
private static final String DEFAULT_ERROR_MSG = "No failure error";
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.";
+ public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
private DatabaseVersion databaseVersion = mock(DatabaseVersion.class);
private Database database = mock(Database.class);
public void verify_example() {
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(RUNNING);
- when(migrationState.getStartedAt()).thenReturn(DateUtils.parseDateTime("2015-02-23T18:54:23+0100"));
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(DATE_TIME_FORMATTER.parse("2015-02-23T18:54:23+0100", Instant::from)));
TestResponse response = tester.newRequest().execute();
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(RUNNING);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getError()).thenReturn(new UnsupportedOperationException(SOME_THROWABLE_MSG));
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getError()).thenReturn(Optional.of(new UnsupportedOperationException(SOME_THROWABLE_MSG)));
TestResponse response = tester.newRequest().execute();
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(FAILED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
- when(migrationState.getError()).thenReturn(null); // no failure throwable caught
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
+ when(migrationState.getError()).thenReturn(Optional.empty()); // no failure throwable caught
TestResponse response = tester.newRequest().execute();
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(SUCCEEDED);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
when(databaseVersion.getStatus()).thenReturn(status);
when(dialect.supportsMigration()).thenReturn(true);
when(migrationState.getStatus()).thenReturn(NONE);
- when(migrationState.getStartedAt()).thenReturn(SOME_DATE);
+ when(migrationState.getStartedAt()).thenReturn(Optional.of(SOME_DATE));
TestResponse response = tester.newRequest().execute();
"}";
}
- private static String expectedResponse(String status, String msg, Date date) {
+ private static String expectedResponse(String status, String msg, Instant date) {
return "{" +
"\"state\":\"" + status + "\"," +
"\"message\":\"" + msg + "\"," +
- "\"startedAt\":\"" + DateUtils.formatDateTime(date) + "\"" +
+ "\"startedAt\":\"" + DATE_TIME_FORMATTER.format(date.atZone(ZoneOffset.UTC)) + "\"" +
"}";
}
}