DB and RabbitMQ integration tests and PostgreSQL testing/bug fixing (#1047)

* Initial matrixSigned-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* License header

* MySQL DB testSigned-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Create matrix for DBsSigned-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* RabbitMQ and H2Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* MySQL 8.0Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Postgresql test supportSigned-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Postgresql test supportSigned-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Fix DB issues post and mssql

Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Wait MSSQL

Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Fix postgresql tests.

Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* MSSQL startup fix.Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Fix syntax error

Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Further fix postgres tests.Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Revert unnecessary changes.

Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Add SonarCloud Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>

* Simplify devcontainer. Test JDK 15Signed-off-by: Kai Zimmermann <kai.zimmermann@microsoft.com>
This commit is contained in:
Kai Zimmermann
2021-01-14 09:07:03 +01:00
committed by GitHub
parent 4ea5d7655b
commit e9f11d2a20
27 changed files with 557 additions and 114 deletions

View File

@@ -0,0 +1,119 @@
# Build hawkBit and run tests with Apache Maven.
# Runs a matrix of various DB,JDK,RabbitMQ versions that are supported by hawkBit
#
# Requires the SonarCloud plugin and connection setup
# https://kaizimmerm.com/post/azure-pipelines-for-the-oss-developer/#analyze-the-code-with-sonarcloud
#
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/en-us/azure/devops/pipelines/ecosystems/java
# https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema
# https://docs.microsoft.com/en-us/azure/devops/pipelines/release/caching?view=azure-devops#maven
pool:
vmImage: "ubuntu-18.04"
# Default values for the hawbit build. Can be overriden in the variable group 'hawkbit'
# see https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups
variables:
# Defines defaults
- name: sonarCloudConnection
value: 'hawkBitSonar'
- name: sonarCloudOrganization
value: 'hawkbit'
- name: sonarProjectKey
value: 'org.eclipse:hawkbit'
# Override defaults
- group: hawkbit
jobs:
- job: JDK_11
displayName: Verify with JDK-11 and SonarCloud analysis
steps:
- template: rabbitmq-template.yml
- template: maven-template.yml
parameters:
mavenGoals: "verify -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=$(sonarCloudOrganization) -Dsonar.projectKey=$(sonarProjectKey)"
jdkVersionOption: "1.11"
sonarQubeRunAnalysis: true
sonarCloudConnection: $(sonarCloudConnection)
sonarCloudOrganization: $(sonarCloudOrganization)
- job: JDK_8
displayName: Build with JDK-8 (hawkBit default)
steps:
- template: maven-template.yml
parameters:
mavenGoals: "install license:check"
- job:
dependsOn: JDK_8
condition: succeeded()
displayName: RABBIT
strategy:
matrix:
3.6:
rabbitmqVersion: "3.6"
3.7:
rabbitmqVersion: "3.7"
3.8:
rabbitmqVersion: "3.8"
steps:
- template: rabbitmq-template.yml
parameters:
rabbitmqVersion: $(rabbitmqVersion)
- template: maven-template.yml
parameters:
mavenGoals: "verify"
- job:
dependsOn: JDK_8
condition: succeeded()
displayName: MYSQL
strategy:
matrix:
5.6:
dbVersion: "5.6"
5.7:
dbVersion: "5.7"
steps:
- template: rabbitmq-template.yml
- script: "docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=8236472364 -e MYSQL_DATABASE=hawkbit -d mysql:$(dbVersion)"
displayName: "Setup MYSQL Database docker instance"
- template: maven-template.yml
parameters:
mavenGoals: "verify -Dspring.jpa.database=MYSQL -Dspring.datasource.driverClassName=org.mariadb.jdbc.Driver -Dspring.datasource.url=jdbc:mysql://localhost:3306/hawkbit -Dspring.datasource.username=root -Dspring.datasource.password=8236472364"
- job:
dependsOn: JDK_8
condition: succeeded()
displayName: MSSQL
strategy:
matrix:
2017:
dbVersion: "2017-latest"
2019:
dbVersion: "2019-latest"
steps:
- template: rabbitmq-template.yml
- script: |
docker run --name mssql -p 1433:1433 -e ACCEPT_EULA=Y -e SA_PASSWORD=1234567890.Ab -d mcr.microsoft.com/mssql/server:$(dbVersion)
until [ "`/usr/bin/docker inspect -f {{.State.Running}} mssql`" == "true" ]; do sleep 1; done
sleep 120
until docker exec mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "1234567890.Ab" -Q "CREATE DATABASE hawkbit"; do sleep 1; done
displayName: "Setup MSSQL Database docker instance"
- template: maven-template.yml
parameters:
mavenGoals: "verify -Dspring.jpa.database=SQL_SERVER -Dspring.datasource.url=jdbc:sqlserver://localhost:1433;database=hawkbit -Dspring.datasource.username=SA -Dspring.datasource.password=1234567890.Ab -Dspring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver"
- job:
dependsOn: JDK_8
condition: succeeded()
displayName: POSTGRESQL
strategy:
matrix:
12:
dbVersion: "12"
13:
dbVersion: "13"
steps:
- template: rabbitmq-template.yml
- script: "docker run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=1234567890 -e POSTGRES_DB=hawkbit -d postgres:$(dbVersion)"
displayName: "Setup POSTGRESQL Database docker instance"
- template: maven-template.yml
parameters:
mavenGoals: "verify -Dspring.jpa.database=POSTGRESQL -Dspring.datasource.url=jdbc:postgresql://localhost:5432/hawkbit?currentSchema=hawkbit -Dspring.datasource.username=postgres -Dspring.datasource.password=1234567890 -Dspring.datasource.driverClassName=org.postgresql.Driver"

View File

@@ -0,0 +1,50 @@
parameters:
- name: mavenGoals
displayName: Maven Goal
type: string
- name: jdkVersionOption
displayName: JDK Version
type: string
default: "1.8"
- name: sonarQubeRunAnalysis
displayName: Enable SonarQube analysis
type: boolean
default: false
- name: sonarCloudConnection
displayName: Optional SonarCloud connection
type: string
default: ''
- name: sonarCloudOrganization
displayName: Optional SonarCloud organization
type: string
default: ''
- name: mavenCacheFolder
displayName: Maven Cache Folder
type: string
default: $(Pipeline.Workspace)/.m2/repository
steps:
- task: SonarCloudPrepare@1
condition: eq('${{ parameters.sonarQubeRunAnalysis }}', true)
displayName: 'Prepare SonarCloud analysis configuration'
inputs:
SonarCloud: ${{ parameters.sonarCloudConnection }}
organization: ${{ parameters.sonarCloudOrganization }}
scannerMode: Other
- task: Cache@2
inputs:
key: 'maven | "$(Agent.OS)" | "${{ parameters.jdkVersionOption }}" | **/pom.xml'
path: ${{ parameters.mavenCacheFolder }}
displayName: Cache Maven local repo
- task: Maven@3
inputs:
mavenPomFile: "pom.xml"
mavenOptions: "-Xmx3072m"
javaHomeOption: "JDKVersion"
jdkVersionOption: ${{ parameters.jdkVersionOption }}
jdkArchitectureOption: "x64"
publishJUnitResults: true
sonarQubeRunAnalysis: ${{ parameters.sonarQubeRunAnalysis }}
sqMavenPluginVersionChoice: 'latest'
testResultsFiles: "**/surefire-reports/TEST-*.xml"
goals: "${{ parameters.mavenGoals }} -Dmaven.repo.local=${{ parameters.mavenCacheFolder }}"

View File

@@ -0,0 +1,9 @@
parameters:
- name: rabbitmqVersion
displayName: RabbitMQ Version
type: string
default: "3.8"
steps:
- script: docker run -d --name rabbit -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_VHOST=/ rabbitmq:${{ parameters.rabbitmqVersion }}-management
displayName: "Setup RabbitMQ docker instance"

9
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
# [Choice] Java version: 8. 11, 15
ARG VARIANT=8
FROM mcr.microsoft.com/vscode/devcontainers/java:${VARIANT}
RUN su vscode -c "source /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>

View File

@@ -0,0 +1,35 @@
{
"name": "Java",
"build": {
"dockerfile": "Dockerfile",
"args": {
// Update the VARIANT arg to pick a Java version: 8, 11, 15
"VARIANT": "8"
}
},
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
"java.home": "/docker-java-home",
"java.format.settings.url": "https://raw.githubusercontent.com/eclipse/hawkbit/master/eclipse_codeformatter.xml",
"maven.executable.path": "/usr/local/sdkman/candidates/maven/current/bin/mvn"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"vscjava.vscode-java-pack",
"DavidAnson.vscode-markdownlint",
"ms-azuretools.vscode-docker",
"ms-azure-devops.azure-pipelines",
"sonarsource.sonarlint-vscode",
"pivotal.vscode-spring-boot",
"vscjava.vscode-spring-boot-dashboard"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
8080
],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "java -version",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
}

