diff --git a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/MessageRepository.java b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/MessageRepository.java index f7491c4a..c5ca022d 100644 --- a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/MessageRepository.java +++ b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/MessageRepository.java @@ -16,11 +16,10 @@ package org.citrusframework.simulator.repository; -import java.util.List; -import java.util.Optional; import org.citrusframework.simulator.model.Message; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; @@ -29,6 +28,9 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + /** * Spring Data JPA repository for the {@link Message} entity. */ @@ -64,5 +66,5 @@ default List findAllForScenarioExecution(Long scenarioExecutionId, Stri @Query("FROM Message WHERE messageId IN :messageIds") @EntityGraph(attributePaths = {"headers", "scenarioExecution", "scenarioExecution.testResult"}) - Page findAllWhereMessageIdIn(@Param("messageIds") List messageIds, Pageable pageable); + List findAllWhereMessageIdIn(@Param("messageIds") List messageIds, Sort sort); } diff --git a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/TestResultRepository.java b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/TestResultRepository.java index c28de067..c43a92c7 100644 --- a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/TestResultRepository.java +++ b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/TestResultRepository.java @@ -16,12 +16,11 @@ package org.citrusframework.simulator.repository; -import java.util.List; -import java.util.Optional; import org.citrusframework.simulator.model.TestResult; import org.citrusframework.simulator.service.dto.TestResultByStatus; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; @@ -29,6 +28,9 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + /** * Spring Data JPA repository for the {@link TestResult} entity. */ @@ -51,5 +53,5 @@ public interface TestResultRepository extends JpaRepository, J @Query("FROM TestResult WHERE id IN :testResultIds") @EntityGraph(attributePaths = {"testParameters"}) - Page findAllWhereIdIn(@Param("testResultIds") List testResultIds, Pageable pageable); + List findAllWhereIdIn(@Param("testResultIds") List testResultIds, Sort sort); } diff --git a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/CriteriaQueryUtils.java b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/CriteriaQueryUtils.java index 00cf4205..d0c2a5ee 100644 --- a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/CriteriaQueryUtils.java +++ b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/CriteriaQueryUtils.java @@ -16,21 +16,23 @@ package org.citrusframework.simulator.service; -import static java.util.Objects.nonNull; -import static org.springframework.data.domain.Pageable.unpaged; - import jakarta.persistence.EntityManager; import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Order; -import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; import jakarta.persistence.metamodel.SingularAttribute; -import java.util.ArrayList; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; +import java.util.ArrayList; +import java.util.List; + +import static java.util.Objects.nonNull; +import static org.springframework.data.domain.Pageable.unpaged; + final class CriteriaQueryUtils { private CriteriaQueryUtils() { @@ -57,16 +59,16 @@ private CriteriaQueryUtils() { * * @see problem with in-memory pagination when having high volume of data */ - static TypedQuery newSelectIdBySpecificationQuery(Class entityClass, SingularAttribute idAttribute, Specification specification, Pageable page, EntityManager entityManager) { - CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + static List selectAllIds(Class entityClass, SingularAttribute idAttribute, Specification specification, Pageable page, EntityManager entityManager) { + var criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Long.class); - Root root = criteriaQuery.from(entityClass); + Root root = criteriaQuery.from(entityClass); - criteriaQuery = selectIdField(idAttribute, criteriaQuery, root); + criteriaQuery = selectIdField(idAttribute, root, criteriaQuery); criteriaQuery = whereSpecificationApplies(specification, root, criteriaQuery, criteriaBuilder); - criteriaQuery = orderByPageSort(page, root, criteriaBuilder, criteriaQuery); + criteriaQuery = orderBy(page.getSort(), root, criteriaQuery, criteriaBuilder); TypedQuery query = entityManager.createQuery(criteriaQuery); @@ -74,7 +76,24 @@ static TypedQuery newSelectIdBySpecificationQuery(Class entityClass query = selectPage(page, query); } - return query; + return query.getResultList(); + } + + /** + * Find all entities matching the {@link Specification}. + */ + static List selectAll(Class entityClass, Specification specification, Pageable page, EntityManager entityManager) { + var criteriaBuilder = entityManager.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(entityClass); + Root root = criteriaQuery.from(entityClass); + + criteriaQuery = whereSpecificationApplies(specification, root, criteriaQuery, criteriaBuilder); + + criteriaQuery = orderBy(page.getSort(), root, criteriaQuery, criteriaBuilder); + + TypedQuery query = entityManager.createQuery(criteriaQuery); + + return query.getResultList(); } /** @@ -82,7 +101,7 @@ static TypedQuery newSelectIdBySpecificationQuery(Class entityClass * * @return the modified {@link CriteriaQuery}. */ - private static CriteriaQuery selectIdField(SingularAttribute idAttribute, CriteriaQuery criteriaQuery, Root root) { + private static CriteriaQuery selectIdField(SingularAttribute idAttribute, Root root, CriteriaQuery criteriaQuery) { return criteriaQuery.select(root.get(idAttribute)); } @@ -91,8 +110,8 @@ private static CriteriaQuery selectIdField(SingularAttribute * * @return the modified {@link CriteriaQuery}. */ - private static CriteriaQuery whereSpecificationApplies(Specification specification, Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) { - Predicate predicate = specification.toPredicate(root, criteriaQuery, criteriaBuilder); + private static CriteriaQuery whereSpecificationApplies(Specification specification, Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) { + var predicate = specification.toPredicate(root, criteriaQuery, criteriaBuilder); if (nonNull(predicate)) { return criteriaQuery.where(predicate); } @@ -104,15 +123,16 @@ private static CriteriaQuery whereSpecificationApplies(Specification}. */ - private static CriteriaQuery orderByPageSort(Pageable page, Root root, CriteriaBuilder criteriaBuilder, CriteriaQuery criteriaQuery) { + private static CriteriaQuery orderBy(Sort sort, Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) { var orders = new ArrayList(); - for (var sortOrder : page.getSort()) { + for (var sortOrder : sort) { var path = root.get(sortOrder.getProperty()); orders.add(sortOrder.isAscending() ? criteriaBuilder.asc(path) : criteriaBuilder.desc(path)); } if (!orders.isEmpty()) { return criteriaQuery.orderBy(orders); } + return criteriaQuery; } @@ -121,7 +141,7 @@ private static CriteriaQuery orderByPageSort(Pageable page, Root ro * * @return The same {@link TypedQuery}. */ - private static TypedQuery selectPage(Pageable page, TypedQuery query) { + private static TypedQuery selectPage(Pageable page, TypedQuery query) { // Calculate the first result index based on the page number and page size var pageSize = page.getPageSize(); var firstResult = page.getPageNumber() * pageSize; diff --git a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/MessageQueryService.java b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/MessageQueryService.java index 92107c53..c4582bc8 100644 --- a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/MessageQueryService.java +++ b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/MessageQueryService.java @@ -34,8 +34,7 @@ import java.util.List; -import static org.citrusframework.simulator.service.CriteriaQueryUtils.newSelectIdBySpecificationQuery; -import static org.springframework.data.domain.Pageable.unpaged; +import static org.citrusframework.simulator.service.CriteriaQueryUtils.selectAllIds; /** * Service for executing complex queries for {@link Message} entities in the database. @@ -67,17 +66,16 @@ public Page findByCriteria(MessageCriteria criteria, Pageable page) { logger.debug("find by criteria : {}, page: {}", criteria, page); var specification = createSpecification(criteria); - var messageIds = newSelectIdBySpecificationQuery( + var messageIds = selectAllIds( Message.class, Message_.messageId, specification, page, entityManager - ) - .getResultList(); + ); - var messages = messageRepository.findAllWhereMessageIdIn(messageIds, unpaged(page.getSort())); - return new PageImpl<>(messages.getContent(), page, messageRepository.count(specification)); + var messages = messageRepository.findAllWhereMessageIdIn(messageIds, page.getSort()); + return new PageImpl<>(messages, page, messageRepository.count(specification)); } /** diff --git a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/QueryService.java b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/QueryService.java index 7f85e597..2941b2bc 100644 --- a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/QueryService.java +++ b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/QueryService.java @@ -16,6 +16,7 @@ package org.citrusframework.simulator.service; +import jakarta.annotation.Nullable; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Root; @@ -32,6 +33,8 @@ import java.util.Set; import java.util.function.Function; +import static java.util.Objects.isNull; + /** * Base service for constructing and executing complex queries. * @@ -145,36 +148,35 @@ protected > Specification buildRangeSpec * @param metaclassFunction lambda, which based on a Root<ENTITY> returns Expression - basicaly picks a column * @return a Specification */ - protected > Specification buildSpecification(RangeFilter filter, - Function, Expression> metaclassFunction) { + protected > Specification buildSpecification( + @Nullable RangeFilter filter, + Function, Expression> metaclassFunction + ) { + Specification result = Specification.where(null); + if (isNull(filter)) { + return result; + } + if (filter.getEquals() != null) { return equalsSpecification(metaclassFunction, filter.getEquals()); } else if (filter.getIn() != null) { return valueIn(metaclassFunction, filter.getIn()); - } - - Specification result = Specification.where(null); - if (filter.getSpecified() != null) { + } else if (filter.getSpecified() != null) { result = result.and(byFieldSpecified(metaclassFunction, filter.getSpecified())); - } - if (filter.getNotEquals() != null) { + } else if (filter.getNotEquals() != null) { result = result.and(notEqualsSpecification(metaclassFunction, filter.getNotEquals())); - } - if (filter.getNotIn() != null) { + } else if (filter.getNotIn() != null) { result = result.and(valueNotIn(metaclassFunction, filter.getNotIn())); - } - if (filter.getGreaterThan() != null) { + } else if (filter.getGreaterThan() != null) { result = result.and(greaterThan(metaclassFunction, filter.getGreaterThan())); - } - if (filter.getGreaterThanOrEqual() != null) { + } else if (filter.getGreaterThanOrEqual() != null) { result = result.and(greaterThanOrEqualTo(metaclassFunction, filter.getGreaterThanOrEqual())); - } - if (filter.getLessThan() != null) { + } else if (filter.getLessThan() != null) { result = result.and(lessThan(metaclassFunction, filter.getLessThan())); - } - if (filter.getLessThanOrEqual() != null) { + } else if (filter.getLessThanOrEqual() != null) { result = result.and(lessThanOrEqualTo(metaclassFunction, filter.getLessThanOrEqual())); } + return result; } @@ -533,7 +535,7 @@ protected > Specification lessThan(Funct * @param txt a {@link java.lang.String} object. * @return a {@link java.lang.String} object. */ - protected String wrapLikeQuery(String txt) { + protected static String wrapLikeQuery(String txt) { return "%" + txt.toUpperCase() + '%'; } diff --git a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/ScenarioExecutionQueryService.java b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/ScenarioExecutionQueryService.java index 6c46c4d5..ea3a5b95 100644 --- a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/ScenarioExecutionQueryService.java +++ b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/ScenarioExecutionQueryService.java @@ -17,11 +17,9 @@ package org.citrusframework.simulator.service; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nullable; import jakarta.persistence.EntityManager; -import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.Path; -import jakarta.persistence.criteria.Predicate; -import jakarta.persistence.criteria.Root; import jakarta.persistence.criteria.SetJoin; import lombok.extern.slf4j.Slf4j; import org.citrusframework.simulator.model.Message; @@ -53,6 +51,7 @@ import java.util.function.Function; import java.util.regex.Pattern; +import static jakarta.persistence.criteria.JoinType.LEFT; import static java.lang.Boolean.TRUE; import static java.lang.Long.parseLong; import static java.lang.String.format; @@ -60,7 +59,8 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.regex.Pattern.compile; -import static org.citrusframework.simulator.service.CriteriaQueryUtils.newSelectIdBySpecificationQuery; +import static org.citrusframework.simulator.service.CriteriaQueryUtils.selectAll; +import static org.citrusframework.simulator.service.CriteriaQueryUtils.selectAllIds; import static org.citrusframework.simulator.service.ScenarioExecutionQueryService.MessageHeaderFilter.fromFilterPattern; import static org.citrusframework.simulator.service.ScenarioExecutionQueryService.Operator.parseOperator; import static org.citrusframework.simulator.service.ScenarioExecutionQueryService.ResultDetailsConfiguration.withAllDetails; @@ -83,17 +83,20 @@ public class ScenarioExecutionQueryService extends QueryService withResultDetailsConfiguration(ResultDetailsConfiguration config) { + private static Specification withIdIn(List executionIds) { + return (root, query, builder) -> { + var in = builder.in(root.get(ScenarioExecution_.executionId)); + executionIds.forEach(in::value); + return in; + }; + } + + private static Specification withResultDetailsConfiguration(ResultDetailsConfiguration resultDetailsConfiguration) { return (root, query, cb) -> { assert query != null; @@ -102,21 +105,21 @@ private static Specification withResultDetailsConfiguration(R return null; } - root.fetch(ScenarioExecution_.testResult, JoinType.LEFT); + root.fetch(ScenarioExecution_.testResult, LEFT); - if (config.includeParameters()) { - root.fetch(ScenarioExecution_.scenarioParameters, JoinType.LEFT); + if (resultDetailsConfiguration.includeParameters()) { + root.fetch(ScenarioExecution_.scenarioParameters, LEFT); } - if (config.includeActions()) { - root.fetch(ScenarioExecution_.scenarioActions, JoinType.LEFT); + if (resultDetailsConfiguration.includeActions()) { + root.fetch(ScenarioExecution_.scenarioActions, LEFT); } - if (config.includeMessages() || config.includeMessageHeaders()) { - var messagesJoin = root.fetch(ScenarioExecution_.scenarioMessages, JoinType.LEFT); - - if (config.includeMessageHeaders()) { - messagesJoin.fetch(Message_.headers, JoinType.LEFT); + if (resultDetailsConfiguration.includeMessages() + || resultDetailsConfiguration.includeMessageHeaders()) { + var messageFetch = root.fetch(ScenarioExecution_.scenarioMessages, LEFT); + if (resultDetailsConfiguration.includeMessageHeaders()) { + messageFetch.fetch(Message_.headers, LEFT); } } @@ -125,14 +128,48 @@ private static Specification withResultDetailsConfiguration(R }; } - private static Specification withIds(List executionIds) { - return (root, query, builder) -> { - var in = builder.in(root.get(ScenarioExecution_.executionId)); - executionIds.forEach(in::value); - return in; + private static Specification buildSpecification(@Nonnull Filter filter, Path path) { + return (root, query, criteriaBuilder) -> { + if (filter.getEquals() != null) { + return criteriaBuilder.equal(path, filter.getEquals()); + } else if (filter.getNotEquals() != null) { + return criteriaBuilder.notEqual(path, filter.getNotEquals()); + } else if (filter.getIn() != null) { + return path.in(filter.getIn()); + } else if (filter.getNotIn() != null) { + return criteriaBuilder.not(path.in(filter.getNotIn())); + } else if (filter.getSpecified() != null) { + return TRUE.equals(filter.getSpecified()) ? criteriaBuilder.isNotNull(path) : criteriaBuilder.isNull(path); + } else if (filter instanceof StringFilter stringFilter) { + if (nonNull(stringFilter.getEqualsIgnoreCase())){ + return criteriaBuilder.equal(criteriaBuilder.upper(path.as(String.class)), stringFilter.getEqualsIgnoreCase().toUpperCase()); + } else if (nonNull(stringFilter.getContains())) { + return criteriaBuilder.like(criteriaBuilder.upper(path.as(String.class)), wrapLikeQuery(stringFilter.getContains().toUpperCase())); + } else if (nonNull(stringFilter.getDoesNotContain())) { + return criteriaBuilder.like(criteriaBuilder.upper(path.as(String.class)), wrapLikeQuery(stringFilter.getDoesNotContain().toUpperCase())); + } + } else if (filter instanceof RangeFilter rangeFilter) { + var comparableExpression = path.as(Comparable.class); + if (nonNull(rangeFilter.getGreaterThan())) { + return criteriaBuilder.greaterThan(comparableExpression, rangeFilter.getGreaterThan()); + } else if (nonNull(rangeFilter.getGreaterThanOrEqual())) { + return criteriaBuilder.greaterThanOrEqualTo(comparableExpression, rangeFilter.getGreaterThanOrEqual()); + } else if (nonNull(rangeFilter.getLessThan())) { + return criteriaBuilder.lessThan(comparableExpression, rangeFilter.getLessThan()); + } else if (nonNull(rangeFilter.getLessThanOrEqual())) { + return criteriaBuilder.lessThanOrEqualTo(comparableExpression, rangeFilter.getLessThanOrEqual()); + } + } + + return null; }; } + public ScenarioExecutionQueryService(EntityManager entityManager, ScenarioExecutionRepository scenarioExecutionRepository) { + this.entityManager = entityManager; + this.scenarioExecutionRepository = scenarioExecutionRepository; + } + /** * Return a {@link List} of {@link ScenarioExecution} which matches the criteria from the database. * @@ -171,29 +208,22 @@ public Page findByCriteria(ScenarioExecutionCriteria criteria logger.debug("find by criteria : {}, page: {}", criteria, page); var specification = createSpecification(criteria); - var scenarioExecutionIds = newSelectIdBySpecificationQuery( - ScenarioExecution.class, - ScenarioExecution_.executionId, - specification, - page, - entityManager - ) - .getResultList(); - + var scenarioExecutionIds = selectAllIds(ScenarioExecution.class, ScenarioExecution_.executionId, specification, page, entityManager); if (scenarioExecutionIds.isEmpty()) { return Page.empty(page); } - var fetchSpec = withIds(scenarioExecutionIds) - .and(withResultDetailsConfiguration(resultDetailsConfiguration)); - - var scenarioExecutions = scenarioExecutionRepository.findAll(fetchSpec, page.getSort()); + var countSpec = withIdIn(scenarioExecutionIds).and(specification); + var fetchSpec = countSpec.and(withResultDetailsConfiguration(resultDetailsConfiguration)); - return new PageImpl<>( - scenarioExecutions, + var scenarioExecutions = selectAll( + ScenarioExecution.class, + fetchSpec, page, - scenarioExecutionRepository.count(specification) + entityManager ); + + return new PageImpl<>(scenarioExecutions, page, scenarioExecutionRepository.count(specification)); } /** @@ -215,7 +245,7 @@ public long countByCriteria(ScenarioExecutionCriteria criteria) { * @param criteria The object which holds all the filters, which the entities should match. * @return the matching {@link Specification} of the entity. */ - protected Specification createSpecification(ScenarioExecutionCriteria criteria) { + protected Specification createSpecification(@Nullable ScenarioExecutionCriteria criteria) { Specification specification = Specification.where(null); if (isNull(criteria)) { return specification; @@ -244,20 +274,24 @@ protected Specification createSpecification(ScenarioExecution } // Join testResult for status filter - if (nonNull(criteria.getStatus())) { - specification = specification.and( - buildSpecification(criteria.getStatus(), root -> root.join(ScenarioExecution_.testResult, JoinType.LEFT).get(TestResult_.status)) - ); - } + specification = specification.and((root, query, criteriaBuilder) -> { + var predicate = criteriaBuilder.conjunction(); + var testResultJoin = root.join(ScenarioExecution_.testResult, LEFT); + + if (nonNull(criteria.getStatus())) { + predicate = criteriaBuilder.and( + predicate, buildSpecification(criteria.getStatus(), r -> testResultJoin.get(TestResult_.status)).toPredicate(root, query, criteriaBuilder) + ); + } - // Grouped join for scenarioMessages-related filters - if (nonNull(criteria.getScenarioMessagesId()) - || nonNull(criteria.getScenarioMessagesDirection()) - || nonNull(criteria.getScenarioMessagesPayload())) { + return predicate; + }); + // Grouped join for scenarioMessages-related filters + if (shouldJoinMessagesOrHeaders(criteria)) { specification = specification.and((root, query, criteriaBuilder) -> { - var messageJoin = root.join(ScenarioExecution_.scenarioMessages, JoinType.LEFT); - Predicate predicate = criteriaBuilder.conjunction(); + var predicate = criteriaBuilder.conjunction(); + var messageJoin = root.join(ScenarioExecution_.scenarioMessages, LEFT); if (nonNull(criteria.getScenarioMessagesId())) { predicate = criteriaBuilder.and( @@ -277,79 +311,75 @@ predicate, buildSpecification(criteria.getScenarioMessagesPayload(), messageJoin ); } + // Message headers filter + if (nonNull(criteria.getHeaders())) { + var messageHeaderSpecifications = createMessageHeaderSpecifications(criteria.getHeaders(), messageJoin); + for (var messageHeaderSpecification : messageHeaderSpecifications) { + predicate = criteriaBuilder.and( + predicate, messageHeaderSpecification.toPredicate(root, query, criteriaBuilder) + ); + } + } + return predicate; }); } // Single join for scenarioActions if (nonNull(criteria.getScenarioActionsId())) { - specification = specification.and( - buildSpecification(criteria.getScenarioActionsId(), root -> root.join(ScenarioExecution_.scenarioActions, JoinType.LEFT).get(ScenarioAction_.actionId)) - ); + specification = specification.and((root, query, criteriaBuilder) -> { + var predicate = criteriaBuilder.conjunction(); + var scenarioExecutionJoin = root.join(ScenarioExecution_.scenarioActions, LEFT); + + if (nonNull(criteria.getScenarioActionsId())) { + predicate = criteriaBuilder.and( + predicate, + buildSpecification(criteria.getScenarioActionsId(), r -> scenarioExecutionJoin.get(ScenarioAction_.actionId)).toPredicate(root, query, criteriaBuilder) + ); + } + + return predicate; + }); } // Single join for scenarioParameters if (nonNull(criteria.getScenarioParametersId())) { - specification = specification.and( - buildSpecification(criteria.getScenarioParametersId(), root -> root.join(ScenarioExecution_.scenarioParameters, JoinType.LEFT).get(ScenarioParameter_.parameterId)) - ); - } + specification = specification.and((root, query, criteriaBuilder) -> { + var predicate = criteriaBuilder.conjunction(); + var scenarioParameterJoin = root.join(ScenarioExecution_.scenarioParameters, LEFT); - // Message headers filter - if (nonNull(criteria.getHeaders())) { - var messageHeaderSpecifications = createMessageHeaderSpecifications(criteria.getHeaders()); - for (var messageHeaderSpecification : messageHeaderSpecifications) { - specification = specification.and(messageHeaderSpecification); - } + if (nonNull(criteria.getScenarioParametersId())) { + predicate = criteriaBuilder.and( + predicate, + buildSpecification(criteria.getScenarioParametersId(), r -> scenarioParameterJoin.get(ScenarioParameter_.parameterId)).toPredicate(root, query, criteriaBuilder) + ); + } + + return predicate; + }); } return specification; } - private Specification buildSpecification(@Nonnull Filter filter, Path path) { - return (root, query, criteriaBuilder) -> { - if (filter.getEquals() != null) { - return criteriaBuilder.equal(path, filter.getEquals()); - } else if (filter.getNotEquals() != null) { - return criteriaBuilder.notEqual(path, filter.getNotEquals()); - } else if (filter.getIn() != null) { - return path.in(filter.getIn()); - } else if (filter.getNotIn() != null) { - return criteriaBuilder.not(path.in(filter.getNotIn())); - } else if (filter.getSpecified() != null) { - return TRUE.equals(filter.getSpecified()) ? criteriaBuilder.isNotNull(path) : criteriaBuilder.isNull(path); - } else if (filter instanceof StringFilter stringFilter) { - if (nonNull(stringFilter.getEqualsIgnoreCase())){ - return criteriaBuilder.equal(criteriaBuilder.upper(path.as(String.class)), stringFilter.getEqualsIgnoreCase().toUpperCase()); - } else if (nonNull(stringFilter.getContains())) { - return criteriaBuilder.like(criteriaBuilder.upper(path.as(String.class)), wrapLikeQuery(stringFilter.getContains().toUpperCase())); - } else if (nonNull(stringFilter.getDoesNotContain())) { - return criteriaBuilder.like(criteriaBuilder.upper(path.as(String.class)), wrapLikeQuery(stringFilter.getDoesNotContain().toUpperCase())); - } - } else if (filter instanceof RangeFilter rangeFilter) { - var comparableExpression = path.as(Comparable.class); - if (nonNull(rangeFilter.getGreaterThan())) { - return criteriaBuilder.greaterThan(comparableExpression, rangeFilter.getGreaterThan()); - } else if (nonNull(rangeFilter.getGreaterThanOrEqual())) { - return criteriaBuilder.greaterThanOrEqualTo(comparableExpression, rangeFilter.getGreaterThanOrEqual()); - } else if (nonNull(rangeFilter.getLessThan())) { - return criteriaBuilder.lessThan(comparableExpression, rangeFilter.getLessThan()); - } else if (nonNull(rangeFilter.getLessThanOrEqual())) { - return criteriaBuilder.lessThanOrEqualTo(comparableExpression, rangeFilter.getLessThanOrEqual()); - } - } - - return null; - }; + private static boolean shouldJoinMessagesOrHeaders(ScenarioExecutionCriteria criteria) { + return nonNull(criteria.getScenarioMessagesId()) + || nonNull(criteria.getScenarioMessagesDirection()) + || nonNull(criteria.getScenarioMessagesPayload()) + || nonNull(criteria.getHeaders()); } - private List> createMessageHeaderSpecifications(String headers) { + private List> createMessageHeaderSpecifications(@Nullable String headers, SetJoin messageJoin) { List> specifications = new ArrayList<>(); + if (isEmpty(headers)) { + return specifications; + } var filterPatterns = headers.split(MULTIPLE_FILTER_EXPRESSION_SEPARATOR); for (var filterPattern : filterPatterns) { + var messageHeaderJoin = messageJoin.join(Message_.headers, LEFT); newMessageHeaderFilterFromFilterPattern(filterPattern) - .map(this::createSpecificationFromMessageHeaderFilter) + .map(filter -> createSpecificationFromMessageHeaderFilter(filter, messageHeaderJoin)) .ifPresent(specifications::add); } @@ -365,48 +395,38 @@ private Optional newMessageHeaderFilterFromFilterPattern(St } } - private Specification createSpecificationFromMessageHeaderFilter(MessageHeaderFilter messageHeaderFilter) { + private Specification createSpecificationFromMessageHeaderFilter(MessageHeaderFilter messageHeaderFilter, SetJoin messageHeaderJoin) { if (messageHeaderFilter.isValueFilterOnly()) { return buildSpecification( new StringFilter().setContains(messageHeaderFilter.value), - ScenarioExecutionQueryService::joinMessageHeadersAndGetValue); + r -> messageHeaderJoin.get(MessageHeader_.value)); } Specification messageHeaderKeyEqualsSpecification = buildSpecification( new StringFilter().setEqualsIgnoreCase(messageHeaderFilter.key), - root -> joinMessageHeaders(root).get(MessageHeader_.name)); + r -> messageHeaderJoin.get(MessageHeader_.name)); var messageHeaderValueSpecification = switch (messageHeaderFilter.operator) { case EQUALS, CONTAINS -> - buildMessageHeaderValueSpecification((StringFilter) messageHeaderFilter.operator.filter.apply(messageHeaderFilter.value)); + buildMessageHeaderValueSpecification((StringFilter) messageHeaderFilter.operator.filter.apply(messageHeaderFilter.value), messageHeaderJoin); case GREATER_THAN, GREATER_THAN_OR_EQUAL_TO, LESS_THAN, LESS_THAN_OR_EQUAL_TO -> - buildMessageHeaderValueSpecification((LongFilter) messageHeaderFilter.operator.filter.apply(messageHeaderFilter.value)); + buildMessageHeaderValueSpecification((LongFilter) messageHeaderFilter.operator.filter.apply(messageHeaderFilter.value), messageHeaderJoin); }; return messageHeaderKeyEqualsSpecification.and(messageHeaderValueSpecification); } - private Specification buildMessageHeaderValueSpecification(StringFilter stringFilter) { - return buildSpecification(stringFilter, ScenarioExecutionQueryService::joinMessageHeadersAndGetValue); - } - - private Specification buildMessageHeaderValueSpecification(LongFilter longFilter) { - return buildSpecification(longFilter, root -> joinMessageHeadersAndGetValue(root).as(Long.class)); - } - - private static SetJoin joinMessageHeaders(Root root) { - return root.join(ScenarioExecution_.scenarioMessages, JoinType.LEFT) - .join(Message_.headers, JoinType.LEFT); + private Specification buildMessageHeaderValueSpecification(StringFilter stringFilter, SetJoin messageHeaderJoin) { + return buildSpecification(stringFilter, r -> messageHeaderJoin.get(MessageHeader_.value)); } - private static Path joinMessageHeadersAndGetValue(Root root) { - return joinMessageHeaders(root) - .get(MessageHeader_.value); + private Specification buildMessageHeaderValueSpecification(LongFilter longFilter, SetJoin messageHeaderJoin) { + return buildSpecification(longFilter, r -> messageHeaderJoin.get(MessageHeader_.value).as(Long.class)); } enum Operator { - EQUALS("=", value -> new StringFilter().setContains(value)), + EQUALS("=", value -> new StringFilter().setEqualsIgnoreCase(value)), CONTAINS("~", value -> new StringFilter().setContains(value)), GREATER_THAN(">", value -> new LongFilter().setGreaterThan(parseLong(value))), GREATER_THAN_OR_EQUAL_TO(">=", value -> new LongFilter().setGreaterThanOrEqual(parseLong(value))), diff --git a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/TestResultQueryService.java b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/TestResultQueryService.java index ed2277ba..07fd8e25 100644 --- a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/TestResultQueryService.java +++ b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/service/TestResultQueryService.java @@ -33,8 +33,7 @@ import java.util.List; -import static org.citrusframework.simulator.service.CriteriaQueryUtils.newSelectIdBySpecificationQuery; -import static org.springframework.data.domain.Pageable.unpaged; +import static org.citrusframework.simulator.service.CriteriaQueryUtils.selectAllIds; /** * Service for executing complex queries for {@link TestResult} entities in the database. @@ -66,17 +65,16 @@ public Page findByCriteria(TestResultCriteria criteria, Pageable pag logger.debug("find by criteria : {}, page: {}", criteria, page); var specification = createSpecification(criteria); - var testResultIds = newSelectIdBySpecificationQuery( + var testResultIds = selectAllIds( TestResult.class, TestResult_.id, specification, page, entityManager - ) - .getResultList(); + ); - var testResults = testResultRepository.findAllWhereIdIn(testResultIds, unpaged(page.getSort())); - return new PageImpl<>(testResults.getContent(), page, testResultRepository.count(specification)); + var testResults = testResultRepository.findAllWhereIdIn(testResultIds, page.getSort()); + return new PageImpl<>(testResults, page, testResultRepository.count(specification)); } /** diff --git a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/service/ScenarioExecutionQueryServiceIT.java b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/service/ScenarioExecutionQueryServiceIT.java index 7d185ddd..41aa966a 100644 --- a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/service/ScenarioExecutionQueryServiceIT.java +++ b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/service/ScenarioExecutionQueryServiceIT.java @@ -60,6 +60,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.InstanceOfAssertFactories.type; +import static org.citrusframework.simulator.model.Message.Direction.OUTBOUND; import static org.citrusframework.simulator.model.TestResult.Status.FAILURE; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.springframework.data.domain.Pageable.unpaged; @@ -125,7 +126,8 @@ void beforeEachSetup() { .addScenarioMessage( MessageResourceIT.createEntityBuilder(entityManager) .citrusMessageId("6eda9f2b-3a7a-423f-b19c-329ed3dd5ecc") - .payload("bar") + .direction(Message.Direction.OUTBOUND) + .payload("pong") .build() .addHeader(MessageHeader.builder() .name(TRACEPARENT) @@ -325,6 +327,17 @@ void selectWithSelectiveJoinMessageHeaderMultipleCriteria() { assertThatScenarioExecutionAtIndexSelectedByCriteria(scenarioExecutionCriteria, 1); } + @Test + void selectWithDirectionalMessageFilter() { + var scenarioExecutionCriteria = new ScenarioExecutionCriteria(); + scenarioExecutionCriteria.setScenarioMessagesDirection((IntegerFilter) new IntegerFilter().setEquals(OUTBOUND.getId())); + + Page scenarioExecutionPage = fixture.findByCriteria(scenarioExecutionCriteria, unpaged()); + + assertThat(scenarioExecutionPage.getTotalPages()).isEqualTo(1); + assertThat(scenarioExecutionPage.getTotalElements()).isEqualTo(1L); + } + static Stream testPagination() { return Stream.of( arguments(1, 3), diff --git a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceIT.java b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceIT.java index 7f51842d..c33e1d51 100644 --- a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceIT.java +++ b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceIT.java @@ -30,6 +30,7 @@ import org.citrusframework.simulator.scenario.AbstractSimulatorScenario; import org.citrusframework.simulator.scenario.Scenario; import org.citrusframework.simulator.scenario.ScenarioRunner; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -37,13 +38,13 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.transaction.annotation.Transactional; import java.time.Instant; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import static java.time.LocalDateTime.now; import static java.time.temporal.ChronoUnit.SECONDS; @@ -55,6 +56,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.nullValue; import static org.springframework.http.HttpStatus.OK; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -94,6 +96,9 @@ public class ScenarioExecutionResourceIT { @Autowired private MockMvc mockMvc; + @Autowired + private TransactionUtils transactionUtils; + private ScenarioExecution scenarioExecution; public static ScenarioExecutionBuilder createEntityBuilder(EntityManager entityManager) { @@ -130,22 +135,48 @@ public static ScenarioExecution createUpdatedEntity(EntityManager entityManager) @BeforeEach void beforeEachSetup() { - var scenarioParameter = ScenarioParameterResourceIT.createEntity(entityManager); - var testResult = TestResultResourceIT.createEntity(entityManager); - var scenarioAction = ScenarioActionResourceIT.createEntity(entityManager); - var message = MessageHeaderResourceIT.createEntity(entityManager).getMessage(); + transactionUtils.doWithinTransaction( + () -> { + var scenarioParameter = ScenarioParameterResourceIT.createEntity(entityManager); + var testResult = TestResultResourceIT.createEntity(entityManager); + var scenarioAction = ScenarioActionResourceIT.createEntity(entityManager); + var message = MessageHeaderResourceIT.createEntity(entityManager).getMessage(); + + scenarioExecution = createEntity(entityManager) + .withTestResult(testResult) + .addScenarioParameter(scenarioParameter) + .addScenarioAction(scenarioAction) + .addScenarioMessage(message); + + scenarioExecutionRepository.saveAndFlush(scenarioExecution); + } + ); + } - scenarioExecution = createEntity(entityManager) - .withTestResult(testResult) - .addScenarioParameter(scenarioParameter) - .addScenarioAction(scenarioAction) - .addScenarioMessage(message); + @AfterEach + void afterEachCleanup() { + transactionUtils.doWithinTransaction(() -> scenarioExecutionRepository.delete(scenarioExecution)); + } - scenarioExecutionRepository.saveAndFlush(scenarioExecution); + @Test + void getAllScenarioExecutions() throws Exception { + // Get all the scenarioExecutionList + mockMvc + .perform(get(ENTITY_API_URL + "?sort=executionId,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$").value(hasSize(1))) + .andExpect(jsonPath("$.[0].executionId").value(equalTo(scenarioExecution.getExecutionId().intValue()))) + .andExpect(jsonPath("$.[0].startDate").value(equalTo(DEFAULT_START_DATE.toString()))) + .andExpect(jsonPath("$.[0].endDate").value(equalTo(DEFAULT_END_DATE.toString()))) + .andExpect(jsonPath("$.[0].scenarioName").value(equalTo(DEFAULT_SCENARIO_NAME))) + .andExpect(jsonPath("$.[0].testResult.status").value(equalTo(SUCCESS.toString()))) + .andExpect(jsonPath("$.[0].scenarioParameters").value(nullValue())) + .andExpect(jsonPath("$.[0].scenarioActions").value(nullValue())) + .andExpect(jsonPath("$.[0].scenarioMessages").value(nullValue())); } @Test - @Transactional void getAllScenarioExecutions_withAllDetails() throws Exception { // Get all the scenarioExecutionList mockMvc @@ -165,7 +196,6 @@ void getAllScenarioExecutions_withAllDetails() throws Exception { } @Test - @Transactional void getScenarioExecution() throws Exception { // Get the scenarioExecution mockMvc @@ -180,7 +210,6 @@ void getScenarioExecution() throws Exception { } @Test - @Transactional void getScenarioExecutionsByIdFiltering() throws Exception { Long executionId = scenarioExecution.getExecutionId(); @@ -195,7 +224,6 @@ void getScenarioExecutionsByIdFiltering() throws Exception { } @Test - @Transactional void getAllScenarioExecutionsByStartDateIsEqualToSomething() throws Exception { // Get all the scenarioExecutionList where startDate equals to DEFAULT_START_DATE defaultScenarioExecutionShouldBeFound("startDate.equals=" + DEFAULT_START_DATE); @@ -205,7 +233,6 @@ void getAllScenarioExecutionsByStartDateIsEqualToSomething() throws Exception { } @Test - @Transactional void getAllScenarioExecutionsByStartDateIsInShouldWork() throws Exception { // Get all the scenarioExecutionList where startDate in DEFAULT_START_DATE or UPDATED_START_DATE defaultScenarioExecutionShouldBeFound("startDate.in=" + DEFAULT_START_DATE + "," + UPDATED_START_DATE); @@ -215,7 +242,6 @@ void getAllScenarioExecutionsByStartDateIsInShouldWork() throws Exception { } @Test - @Transactional void getAllScenarioExecutionsByStartDateIsNullOrNotNull() throws Exception { // Get all the scenarioExecutionList where startDate is not null defaultScenarioExecutionShouldBeFound("startDate.specified=true"); @@ -225,7 +251,6 @@ void getAllScenarioExecutionsByStartDateIsNullOrNotNull() throws Exception { } @Test - @Transactional void getAllTestParametersByCreatedDateIsGreaterThanOrEqualToSomething() throws Exception { // Get all the testParameterList where startDate is greater than or equal to DEFAULT_CREATED_DATE defaultScenarioExecutionShouldBeFound("startDate.greaterThanOrEqual=" + DEFAULT_START_DATE); @@ -235,7 +260,6 @@ void getAllTestParametersByCreatedDateIsGreaterThanOrEqualToSomething() throws E } @Test - @Transactional void getAllTestParametersByCreatedDateIsLessThanOrEqualToSomething() throws Exception { // Get all the testParameterList where startDate is less than or equal to DEFAULT_CREATED_DATE defaultScenarioExecutionShouldBeFound("startDate.lessThanOrEqual=" + DEFAULT_START_DATE); @@ -245,7 +269,6 @@ void getAllTestParametersByCreatedDateIsLessThanOrEqualToSomething() throws Exce } @Test - @Transactional void getAllScenarioExecutionsByEndDateIsEqualToSomething() throws Exception { // Get all the scenarioExecutionList where endDate equals to DEFAULT_END_DATE defaultScenarioExecutionShouldBeFound("endDate.equals=" + DEFAULT_END_DATE); @@ -255,7 +278,6 @@ void getAllScenarioExecutionsByEndDateIsEqualToSomething() throws Exception { } @Test - @Transactional void getAllScenarioExecutionsByEndDateIsInShouldWork() throws Exception { // Get all the scenarioExecutionList where endDate in DEFAULT_END_DATE or UPDATED_END_DATE defaultScenarioExecutionShouldBeFound("endDate.in=" + DEFAULT_END_DATE + "," + UPDATED_END_DATE); @@ -265,7 +287,6 @@ void getAllScenarioExecutionsByEndDateIsInShouldWork() throws Exception { } @Test - @Transactional void getAllScenarioExecutionsByEndDateIsNullOrNotNull() throws Exception { // Get all the scenarioExecutionList where endDate is not null defaultScenarioExecutionShouldBeFound("endDate.specified=true"); @@ -275,7 +296,6 @@ void getAllScenarioExecutionsByEndDateIsNullOrNotNull() throws Exception { } @Test - @Transactional void getAllTestParametersByLastModifiedDateIsGreaterThanOrEqualToSomething() throws Exception { // Get all the testParameterList where endDate is greater than or equal to DEFAULT_LAST_MODIFIED_DATE defaultScenarioExecutionShouldBeFound("endDate.greaterThanOrEqual=" + DEFAULT_END_DATE); @@ -285,7 +305,6 @@ void getAllTestParametersByLastModifiedDateIsGreaterThanOrEqualToSomething() thr } @Test - @Transactional void getAllTestParametersByLastModifiedDateIsLessThanOrEqualToSomething() throws Exception { // Get all the testParameterList where endDate is less than or equal to DEFAULT_LAST_MODIFIED_DATE defaultScenarioExecutionShouldBeFound("endDate.lessThanOrEqual=" + DEFAULT_END_DATE); @@ -295,7 +314,6 @@ void getAllTestParametersByLastModifiedDateIsLessThanOrEqualToSomething() throws } @Test - @Transactional void getAllScenarioExecutionsByScenarioNameIsEqualToSomething() throws Exception { // Get all the scenarioExecutionList where scenarioName equals to DEFAULT_SCENARIO_NAME defaultScenarioExecutionShouldBeFound("scenarioName.equals=" + DEFAULT_SCENARIO_NAME); @@ -305,7 +323,6 @@ void getAllScenarioExecutionsByScenarioNameIsEqualToSomething() throws Exception } @Test - @Transactional void getAllScenarioExecutionsByScenarioNameIsInShouldWork() throws Exception { // Get all the scenarioExecutionList where scenarioName in DEFAULT_SCENARIO_NAME or UPDATED_SCENARIO_NAME defaultScenarioExecutionShouldBeFound("scenarioName.in=" + DEFAULT_SCENARIO_NAME + "," + UPDATED_SCENARIO_NAME); @@ -315,7 +332,6 @@ void getAllScenarioExecutionsByScenarioNameIsInShouldWork() throws Exception { } @Test - @Transactional void getAllScenarioExecutionsByScenarioNameIsNullOrNotNull() throws Exception { // Get all the scenarioExecutionList where scenarioName is not null defaultScenarioExecutionShouldBeFound("scenarioName.specified=true"); @@ -325,7 +341,6 @@ void getAllScenarioExecutionsByScenarioNameIsNullOrNotNull() throws Exception { } @Test - @Transactional void getAllScenarioExecutionsByScenarioNameContainsSomething() throws Exception { // Get all the scenarioExecutionList where scenarioName contains DEFAULT_SCENARIO_NAME defaultScenarioExecutionShouldBeFound("scenarioName.contains=" + DEFAULT_SCENARIO_NAME); @@ -335,7 +350,6 @@ void getAllScenarioExecutionsByScenarioNameContainsSomething() throws Exception } @Test - @Transactional void getAllScenarioExecutionsByScenarioNameNotContainsSomething() throws Exception { // Get all the scenarioExecutionList where scenarioName does not contain DEFAULT_SCENARIO_NAME defaultScenarioExecutionShouldNotBeFound("scenarioName.doesNotContain=" + DEFAULT_SCENARIO_NAME); @@ -345,7 +359,6 @@ void getAllScenarioExecutionsByScenarioNameNotContainsSomething() throws Excepti } @Test - @Transactional void getAllScenarioExecutionsByStatusIsEqualToSomething() throws Exception { // Get all the scenarioExecutionList where status equals to DEFAULT_STATUS defaultScenarioExecutionShouldBeFound("status.equals=" + DEFAULT_STATUS.getId()); @@ -355,7 +368,6 @@ void getAllScenarioExecutionsByStatusIsEqualToSomething() throws Exception { } @Test - @Transactional void getAllScenarioExecutionsByStatusIsInShouldWork() throws Exception { // Get all the scenarioExecutionList where status in DEFAULT_STATUS or UPDATED_STATUS defaultScenarioExecutionShouldBeFound("status.in=" + DEFAULT_STATUS.getId() + "," + UPDATED_STATUS.getId()); @@ -365,7 +377,6 @@ void getAllScenarioExecutionsByStatusIsInShouldWork() throws Exception { } @Test - @Transactional void getAllScenarioExecutionsByStatusIsNullOrNotNull() throws Exception { // Get all the scenarioExecutionList where status is not null defaultScenarioExecutionShouldBeFound("status.specified=true"); @@ -375,7 +386,6 @@ void getAllScenarioExecutionsByStatusIsNullOrNotNull() throws Exception { } @Test - @Transactional void getAllScenarioExecutionsByScenarioActionIsEqualToSomething() throws Exception { Long scenarioActionId = TestUtil.findAll(entityManager, ScenarioAction.class).get(0).getActionId(); @@ -387,8 +397,7 @@ void getAllScenarioExecutionsByScenarioActionIsEqualToSomething() throws Excepti } @Test - @Transactional - void getAllScenarioExecutionsByScenarioMessagesIsEqualToSomething() throws Exception { + void getAllScenarioExecutionsByScenarioMessagesIdIsEqualToSomething() throws Exception { Long scenarioMessagesId = TestUtil.findAll(entityManager, Message.class).get(0).getMessageId(); // Get all the scenarioExecutionList where scenarioMessages equals to scenarioMessagesId @@ -399,21 +408,24 @@ void getAllScenarioExecutionsByScenarioMessagesIsEqualToSomething() throws Excep } @Test - @Transactional void getAllScenarioExecutionsByScenarioMessageDirectionIsEqualToSomething() throws Exception { - Message scenarioMessages; - if (TestUtil.findAll(entityManager, Message.class).isEmpty()) { + AtomicReference message = new AtomicReference<>(); + + transactionUtils.doWithinTransaction(() -> { + if (TestUtil.findAll(entityManager, Message.class).isEmpty()) { + scenarioExecutionRepository.saveAndFlush(scenarioExecution); + message.set(MessageResourceIT.createEntity(entityManager)); + } else { + message.set(TestUtil.findAll(entityManager, Message.class).get(0)); + } + message.get().setDirection(Message.Direction.INBOUND); + entityManager.persist(message.get()); + entityManager.flush(); + scenarioExecution.addScenarioMessage(message.get()); scenarioExecutionRepository.saveAndFlush(scenarioExecution); - scenarioMessages = MessageResourceIT.createEntity(entityManager); - } else { - scenarioMessages = TestUtil.findAll(entityManager, Message.class).get(0); - } - scenarioMessages.setDirection(Message.Direction.INBOUND); - entityManager.persist(scenarioMessages); - entityManager.flush(); - scenarioExecution.addScenarioMessage(scenarioMessages); - scenarioExecutionRepository.saveAndFlush(scenarioExecution); - int scenarioMessageDirection = scenarioMessages.getDirection().getId(); + }); + + int scenarioMessageDirection = message.get().getDirection().getId(); // Get all the scenarioExecutionList where scenarioMessagesDirection equals to scenarioMessagesDirection defaultScenarioExecutionShouldBeFound("scenarioMessagesDirection.equals=" + scenarioMessageDirection); @@ -422,7 +434,6 @@ void getAllScenarioExecutionsByScenarioMessageDirectionIsEqualToSomething() thro } @Test - @Transactional void getAllScenarioExecutionsByScenarioMessagesPayloadIsEqualToSomething() throws Exception { // Get all the scenarioExecutionList where payload equals the default payload defaultScenarioExecutionShouldBeFound("scenarioMessagesPayload.equals=" + DEFAULT_PAYLOAD); @@ -432,7 +443,6 @@ void getAllScenarioExecutionsByScenarioMessagesPayloadIsEqualToSomething() throw } @Test - @Transactional void getAllScenarioExecutionsByScenarioParametersIsEqualToSomething() throws Exception { Long scenarioParametersId = TestUtil.findAll(entityManager, ScenarioParameter.class).get(0).getParameterId(); @@ -485,7 +495,6 @@ private void defaultScenarioExecutionShouldNotBeFound(String filter) throws Exce } @Test - @Transactional void getNonExistingScenarioExecution() throws Exception { // Get the scenarioExecution mockMvc.perform(get(ENTITY_API_URL_ID, Long.MAX_VALUE)).andExpect(status().isNotFound()); @@ -503,7 +512,6 @@ class CorrectTimeOnScenarioExecution { private MockMvc mockMvc; @Test - @Transactional void shouldInvokeScenario() throws Exception { String mockEndpointResult = mockMvc .perform(get("/services/rest/api/v1/ZmNrqCkoGQ")) diff --git a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/TransactionUtils.java b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/TransactionUtils.java new file mode 100644 index 00000000..950d3b5e --- /dev/null +++ b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/TransactionUtils.java @@ -0,0 +1,13 @@ +package org.citrusframework.simulator.web.rest; + +import jakarta.transaction.Transactional; +import org.springframework.stereotype.Component; + +@Component +class TransactionUtils { + + @Transactional + void doWithinTransaction(Runnable runnable) { + runnable.run(); + } +}