Speedup build: introduced SharedSqlTestDatabase and DisposableSqlTestDatabase junit extensions (#1218)

* fixed PostgreSql migration scripts
* introduces SharedSqlTestDatabaseExtension and DisposableSqlTestDatabaseExtension
* Add cause msg to JUnitTestLoggerExtension

Signed-off-by: Ahmed Sayed <ahmed.sayed@bosch.io>
Signed-off-by: Florian Ruschbaschan <florian.ruschbaschan@bosch.io>
Co-authored-by: Florian Ruschbaschan <florian.ruschbaschan@bosch.io>
This commit is contained in:
Ahmed Sayed
2023-03-09 07:30:22 +01:00
committed by GitHub
parent 92256ed787
commit 090db6fd7b
14 changed files with 396 additions and 175 deletions

View File

@@ -20,8 +20,10 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.report.model.TenantUsage;
import org.eclipse.hawkbit.repository.test.util.DisposableSqlTestDatabaseExtension;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -29,6 +31,7 @@ import io.qameta.allure.Story;
@Feature("Component Tests - Repository")
@Story("System Management")
@ExtendWith(DisposableSqlTestDatabaseExtension.class)
public class SystemManagementTest extends AbstractJpaIntegrationTest {
@Test

View File

@@ -19,9 +19,11 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.util.DisposableSqlTestDatabaseExtension;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.data.domain.Slice;
import io.qameta.allure.Description;
@@ -36,6 +38,7 @@ import io.qameta.allure.Story;
*/
@Feature("Component Tests - Repository")
@Story("Multi Tenancy")
@ExtendWith(DisposableSqlTestDatabaseExtension.class)
public class MultiTenancyEntityTest extends AbstractJpaIntegrationTest {
@Test

View File

@@ -90,7 +90,7 @@ import org.springframework.test.context.TestExecutionListeners.MergeMode;
import org.springframework.test.context.TestPropertySource;
@ActiveProfiles({ "test" })
@ExtendWith({ JUnitTestLoggerExtension.class, WithSpringAuthorityRule.class })
@ExtendWith({ JUnitTestLoggerExtension.class, WithSpringAuthorityRule.class , SharedSqlTestDatabaseExtension.class })
@WithUser(principal = "bumlux", allSpPermissions = true, authorities = { CONTROLLER_ROLE, SYSTEM_ROLE })
@SpringBootTest
@ContextConfiguration(classes = { TestConfiguration.class, TestSupportBinderAutoConfiguration.class })
@@ -102,9 +102,8 @@ import org.springframework.test.context.TestPropertySource;
// Cleaning repository will fire "delete" events. We won't count them to the
// test execution. So, the order execution between EventVerifier and Cleanup is
// important!
@TestExecutionListeners(listeners = { EventVerifier.class, CleanupTestExecutionListener.class,
MySqlTestDatabase.class, MsSqlTestDatabase.class,
PostgreSqlTestDatabase.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
@TestExecutionListeners(listeners = { EventVerifier.class, CleanupTestExecutionListener.class },
mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
@TestPropertySource(properties = "spring.main.allow-bean-definition-overriding=true")
public abstract class AbstractIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class);

View File

@@ -8,48 +8,47 @@
*/
package org.eclipse.hawkbit.repository.test.util;
import static java.sql.DriverManager.getConnection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.util.AntPathMatcher;
/**
* A {@link TestExecutionListener} for creating and dropping MySql schemas if
* tests are setup with MySql.
* A {@link TestExecutionListener} for creating and dropping SQL schemas if
* tests are setup with an SQL schema.
*/
public abstract class AbstractSqlTestDatabase extends AbstractTestExecutionListener {
private static final Logger LOG = LoggerFactory.getLogger(AbstractSqlTestDatabase.class);
protected String schemaName;
protected String uri;
protected String username;
protected String password;
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSqlTestDatabase.class);
protected static final AntPathMatcher MATCHER = new AntPathMatcher();
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
if (isRunningWithSql()) {
LOG.info("Setting up database for test class {}", testContext.getTestClass().getName());
this.username = System.getProperty("spring.datasource.username");
this.password = System.getProperty("spring.datasource.password");
this.uri = System.getProperty("spring.datasource.url");
createSchemaUri();
createSchema();
}
protected final DatasourceContext context;
public AbstractSqlTestDatabase(final DatasourceContext context) {
this.context = context;
}
@Override
public void afterTestClass(final TestContext testContext) throws Exception {
if (isRunningWithSql()) {
dropSchema();
protected abstract AbstractSqlTestDatabase createRandomSchema();
protected abstract void dropRandomSchema();
protected abstract String getRandomSchemaUri();
protected void executeStatement(final String uri, final String statement) {
LOGGER.trace("\033[0;33mExecuting statement {} on uri {} \033[0m", statement, uri);
try (final Connection connection = getConnection(uri, context.getUsername(), context.getPassword());
final PreparedStatement preparedStatement = connection.prepareStatement(statement)) {
preparedStatement.execute();
} catch (final SQLException e) {
LOGGER.error("Execution of statement '{}' on uri {} failed!", statement, uri, e);
}
}
protected abstract void createSchemaUri();
protected abstract boolean isRunningWithSql();
protected abstract void createSchema();
protected abstract void dropSchema();
}

View File

@@ -0,0 +1,89 @@
/**
* Copyright (c) 2022 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.test.util;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Holds all database related configuration
*/
public class DatasourceContext {
private static final Logger LOGGER = LoggerFactory.getLogger(DatasourceContext.class);
public static final String SPRING_DATASOURCE_URL_KEY = "spring.datasource.url";
public static final String SPRING_DATABASE_KEY = "spring.jpa.database";
public static final String SPRING_DATABASE_USERNAME_KEY = "spring.datasource.username";
public static final String SPRING_DATABASE_PASSWORD_KEY = "spring.datasource.password";
public static final String DATABASE_PREFIX_KEY = "spring.database.random.prefix";
private static final String RANDOM_DB_PREFIX = System.getProperty(DATABASE_PREFIX_KEY, "HAWKBIT_TEST_");
private final String database;
private final String datasourceUrl;
private final String username;
private final String password;
private final String randomSchemaName = RANDOM_DB_PREFIX + RandomStringUtils.randomAlphanumeric(10);
/**
* Constructor
*/
public DatasourceContext(final String database, final String datasourceUrl, final String username,
final String password) {
this.database = database;
this.datasourceUrl = datasourceUrl;
this.username = username;
this.password = password;
}
/**
* Constructor
*/
public DatasourceContext() {
database = System.getProperty(SPRING_DATABASE_KEY, System.getProperty(upperCaseVariant(SPRING_DATABASE_KEY)));
datasourceUrl = System.getProperty(SPRING_DATASOURCE_URL_KEY,
System.getProperty(upperCaseVariant(SPRING_DATASOURCE_URL_KEY)));
username = System.getProperty(SPRING_DATABASE_USERNAME_KEY,
System.getProperty(upperCaseVariant(SPRING_DATABASE_USERNAME_KEY)));
password = System.getProperty(SPRING_DATABASE_PASSWORD_KEY,
System.getProperty(upperCaseVariant(SPRING_DATABASE_PASSWORD_KEY)));
}
private static String upperCaseVariant(final String key) {
return key.toUpperCase().replace('.', '_');
}
public String getDatabase() {
return database;
}
public String getDatasourceUrl() {
return datasourceUrl;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getRandomSchemaName() {
return randomSchemaName;
}
public boolean isNotProperlyConfigured() {
LOGGER.debug("Datasource environment variables: [database: {}, username: {}, password: {}, datasourceUrl: {}]",
database, username, password, datasourceUrl);
return database == null || datasourceUrl == null || username == null || password == null;
}
}

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2022 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.test.util;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.eclipse.hawkbit.repository.test.util.DatasourceContext.SPRING_DATASOURCE_URL_KEY;
/**
* Provides a convenient way to generate a test database that can be used, and disposed of after the test is executed.
*/
public class DisposableSqlTestDatabaseExtension extends SharedSqlTestDatabaseExtension implements AfterAllCallback {
private static final Logger LOGGER = LoggerFactory.getLogger(SharedSqlTestDatabaseExtension.class);
private DatasourceContext datasourceContext = null;
@Override
public void beforeAll(final ExtensionContext extensionContext) {
super.beforeAll(extensionContext);
final DatasourceContext sharedContext = CONTEXT.get();
if (sharedContext == null || sharedContext.isNotProperlyConfigured()) {
return;
}
datasourceContext = new DatasourceContext(sharedContext.getDatabase(), sharedContext.getDatasourceUrl(),
sharedContext.getUsername(), sharedContext.getPassword());
final AbstractSqlTestDatabase database = matchingDatabase(datasourceContext);
final String randomSchemaUri = database.createRandomSchema().getRandomSchemaUri();
LOGGER.info("\033[0;33mRandom Schema URI is {} \033[0m", randomSchemaUri);
System.setProperty(SPRING_DATASOURCE_URL_KEY, randomSchemaUri);
}
@Override
public void afterAll(final ExtensionContext extensionContext) {
if (datasourceContext == null) {
return;
}
matchingDatabase(datasourceContext).dropRandomSchema();
System.setProperty(SPRING_DATASOURCE_URL_KEY, matchingDatabase(CONTEXT.get()).getRandomSchemaUri());
}
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2022 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.test.util;
import org.junit.jupiter.api.extension.Extension;
/**
* An {@link Extension} for creating and dropping H2 schemas if
* tests are set up with H2.
*/
public class H2TestDatabase extends AbstractSqlTestDatabase {
public H2TestDatabase(final DatasourceContext context) {
super(context);
}
@Override
public H2TestDatabase createRandomSchema() {
// do nothing, since H2 is in memory
return this;
}
@Override
protected void dropRandomSchema() {
// do nothing, since H2 is in memory
}
@Override
protected String getRandomSchemaUri() {
return "jdbc:h2:mem:" + context.getRandomSchemaName() +";MODE=MySQL;";
}
}

View File

@@ -19,17 +19,17 @@ public class JUnitTestLoggerExtension implements BeforeTestExecutionCallback, Te
private static final Logger LOG = LoggerFactory.getLogger(JUnitTestLoggerExtension.class);
@Override
public void testSuccessful(ExtensionContext context) {
public void testSuccessful(final ExtensionContext context) {
LOG.info("Test {} succeeded.", context.getTestMethod());
}
@Override
public void testFailed(ExtensionContext context, Throwable cause) {
LOG.error("Test {} failed with {}.", context.getTestMethod());
public void testFailed(final ExtensionContext context, final Throwable cause) {
LOG.error("Test {} failed with {}.", context.getTestMethod(), cause.getMessage());
}
@Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
public void beforeTestExecution(final ExtensionContext context) {
LOG.info("Starting Test {}...", context.getTestMethod());
}
}

View File

@@ -8,67 +8,45 @@
*/
package org.eclipse.hawkbit.repository.test.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.extension.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.TestExecutionListener;
/**
* A {@link TestExecutionListener} for creating and dropping MS SQL Server
* schemas if tests are setup with MS SQL Server.
* An {@link Extension} for creating and dropping MS SQL Server
* schemas if tests are set up with MS SQL Server.
*/
public class MsSqlTestDatabase extends AbstractSqlTestDatabase {
private static final Logger LOG = LoggerFactory.getLogger(MsSqlTestDatabase.class);
private static final Logger LOGGER = LoggerFactory.getLogger(MsSqlTestDatabase.class);
@Override
protected void createSchemaUri() {
schemaName = "SP" + RandomStringUtils.randomAlphanumeric(10);
this.uri = this.uri.substring(0, uri.indexOf(';'));
System.setProperty("spring.datasource.url", uri + ";database=" + schemaName);
public MsSqlTestDatabase(final DatasourceContext context) {
super(context);
}
@Override
protected boolean isRunningWithSql() {
return "SQL_SERVER".equals(System.getProperty("spring.jpa.database"));
public MsSqlTestDatabase createRandomSchema() {
final String uri = context.getDatasourceUrl();
LOGGER.info("\033[0;33mCreating mssql schema {} \033[0m", context.getRandomSchemaName());
executeStatement(uri.split(";database=")[0], "CREATE DATABASE " + context.getRandomSchemaName() + ";");
return this;
}
@Override
protected void createSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
try (PreparedStatement statement = connection.prepareStatement("CREATE DATABASE " + schemaName + ";")) {
LOG.info("Creating schema {} on uri {}", schemaName, uri);
statement.execute();
LOG.info("Created schema {} on uri {}", schemaName, uri);
}
} catch (final SQLException e) {
LOG.error("Schema creation failed!", e);
}
protected void dropRandomSchema() {
final String uri = context.getDatasourceUrl();
final String dbServerUri = uri.split(";database=")[0];
LOGGER.info("\033[0;33mDropping mssql schema {} \033[0m", context.getRandomSchemaName());
// Needed to avoid the DROP is rejected with "database still in use"
executeStatement(dbServerUri, "ALTER DATABASE " + context.getRandomSchemaName() + " SET SINGLE_USER WITH ROLLBACK IMMEDIATE;");
executeStatement(dbServerUri, "DROP DATABASE " + context.getRandomSchemaName() + ";");
}
@Override
protected void dropSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
// Needed to avoid the DROP is rejected with "database still in use"
try (PreparedStatement statement = connection
.prepareStatement("ALTER DATABASE " + schemaName + " SET SINGLE_USER WITH ROLLBACK IMMEDIATE;")) {
statement.execute();
}
try (PreparedStatement statement = connection.prepareStatement("DROP DATABASE " + schemaName + ";")) {
LOG.info("Dropping schema {} on uri {}", schemaName, uri);
statement.execute();
LOG.info("Dropped schema {} on uri {}", schemaName, uri);
}
} catch (final SQLException e) {
LOG.error("Schema drop failed!", e);
}
protected String getRandomSchemaUri() {
final String uri = context.getDatasourceUrl();
return uri.substring(0, uri.indexOf(';')) + ";database=" + context.getRandomSchemaName();
}
}

View File

@@ -8,61 +8,55 @@
*/
package org.eclipse.hawkbit.repository.test.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Map;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.extension.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.TestExecutionListener;
/**
* A {@link TestExecutionListener} for creating and dropping MySql schemas if
* tests are setup with MySql.
* An {@link Extension} for creating and dropping MySql schemas if
* tests are set up with MySql.
*/
public class MySqlTestDatabase extends AbstractSqlTestDatabase {
private static final Logger LOG = LoggerFactory.getLogger(MySqlTestDatabase.class);
private static final Logger LOGGER = LoggerFactory.getLogger(MySqlTestDatabase.class);
protected static final String MYSQL_URI_PATTERN = "jdbc:mysql://{host}:{port}/{db}*";
@Override
protected void createSchemaUri() {
schemaName = "SP" + RandomStringUtils.randomAlphanumeric(10);
this.uri = this.uri.substring(0, uri.lastIndexOf('/') + 1);
System.setProperty("spring.datasource.url", uri + schemaName);
public MySqlTestDatabase(final DatasourceContext context) {
super(context);
}
@Override
protected boolean isRunningWithSql() {
return "MYSQL".equals(System.getProperty("spring.jpa.database"));
public MySqlTestDatabase createRandomSchema() {
final String uri = context.getDatasourceUrl();
final String schemaName = getSchemaName(uri);
LOGGER.info("\033[0;33mCreating mysql schema {} if not existing \033[0m", context.getRandomSchemaName());
executeStatement(uri.split("/" + schemaName)[0],
"CREATE SCHEMA IF NOT EXISTS " + context.getRandomSchemaName() + ";");
return this;
}
@Override
protected void createSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
try (PreparedStatement statement = connection.prepareStatement("CREATE SCHEMA " + schemaName + ";")) {
LOG.info("Creating schema {} on uri {}", schemaName, uri);
statement.execute();
LOG.info("Created schema {} on uri {}", schemaName, uri);
}
} catch (final SQLException e) {
LOG.error("Schema creation failed!", e);
}
protected void dropRandomSchema() {
final String uri = context.getDatasourceUrl();
final String schemaName = getSchemaName(uri);
LOGGER.info("\033[0;33mDropping mysql schema {} \033[0m", context.getRandomSchemaName());
executeStatement(uri.split("/" + schemaName)[0], "DROP SCHEMA " + context.getRandomSchemaName() + ";");
}
@Override
protected void dropSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
try (PreparedStatement statement = connection.prepareStatement("DROP SCHEMA " + schemaName + ";")) {
LOG.info("Dropping schema {} on uri {}", schemaName, uri);
statement.execute();
LOG.info("Dropped schema {} on uri {}", schemaName, uri);
}
} catch (final SQLException e) {
LOG.error("Schema drop failed!", e);
}
protected String getRandomSchemaUri() {
final String uri = context.getDatasourceUrl();
final Map<String, String> databaseProperties = MATCHER.extractUriTemplateVariables(MYSQL_URI_PATTERN, uri);
return MYSQL_URI_PATTERN.replace("{host}", databaseProperties.get("host"))
.replace("{port}", databaseProperties.get("port"))
.replace("{db}*", context.getRandomSchemaName());
}
private static String getSchemaName(final String datasourceUrl) {
return MATCHER.extractUriTemplateVariables(MYSQL_URI_PATTERN, datasourceUrl).get("db");
}
}

View File

@@ -8,61 +8,57 @@
*/
package org.eclipse.hawkbit.repository.test.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Map;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.extension.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.TestExecutionListener;
/**
* A {@link TestExecutionListener} for creating and dropping MySql schemas if
* tests are setup with MySql.
* An {@link Extension} for creating and dropping MySql schemas if
* tests are set up with MySql.
*/
public class PostgreSqlTestDatabase extends AbstractSqlTestDatabase {
private static final Logger LOG = LoggerFactory.getLogger(PostgreSqlTestDatabase.class);
private static final Logger LOGGER = LoggerFactory.getLogger(PostgreSqlTestDatabase.class);
private static final String POSTGRESQL_URI_PATTERN = "jdbc:postgresql://{host}:{port}/{db}*";
@Override
protected void createSchemaUri() {
schemaName = "sp" + RandomStringUtils.randomAlphanumeric(10).toLowerCase();
this.uri = this.uri.substring(0, uri.indexOf('?'));
System.setProperty("spring.datasource.url", uri + "?currentSchema=" + schemaName);
public PostgreSqlTestDatabase(final DatasourceContext context) {
super(context);
}
@Override
protected boolean isRunningWithSql() {
return "POSTGRESQL".equals(System.getProperty("spring.jpa.database"));
protected PostgreSqlTestDatabase createRandomSchema() {
LOGGER.info("\033[0;33mCreating postgreSql schema {} \033[0m", context.getRandomSchemaName());
final String uri = getBaseUri() + "?currentSchema=" + getSchemaName();
executeStatement(uri, "CREATE SCHEMA " + context.getRandomSchemaName() + ";");
return this;
}
@Override
protected void createSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
try (PreparedStatement statement = connection.prepareStatement("CREATE schema " + schemaName + ";")) {
LOG.info("Creating schema {} on uri {}", schemaName, uri);
statement.execute();
LOG.info("Created schema {} on uri {}", schemaName, uri);
}
} catch (final SQLException e) {
LOG.error("Schema creation failed!", e);
}
protected void dropRandomSchema() {
LOGGER.info("\033[0;33mDropping postgreSql schema {}\033[0m", context.getRandomSchemaName());
final String uri = getBaseUri() + "?currentSchema=" + getSchemaName();
executeStatement(uri, "DROP SCHEMA " + context.getRandomSchemaName() + " CASCADE;");
}
@Override
protected void dropSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
try (PreparedStatement statement = connection.prepareStatement("DROP schema " + schemaName + " CASCADE;")) {
LOG.info("Dropping schema {} on uri {}", schemaName, uri);
statement.execute();
LOG.info("Dropped schema {} on uri {}", schemaName, uri);
}
} catch (final SQLException e) {
LOG.error("Schema drop failed!", e);
}
protected String getRandomSchemaUri() {
return getBaseUri() + "?currentSchema=" + context.getRandomSchemaName();
}
private String getBaseUri() {
final String uri = context.getDatasourceUrl();
final Map<String, String> databaseProperties = MATCHER.extractUriTemplateVariables(POSTGRESQL_URI_PATTERN, uri);
return POSTGRESQL_URI_PATTERN.replace("{host}", databaseProperties.get("host"))
.replace("{port}", databaseProperties.get("port"))
.replace("{db}*", getSchemaName());
}
private String getSchemaName() {
return MATCHER.extractUriTemplateVariables(POSTGRESQL_URI_PATTERN, context.getDatasourceUrl())
.get("db")
.split("\\?")[0];
}
}

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2022 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.test.util;
import static org.eclipse.hawkbit.repository.test.util.DatasourceContext.SPRING_DATASOURCE_URL_KEY;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents a test database configuration for a "shared" database instance across all tests annotated with this extension
*/
public class SharedSqlTestDatabaseExtension implements BeforeAllCallback {
private static final Logger LOGGER = LoggerFactory.getLogger(SharedSqlTestDatabaseExtension.class);
protected static final AtomicReference<DatasourceContext> CONTEXT = new AtomicReference<>();
@Override
public void beforeAll(final ExtensionContext extensionContext) {
final DatasourceContext testDatasourceContext = new DatasourceContext();
if (testDatasourceContext.isNotProperlyConfigured()) {
LOGGER.info("\033[0;33mSchema generation skipped... No datasource environment variables found!\033[0m");
return;
}
// update CONTEXT only if the current value is null => initialize only
if (!CONTEXT.compareAndSet(null, testDatasourceContext)) {
final String randomSchemaUri = matchingDatabase(testDatasourceContext).getRandomSchemaUri();
LOGGER.info("\033[0;33mReusing Random Schema at URI {} \033[0m", randomSchemaUri);
return;
}
final AbstractSqlTestDatabase database = matchingDatabase(testDatasourceContext);
final String randomSchemaUri = database.createRandomSchema().getRandomSchemaUri();
LOGGER.info("\033[0;33mRandom Schema URI is {} \033[0m", randomSchemaUri);
System.setProperty(SPRING_DATASOURCE_URL_KEY, randomSchemaUri);
registerDropSchemaOnSystemShutdownHook(database, randomSchemaUri);
}
private void registerDropSchemaOnSystemShutdownHook(final AbstractSqlTestDatabase database, final String schemaUri) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LOGGER.warn("\033[0;33mDropping schema at url {} \033[0m", schemaUri);
database.dropRandomSchema();
}));
}
protected AbstractSqlTestDatabase matchingDatabase(final DatasourceContext context) {
AbstractSqlTestDatabase database;
switch (context.getDatabase()) {
case "H2":
database = new H2TestDatabase(context);
break;
case "SQL_SERVER":
database = new MsSqlTestDatabase(context);
break;
case "MYSQL":
database = new MySqlTestDatabase(context);
break;
case "POSTGRESQL":
database = new PostgreSqlTestDatabase(context);
break;
default:
throw new IllegalStateException("No supported database found for type " + context.getDatabase());
}
return database;
}
}

View File

@@ -8,24 +8,19 @@
*/
package org.eclipse.hawkbit.app;
import org.eclipse.hawkbit.repository.test.util.MsSqlTestDatabase;
import org.eclipse.hawkbit.repository.test.util.MySqlTestDatabase;
import org.eclipse.hawkbit.repository.test.util.SharedSqlTestDatabaseExtension;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.TestExecutionListeners.MergeMode;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@SpringBootTest(properties = { "hawkbit.dmf.rabbitmq.enabled=false" })
@TestExecutionListeners(listeners = { MySqlTestDatabase.class,
MsSqlTestDatabase.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
@DirtiesContext
@ExtendWith(SharedSqlTestDatabaseExtension.class)
public abstract class AbstractSecurityTest {
@Autowired
@@ -40,4 +35,4 @@ public abstract class AbstractSecurityTest {
mvc = builder.build();
}
}
}

View File

@@ -14,14 +14,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.test.util.MsSqlTestDatabase;
import org.eclipse.hawkbit.repository.test.util.MySqlTestDatabase;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.TestExecutionListeners.MergeMode;
import org.springframework.test.web.servlet.ResultActions;
import io.qameta.allure.Description;
@@ -31,10 +28,7 @@ import io.qameta.allure.Story;
@SpringBootTest(properties = { "hawkbit.dmf.rabbitmq.enabled=false", "hawkbit.server.security.cors.enabled=true",
"hawkbit.server.security.cors.allowedOrigins=" + CorsTest.ALLOWED_ORIGIN_FIRST + ","
+ CorsTest.ALLOWED_ORIGIN_SECOND,
"hawkbit.server.security.cors.exposedHeaders=Access-Control-Allow-Origin" })
@TestExecutionListeners(listeners = { MySqlTestDatabase.class,
MsSqlTestDatabase.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
@Feature("Integration Test - Security")
"hawkbit.server.security.cors.exposedHeaders=Access-Control-Allow-Origin" })@Feature("Integration Test - Security")
@Story("CORS")
public class CorsTest extends AbstractSecurityTest {