View File

@@ -48,7 +48,7 @@ There are clients outside of the Eclipse IoT eco system as well, e.g.:
# Runtime dependencies and support
## Java Runtime Environment: 1.8
## Java Runtime Environment: 1.8,11
## SQL database
@@ -56,12 +56,12 @@ There are clients outside of the Eclipse IoT eco system as well, e.g.:
| --------------------------------- | :------------------------------------------------: | :-----------------------------------------------------------------------: | :-------------------------------------------------------: | :----------------------------------------------------------------: | :----------------: |
| DDLs maintained by project | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Test dependencies defined | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | |
| Versions tested | 1.4 | MySQL 5.6/5.7, AWS Aurora | MS SQL Server 2017 | PostgreSQL 12.1 | DB2 Server v11.1 |
| Versions tested | 1.4 | MySQL 5.6/5.7, AWS Aurora | MS SQL Server 2017/2019 | PostgreSQL 12/13 | DB2 Server v11.1 |
| Docker image with driver provided | :white_check_mark: | :white_check_mark: (Tag: "-mysql") | :white_check_mark: | :white_check_mark: | |
| JDBC driver | [H2 1.4](https://github.com/h2database/h2database) | [MariaDB Connector/J 2.0](https://github.com/MariaDB/mariadb-connector-j) | [MSSQL-JDBC 6.4](https://github.com/Microsoft/mssql-jdbc) | [PostgreSQL JDBC Driver 42.2.10](https://github.com/pgjdbc/pgjdbc) | |
| Status | Test, Dev | Production grade | Production grade | Test, Dev | Test, Dev |
## (Optional) RabbitMQ: 3.6,3.7
## (Optional) RabbitMQ: 3.6,3.7,3.8
# Getting Started

View File

@@ -123,6 +123,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED = new EnumMap<>(Database.class);
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.SQL_SERVER, "DELETE TOP (" + ACTION_PAGE_LIMIT
+ ") FROM sp_action WHERE tenant=#tenant AND status IN (%s) AND last_modified_at<#last_modified_at ");
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.POSTGRESQL,
"DELETE FROM sp_action WHERE id IN (SELECT id FROM sp_action WHERE tenant=#tenant AND status IN (%s) AND last_modified_at<#last_modified_at LIMIT "
+ ACTION_PAGE_LIMIT + ")");
}
private final EntityManager entityManager;
@@ -209,8 +212,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
final Map<Long, List<TargetWithActionType>> assignmentsByDsIds = convertRequest(validatedRequests);
final List<DistributionSetAssignmentResult> results = assignmentsByDsIds.entrySet().stream()
.map(entry -> assignDistributionSetToTargetsWithRetry(initiatedBy, entry.getKey(), entry.getValue(), actionMessage,
strategy))
.map(entry -> assignDistributionSetToTargetsWithRetry(initiatedBy, entry.getKey(), entry.getValue(),
actionMessage, strategy))
.collect(Collectors.toList());
strategy.sendDeploymentEvents(results);
return results;
@@ -239,8 +242,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
}
}
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy, final Long dsID,
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy,
final Long dsID, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
final AbstractDsAssignmentStrategy assignmentStrategy) {
final RetryCallback<DistributionSetAssignmentResult, ConcurrencyFailureException> retryCallback = retryContext -> assignDistributionSetToTargets(
initiatedBy, dsID, targetsWithActionType, actionMessage, assignmentStrategy);
@@ -427,9 +430,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
return targetsWithActionType.stream()
.map(twt -> assignmentStrategy.createTargetAction(initiatedBy, twt, targets, set))
.filter(Objects::nonNull)
.map(actionRepository::save)
.collect(Collectors.toList());
.filter(Objects::nonNull).map(actionRepository::save).collect(Collectors.toList());
}
private void createActionsStatus(final Collection<JpaAction> actions,

View File

@@ -38,6 +38,7 @@ import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.dao.ConcurrencyFailureException;
@@ -45,6 +46,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.PlatformTransactionManager;
@@ -123,11 +125,15 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Autowired
private ArtifactRepository artifactRepository;
@Autowired
private JpaProperties properties;
@Override
public SystemUsageReport getSystemUsageStatistics() {
final Number count = (Number) entityManager.createNativeQuery(
"select SUM(file_size) from sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = 0")
"select SUM(file_size) from sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = "
+ (isPostgreSql(properties) ? "false" : "0"))
.getSingleResult();
long sumOfArtifacts = 0;
@@ -141,7 +147,8 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
.getSingleResult()).longValue();
final long artifacts = ((Number) entityManager.createNativeQuery(
"SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = 0")
"SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = "
+ (isPostgreSql(properties) ? "false" : "0"))
.getSingleResult()).longValue();
final long actions = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action")
@@ -151,6 +158,10 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
tenantMetaDataRepository.count());
}
private static boolean isPostgreSql(final JpaProperties properties) {
return Database.POSTGRESQL.equals(properties.getDatabase());
}
@Override
public SystemUsageReportWithTenants getSystemUsageStatisticsWithTenants() {
final SystemUsageReportWithTenants result = (SystemUsageReportWithTenants) getSystemUsageStatistics();

View File

@@ -35,6 +35,7 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.text.StrLookup;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.FieldValueConverter;
@@ -375,7 +376,8 @@ public final class RSQLUtility {
*/
private Path<Object> getFieldPath(final A enumField, final String finalProperty) {
return (Path<Object>) getFieldPath(root, getSubAttributesFrom(finalProperty), enumField.isMap(),
this::getJoinFieldPath).orElseThrow(() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null));
this::getJoinFieldPath).orElseThrow(
() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null));
}
private Path<?> getJoinFieldPath(final Path<?> fieldPath, final String fieldNameSplit) {
@@ -396,8 +398,8 @@ public final class RSQLUtility {
return fieldPath;
}
private static Optional<Path<?>> getFieldPath(final Root<?> root, final String[] split, final boolean isMapKeyField,
final BiFunction<Path<?>, String, Path<?>> joinFieldPathProvider) {
private static Optional<Path<?>> getFieldPath(final Root<?> root, final String[] split,
final boolean isMapKeyField, final BiFunction<Path<?>, String, Path<?>> joinFieldPathProvider) {
Path<?> fieldPath = null;
for (int i = 0; i < split.length; i++) {
if (!(isMapKeyField && i == (split.length - 1))) {
@@ -685,7 +687,11 @@ public final class RSQLUtility {
}
private Predicate getEqualToPredicate(final Object transformedValue, final Path<Object> fieldPath) {
if (transformedValue instanceof String) {
if (transformedValue == null) {
return cb.isNull(pathOfString(fieldPath));
}
if ((transformedValue instanceof String) && !NumberUtils.isCreatable((String) transformedValue)) {
if (StringUtils.isEmpty(transformedValue)) {
return cb.or(cb.isNull(pathOfString(fieldPath)), cb.equal(pathOfString(fieldPath), ""));
}
@@ -694,10 +700,6 @@ public final class RSQLUtility {
return cb.like(cb.upper(pathOfString(fieldPath)), sqlValue, ESCAPE_CHAR);
}
if (transformedValue == null) {
return cb.isNull(pathOfString(fieldPath));
}
return cb.equal(fieldPath, transformedValue);
}
@@ -708,7 +710,7 @@ public final class RSQLUtility {
return toNotNullPredicate(fieldPath);
}
if (transformedValue instanceof String) {
if ((transformedValue instanceof String) && !NumberUtils.isCreatable((String) transformedValue)) {
if (StringUtils.isEmpty(transformedValue)) {
return toNotNullAndNotEmptyPredicate(fieldPath);
}
@@ -725,7 +727,7 @@ public final class RSQLUtility {
return toNotEqualWithSubQueryPredicate(enumField, sqlValue, fieldNames);
}
return toNotEqualPredicate(fieldPath, transformedValue);
return toNullOrNotEqualPredicate(fieldPath, transformedValue);
}
private void clearOuterJoinsIfNotNeeded() {
@@ -738,15 +740,15 @@ public final class RSQLUtility {
return cb.isNotNull(pathOfString(fieldPath));
}
private Predicate toNotEqualPredicate(final Path<Object> fieldPath, final Object transformedValue) {
return cb.notEqual(fieldPath, transformedValue);
}
private Predicate toNullOrNotLikePredicate(final Path<Object> fieldPath, final String sqlValue) {
return cb.or(cb.isNull(pathOfString(fieldPath)),
cb.notLike(cb.upper(pathOfString(fieldPath)), sqlValue, ESCAPE_CHAR));
}
private Predicate toNullOrNotEqualPredicate(final Path<Object> fieldPath, final Object transformedValue) {
return cb.or(cb.isNull(pathOfString(fieldPath)), cb.notEqual(fieldPath, transformedValue));
}
private Predicate toNotNullAndNotEmptyPredicate(final Path<Object> fieldPath) {
return cb.and(cb.isNotNull(pathOfString(fieldPath)), cb.notEqual(pathOfString(fieldPath), ""));
}
@@ -797,9 +799,11 @@ public final class RSQLUtility {
}
private static Path<?> getInnerFieldPath(final Root<?> subqueryRoot, final String[] split,
final boolean isMapKeyField) {
final boolean isMapKeyField) {
return getFieldPath(subqueryRoot, split, isMapKeyField,
(fieldPath, fieldNameSplit) -> getInnerJoinFieldPath(subqueryRoot, fieldPath, fieldNameSplit)).orElseThrow(() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null));
(fieldPath, fieldNameSplit) -> getInnerJoinFieldPath(subqueryRoot, fieldPath, fieldNameSplit))
.orElseThrow(() -> new RSQLParameterUnsupportedFieldException(
"RSQL field path cannot be empty", null));
}
private static Path<?> getInnerJoinFieldPath(final Root<?> subqueryRoot, final Path<?> fieldPath,

View File

@@ -31,7 +31,9 @@ import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.transaction.annotation.Transactional;
@@ -101,6 +103,13 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Autowired
protected RolloutTestApprovalStrategy approvalStrategy;
@Autowired
private JpaProperties jpaProperties;
protected Database getDatabase() {
return jpaProperties.getDatabase();
}
@Transactional(readOnly = true)
protected List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
return Lists.newArrayList(actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), actionStatus));

View File

@@ -24,6 +24,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -55,7 +56,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
final JpaAction newAction = new JpaAction();
newAction.setActionType(ActionType.SOFT);
newAction.setDistributionSet(dsA);
newAction.setActive(i % 2 == 0);
newAction.setActive((i % 2) == 0);
newAction.setStatus(Status.RUNNING);
newAction.setTarget(target);
newAction.setWeight(45);
@@ -71,9 +72,18 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
public void testFilterByParameterId() {
assertRSQLQuery(ActionFields.ID.name() + "==" + action.getId(), 1);
assertRSQLQuery(ActionFields.ID.name() + "!=" + action.getId(), 10);
assertRSQLQuery(ActionFields.ID.name() + "==noExist*", 0);
assertRSQLQuery(ActionFields.ID.name() + "=in=(" + action.getId() + ",1000000)", 1);
assertRSQLQuery(ActionFields.ID.name() + "=out=(" + action.getId() + ",1000000)", 10);
assertRSQLQuery(ActionFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(ActionFields.ID.name() + "!=" + -1, 11);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(ActionFields.ID.name() + "==*", 11);
assertRSQLQuery(ActionFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(ActionFields.ID.name() + "=in=(" + action.getId() + ",10000000)", 1);
assertRSQLQuery(ActionFields.ID.name() + "=out=(" + action.getId() + ",10000000)", 10);
}
@Test

View File

@@ -23,6 +23,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -32,10 +33,12 @@ import io.qameta.allure.Story;
@Story("RSQL filter distribution set")
public class RSQLDistributionSetFieldTest extends AbstractJpaIntegrationTest {
private DistributionSet ds;
@Before
public void seuptBeforeTest() {
DistributionSet ds = testdataFactory.createDistributionSet("DS");
ds = testdataFactory.createDistributionSet("DS");
ds = distributionSetManagement.update(entityFactory.distributionSet().update(ds.getId()).description("DS"));
createDistributionSetMetadata(ds.getId(), entityFactory.generateDsMetadata("metaKey", "metaValue"));
@@ -59,7 +62,20 @@ public class RSQLDistributionSetFieldTest extends AbstractJpaIntegrationTest {
@Test
@Description("Test filter distribution set by id")
public void testFilterByParameterId() {
assertRSQLQuery(DistributionSetFields.ID.name() + "==" + ds.getId(), 1);
assertRSQLQuery(DistributionSetFields.ID.name() + "!=" + ds.getId(), 4);
assertRSQLQuery(DistributionSetFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(DistributionSetFields.ID.name() + "!=" + -1, 5);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(DistributionSetFields.ID.name() + "==*", 5);
assertRSQLQuery(DistributionSetFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(DistributionSetFields.ID.name() + "=in=(" + ds.getId() + ",10000000)", 1);
assertRSQLQuery(DistributionSetFields.ID.name() + "=out=(" + ds.getId() + ",10000000)", 4);
}
@Test

View File

@@ -21,6 +21,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -47,11 +48,21 @@ public class RSQLRolloutGroupFields extends AbstractJpaIntegrationTest {
@Test
@Description("Test filter rollout group by id")
public void testFilterByParameterId() {
assertRSQLQuery(RolloutGroupFields.ID.name() + "==*", 3);
assertRSQLQuery(RolloutGroupFields.ID.name() + "==" + rolloutGroupId, 1);
assertRSQLQuery(RolloutGroupFields.ID.name() + "!=" + rolloutGroupId, 3);
assertRSQLQuery(RolloutGroupFields.ID.name() + "==noExist*", 0);
assertRSQLQuery(RolloutGroupFields.ID.name() + "=in=(" + rolloutGroupId + ")", 1);
assertRSQLQuery(RolloutGroupFields.ID.name() + "=out=(" + rolloutGroupId + ")", 3);
assertRSQLQuery(RolloutGroupFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(RolloutGroupFields.ID.name() + "!=" + -1, 4);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(RolloutGroupFields.ID.name() + "==*", 4);
assertRSQLQuery(RolloutGroupFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(RolloutGroupFields.ID.name() + "=in=(" + rolloutGroupId + ",10000000)", 1);
assertRSQLQuery(RolloutGroupFields.ID.name() + "=out=(" + rolloutGroupId + ",10000000)", 2);
}
@Test

View File

@@ -20,6 +20,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -29,10 +30,12 @@ import io.qameta.allure.Story;
@Story("RSQL filter software module")
public class RSQLSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
private SoftwareModule ah;
@Before
public void setupBeforeTest() {
final SoftwareModule ah = softwareModuleManagement.create(entityFactory.softwareModule().create().type(appType)
.name("agent-hub").version("1.0.1").description("agent-hub"));
ah = softwareModuleManagement.create(entityFactory.softwareModule().create().type(appType).name("agent-hub")
.version("1.0.1").description("agent-hub"));
softwareModuleManagement.create(entityFactory.softwareModule().create().type(runtimeType).name("oracle-jre")
.version("1.7.2").description("aa"));
softwareModuleManagement.create(
@@ -55,7 +58,20 @@ public class RSQLSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
@Test
@Description("Test filter software module by id")
public void testFilterByParameterId() {
assertRSQLQuery(SoftwareModuleFields.ID.name() + "==" + ah.getId(), 1);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "!=" + ah.getId(), 4);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "!=" + -1, 5);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(SoftwareModuleFields.ID.name() + "==*", 5);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "=in=(" + ah.getId() + ",1000000)", 1);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "=out=(" + ah.getId() + ",1000000)", 4);
}
@Test

View File

@@ -17,6 +17,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -29,7 +30,20 @@ public class RSQLSoftwareModuleTypeFieldsTest extends AbstractJpaIntegrationTest
@Test
@Description("Test filter software module test type by id")
public void testFilterByParameterId() {
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==" + osType.getId(), 1);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "!=" + osType.getId(), 2);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "!=" + -1, 3);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==*", 3);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "=in=(" + osType.getId() + ",1000000)", 1);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "=out=(" + osType.getId() + ",1000000)", 2);
}
@Test

View File

@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -54,7 +55,21 @@ public class RSQLTargetFilterQueryFieldsTest extends AbstractJpaIntegrationTest
@Test
@Description("Test filter target filter query by id")
public void testFilterByParameterId() {
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==" + filter1.getId(), 1);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "!=" + filter1.getId(), 2);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "!=" + -1, 3);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==*", 3);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "=in=(" + filter1.getId() + ",10000000)", 1);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "=out=(" + filter1.getId() + ",10000000)", 2);
}
@Test

View File

@@ -196,8 +196,7 @@ public class RSQLUtilityTest {
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
.thenReturn(pathOfString(baseSoftwareModuleRootMock));
when(criteriaBuilderMock.like(any(Expression.class), anyString(), eq('\\'))).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.equal(any(Expression.class), any(String.class))).thenReturn(mock(Predicate.class));
// test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null, testDb).toPredicate(baseSoftwareModuleRootMock,

View File

@@ -51,6 +51,10 @@
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- Add a JDBC DB2 compatabile driver if you want to run tests against DB2, e.g. -->
<!-- <dependency> -->
<!-- <groupId>com.ibm.db2.jcc</groupId> -->

View File

@@ -107,7 +107,8 @@ import com.google.common.io.Files;
// test execution. So, the order execution between EventVerifier and Cleanup is
// important!
@TestExecutionListeners(inheritListeners = true, listeners = { EventVerifier.class, CleanupTestExecutionListener.class,
MySqlTestDatabase.class, MsSqlTestDatabase.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
MySqlTestDatabase.class, MsSqlTestDatabase.class,
PostgreSqlTestDatabase.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

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2020 Microsoft 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.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;
/**
* A {@link TestExecutionListener} for creating and dropping MySql schemas if
* tests are setup with MySql.
*/
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;
@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();
}
}
@Override
public void afterTestClass(final TestContext testContext) throws Exception {
if (isRunningWithSql()) {
dropSchema();
}
}
protected abstract void createSchemaUri();
protected abstract boolean isRunningWithSql();
protected abstract void createSchema();
protected abstract void dropSchema();
}

View File

@@ -16,53 +16,31 @@ import java.sql.SQLException;
import org.apache.commons.lang3.RandomStringUtils;
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;
/**
* A {@link TestExecutionListener} for creating and dropping MS SQL Server
* schemas if tests are setup with MS SQL Server.
*/
public class MsSqlTestDatabase extends AbstractTestExecutionListener {
public class MsSqlTestDatabase extends AbstractSqlTestDatabase {
private static final Logger LOG = LoggerFactory.getLogger(MsSqlTestDatabase.class);
private String schemaName;
private String uri;
private String username;
private String password;
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
if (isRunningWithMsSql()) {
LOG.info("Setting up mysql schema 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();
}
}
@Override
public void afterTestClass(final TestContext testContext) throws Exception {
if (isRunningWithMsSql()) {
dropSchema();
}
}
private void createSchemaUri() {
protected void createSchemaUri() {
schemaName = "SP" + RandomStringUtils.randomAlphanumeric(10);
this.uri = this.uri.substring(0, uri.indexOf(';'));
System.setProperty("spring.datasource.url", uri + ";database=" + schemaName);
}
private static boolean isRunningWithMsSql() {
@Override
protected boolean isRunningWithSql() {
return "SQL_SERVER".equals(System.getProperty("spring.jpa.database"));
}
private void createSchema() {
@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);
@@ -75,7 +53,8 @@ public class MsSqlTestDatabase extends AbstractTestExecutionListener {
}
private void dropSchema() {
@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

View File

@@ -16,53 +16,31 @@ import java.sql.SQLException;
import org.apache.commons.lang3.RandomStringUtils;
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;
/**
* A {@link TestExecutionListener} for creating and dropping MySql schemas if
* tests are setup with MySql.
*/
public class MySqlTestDatabase extends AbstractTestExecutionListener {
public class MySqlTestDatabase extends AbstractSqlTestDatabase {
private static final Logger LOG = LoggerFactory.getLogger(MySqlTestDatabase.class);
private String schemaName;
private String uri;
private String username;
private String password;
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
if (isRunningWithMySql()) {
LOG.info("Setting up mysql schema 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();
}
}
@Override
public void afterTestClass(final TestContext testContext) throws Exception {
if (isRunningWithMySql()) {
dropSchema();
}
}
private void createSchemaUri() {
protected void createSchemaUri() {
schemaName = "SP" + RandomStringUtils.randomAlphanumeric(10);
this.uri = this.uri.substring(0, uri.lastIndexOf('/') + 1);
System.setProperty("spring.datasource.url", uri + schemaName);
}
private boolean isRunningWithMySql() {
@Override
protected boolean isRunningWithSql() {
return "MYSQL".equals(System.getProperty("spring.jpa.database"));
}
private void createSchema() {
@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);
@@ -75,7 +53,8 @@ public class MySqlTestDatabase extends AbstractTestExecutionListener {
}
private void dropSchema() {
@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);

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2020 Microsoft 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 java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apache.commons.lang3.RandomStringUtils;
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.
*/
public class PostgreSqlTestDatabase extends AbstractSqlTestDatabase {
private static final Logger LOG = LoggerFactory.getLogger(PostgreSqlTestDatabase.class);
@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);
}
@Override
protected boolean isRunningWithSql() {
return "POSTGRESQL".equals(System.getProperty("spring.jpa.database"));
}
@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);
}
}
@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);
}
}
}

View File

@@ -64,12 +64,15 @@ import org.eclipse.hawkbit.util.IpUtil;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.test.web.servlet.MvcResult;
import com.jayway.jsonpath.JsonPath;
@@ -113,6 +116,9 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
private static final String JSON_PATH_DESCRIPTION = JSON_PATH_ROOT + JSON_PATH_FIELD_DESCRIPTION;
private static final String JSON_PATH_LAST_REQUEST_AT = JSON_PATH_ROOT + JSON_PATH_FIELD_LAST_REQUEST_AT;
@Autowired
private JpaProperties jpaProperties;
@Test
@Description("Ensures that actions list is in exptected order.")
public void getActionStatusReturnsCorrectType() throws Exception {
@@ -2015,11 +2021,22 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
assignDistributionSet(dsId, targetId, customWeightHigh);
assignDistributionSet(dsId, targetId, customWeightLow);
mvc.perform(get("/rest/v1/targets/{targetId}/actions", targetId)
.param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "WEIGHT:ASC")).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("content.[0].weight").doesNotExist())
.andExpect(jsonPath("content.[1].weight", equalTo(customWeightLow)))
.andExpect(jsonPath("content.[2].weight", equalTo(customWeightHigh)));
// POSTGRESQL sets null values at the end, not the beginning
if (Database.POSTGRESQL.equals(jpaProperties.getDatabase())) {
mvc.perform(get("/rest/v1/targets/{targetId}/actions", targetId)
.param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "WEIGHT:ASC"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[0].weight", equalTo(customWeightLow)))
.andExpect(jsonPath("content.[1].weight", equalTo(customWeightHigh)))
.andExpect(jsonPath("content.[2].weight").doesNotExist());
} else {
mvc.perform(get("/rest/v1/targets/{targetId}/actions", targetId)
.param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "WEIGHT:ASC"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[0].weight").doesNotExist())
.andExpect(jsonPath("content.[1].weight", equalTo(customWeightLow)))
.andExpect(jsonPath("content.[2].weight", equalTo(customWeightHigh)));
}
}

View File

@@ -15,6 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
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.eclipse.hawkbit.repository.test.util.PostgreSqlTestDatabase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -23,30 +24,31 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.TestExecutionListeners.MergeMode;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.test.context.TestExecutionListeners.MergeMode;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
@RunWith(SpringRunner.class)
@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})
@TestExecutionListeners(listeners = { MySqlTestDatabase.class, MsSqlTestDatabase.class },
mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
@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 })
@TestExecutionListeners(listeners = { MySqlTestDatabase.class, MsSqlTestDatabase.class,
PostgreSqlTestDatabase.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
@Feature("Integration Test - Security")
@Story("CORS")
public class CorsTest {
final static String ALLOWED_ORIGIN_FIRST = "http://test.first.origin";
final static String ALLOWED_ORIGIN_SECOND = "http://test.second.origin";
private final static String INVALID_ORIGIN = "http://test.invalid.origin";
private final static String INVALID_CORS_REQUEST = "Invalid CORS request";
@@ -73,15 +75,16 @@ public class CorsTest {
.andExpect(status().isForbidden()).andReturn().getResponse().getContentAsString();
assertThat(invalidOriginResponseBody).isEqualTo(INVALID_CORS_REQUEST);
final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin(MgmtRestConstants.BASE_SYSTEM_MAPPING, ALLOWED_ORIGIN_FIRST)
.andExpect(status().isForbidden()).andReturn().getResponse().getContentAsString();
final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin(
MgmtRestConstants.BASE_SYSTEM_MAPPING, ALLOWED_ORIGIN_FIRST).andExpect(status().isForbidden())
.andReturn().getResponse().getContentAsString();
assertThat(invalidCorsUrlResponseBody).isEqualTo(INVALID_CORS_REQUEST);
}
private ResultActions performOptionsRequestToRestWithOrigin(final String origin) throws Exception {
return performOptionsRequestToUrlWithOrigin(MgmtRestConstants.BASE_V1_REQUEST_MAPPING, origin);
}
private ResultActions performOptionsRequestToUrlWithOrigin(final String url, final String origin) throws Exception {
return mvc.perform(options(url).header("Access-Control-Request-Method", "GET").header("Origin", origin));
}

View File

@@ -0,0 +1,6 @@
Copyright (c) 2020 Microsoft 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

View File

@@ -167,7 +167,7 @@
<cron-utils.version>9.1.3</cron-utils.version>
<jsoup.version>1.11.2</jsoup.version>
<allure.version>2.13.6</allure.version>
<eclipselink.version>2.7.7</eclipselink.version>
<eclipselink.version>2.7.3</eclipselink.version>
<gwtmockito.version>1.1.8</gwtmockito.version>
<guava.version>25.0-jre</guava.version>
<javax.el-api.version>2.2.4</javax.el-api.version>
@@ -332,11 +332,14 @@
<validHeader>licenses/LICENSE_HEADER_TEMPLATE_BOSCH_20.txt</validHeader>
<validHeader>licenses/LICENSE_HEADER_TEMPLATE_BOSCH_21.txt</validHeader>
<validHeader>licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_18.txt</validHeader>
<validHeader>licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_20.txt</validHeader>
<validHeader>licenses/LICENSE_HEADER_TEMPLATE_DEVOLO_19.txt</validHeader>
<validHeader>licenses/LICENSE_HEADER_TEMPLATE_KIWIGRID_19.txt</validHeader>
<validHeader>licenses/LICENSE_HEADER_TEMPLATE_ENAPTER.txt</validHeader>
</validHeaders>
<excludes>
<exclude>.azure-pipelines/*</exclude>
<exclude>.devcontainer/*</exclude>
<exclude>**/banner.txt</exclude>
<exclude>**/helm/**</exclude>
<exclude>**/README</exclude>