diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 18a73d4ce..4d4cb830f 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -36,7 +36,7 @@ on: - 'LICENSE' - 'NOTICE' jobs: - build: + simulator-ui: strategy: fail-fast: true matrix: @@ -51,11 +51,15 @@ jobs: uses: actions/checkout@v4 - name: Info run: | - node -version - npm -version + node --version + npm --version - name: Change to Citrus-Simulator UI run: cd simulator-ui - name: Install dependencies run: npm ci --cache .npm + - name: Lint + run: npm run lint + - name: Prettier + run: npm run prettier:check - name: Frontend Tests run: npm run ci:frontend:test diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 99b8cf854..6546ecb35 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -9,6 +9,8 @@ After forking/cloning the source code repository from [https://github.com/citrus mvn clean install ``` +Add the `-DskipFrontend=false` parameter to include the `simulator-ui` into the build. + ## Lombok This will compile all classes and generate constructors as well as getters and setters using [Project Lombok](https://projectlombok.org/). diff --git a/pom.xml b/pom.xml index 5073eeeec..0dd55d2e1 100644 --- a/pom.xml +++ b/pom.xml @@ -20,9 +20,11 @@ none + 3.11.0 + 1.18.20 4.0.0-M2 - 3.1.2 + 3.1.2 6.0.9 3.1.3 7.5.1 @@ -164,7 +166,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import @@ -252,12 +254,6 @@ ${testng.version} test - - org.mockito - mockito-core - 2.15.0 - test - @@ -292,13 +288,6 @@ ${project.build.sourceEncoding} ${java.version} ${java.version} - - - org.projectlombok - lombok - ${lombok.version} - - @@ -377,21 +366,32 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.1.2 + + **/*IT.java + + false 1 ${reuseForks} - false org.apache.maven.plugins maven-failsafe-plugin - 2.22.2 + 3.1.2 false + + + + integration-test + verify + + + diff --git a/simulator-docs/src/main/asciidoc/images/dashboard.png b/simulator-docs/src/main/asciidoc/images/dashboard.png index d4a57fdba..145bff80a 100644 Binary files a/simulator-docs/src/main/asciidoc/images/dashboard.png and b/simulator-docs/src/main/asciidoc/images/dashboard.png differ diff --git a/simulator-samples/pom.xml b/simulator-samples/pom.xml index 2e2dad316..ac53a6c0c 100644 --- a/simulator-samples/pom.xml +++ b/simulator-samples/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 @@ -41,7 +41,7 @@ org.springframework.boot spring-boot-maven-plugin - ${spring.boot.version} + ${spring-boot.version} diff --git a/simulator-samples/sample-bank-service/pom.xml b/simulator-samples/sample-bank-service/pom.xml index 6bdfdf2da..2cabaf918 100644 --- a/simulator-samples/sample-bank-service/pom.xml +++ b/simulator-samples/sample-bank-service/pom.xml @@ -19,7 +19,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import diff --git a/simulator-samples/sample-combined/pom.xml b/simulator-samples/sample-combined/pom.xml index da53ad3b0..0efcb2556 100644 --- a/simulator-samples/sample-combined/pom.xml +++ b/simulator-samples/sample-combined/pom.xml @@ -23,7 +23,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import diff --git a/simulator-samples/sample-jms-fax/pom.xml b/simulator-samples/sample-jms-fax/pom.xml index ce9fb4b17..d4ce74947 100644 --- a/simulator-samples/sample-jms-fax/pom.xml +++ b/simulator-samples/sample-jms-fax/pom.xml @@ -27,7 +27,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import diff --git a/simulator-samples/sample-jms/pom.xml b/simulator-samples/sample-jms/pom.xml index 51e27bf78..b06350c44 100644 --- a/simulator-samples/sample-jms/pom.xml +++ b/simulator-samples/sample-jms/pom.xml @@ -23,7 +23,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import diff --git a/simulator-samples/sample-mail/pom.xml b/simulator-samples/sample-mail/pom.xml index d8eff0d73..ba094d6a0 100644 --- a/simulator-samples/sample-mail/pom.xml +++ b/simulator-samples/sample-mail/pom.xml @@ -19,7 +19,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import diff --git a/simulator-samples/sample-rest/pom.xml b/simulator-samples/sample-rest/pom.xml index 0ce4643ab..b0249f2fe 100644 --- a/simulator-samples/sample-rest/pom.xml +++ b/simulator-samples/sample-rest/pom.xml @@ -19,7 +19,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import @@ -49,12 +49,6 @@ testng test - - - - org.springframework.boot - spring-boot-devtools - diff --git a/simulator-samples/sample-swagger/pom.xml b/simulator-samples/sample-swagger/pom.xml index 5ed14d304..1751205c0 100644 --- a/simulator-samples/sample-swagger/pom.xml +++ b/simulator-samples/sample-swagger/pom.xml @@ -19,7 +19,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import diff --git a/simulator-samples/sample-swagger/src/test/java/org/citrusframework/simulator/SimulatorSwaggerIT.java b/simulator-samples/sample-swagger/src/test/java/org/citrusframework/simulator/SimulatorSwaggerIT.java index b5db72f08..fcbbeb190 100644 --- a/simulator-samples/sample-swagger/src/test/java/org/citrusframework/simulator/SimulatorSwaggerIT.java +++ b/simulator-samples/sample-swagger/src/test/java/org/citrusframework/simulator/SimulatorSwaggerIT.java @@ -74,7 +74,8 @@ public void testUiInfo() { "{" + "\"name\":\"REST Petstore Simulator\"," + "\"version\":\"@ignore@\"" + - "}" + + "}," + + "\"activeProfiles\": []" + "}")); } diff --git a/simulator-samples/sample-ws-client/pom.xml b/simulator-samples/sample-ws-client/pom.xml index 57af62783..96efab4d8 100644 --- a/simulator-samples/sample-ws-client/pom.xml +++ b/simulator-samples/sample-ws-client/pom.xml @@ -19,7 +19,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import diff --git a/simulator-samples/sample-ws/pom.xml b/simulator-samples/sample-ws/pom.xml index 999230bda..b752eac5a 100644 --- a/simulator-samples/sample-ws/pom.xml +++ b/simulator-samples/sample-ws/pom.xml @@ -19,7 +19,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import diff --git a/simulator-samples/sample-wsdl/pom.xml b/simulator-samples/sample-wsdl/pom.xml index 297e0cd1d..6e139633c 100644 --- a/simulator-samples/sample-wsdl/pom.xml +++ b/simulator-samples/sample-wsdl/pom.xml @@ -19,7 +19,7 @@ org.springframework.boot spring-boot-dependencies - ${spring.boot.version} + ${spring-boot.version} pom import diff --git a/simulator-samples/sample-wsdl/src/main/java/org/citrusframework/simulator/sample/Simulator.java b/simulator-samples/sample-wsdl/src/main/java/org/citrusframework/simulator/sample/Simulator.java index 38ab7992c..05fe874da 100644 --- a/simulator-samples/sample-wsdl/src/main/java/org/citrusframework/simulator/sample/Simulator.java +++ b/simulator-samples/sample-wsdl/src/main/java/org/citrusframework/simulator/sample/Simulator.java @@ -40,7 +40,7 @@ public static void main(String[] args) { @Override public String servletMapping(SimulatorWebServiceConfigurationProperties simulatorWebServiceConfiguration) { - return "/services/ws/HelloService/v1/*"; + return "/services/ws/HelloService/*"; } @Override diff --git a/simulator-samples/sample-wsdl/src/main/resources/application.properties b/simulator-samples/sample-wsdl/src/main/resources/application.properties index 83a11b72f..d1aaf978d 100644 --- a/simulator-samples/sample-wsdl/src/main/resources/application.properties +++ b/simulator-samples/sample-wsdl/src/main/resources/application.properties @@ -21,3 +21,7 @@ citrus.simulator.defaultScenario=Default # Should Citrus validate incoming messages on syntax and semantics citrus.simulator.templateValidation=true + + +logging.level.root=DEBUG +logging.level.web=TRACE diff --git a/simulator-samples/sample-wsdl/src/test/java/org/citrusframework/simulator/SimulatorWebServiceIT.java b/simulator-samples/sample-wsdl/src/test/java/org/citrusframework/simulator/SimulatorWebServiceWithWsdlIT.java similarity index 97% rename from simulator-samples/sample-wsdl/src/test/java/org/citrusframework/simulator/SimulatorWebServiceIT.java rename to simulator-samples/sample-wsdl/src/test/java/org/citrusframework/simulator/SimulatorWebServiceWithWsdlIT.java index 1240a4981..23eecf134 100644 --- a/simulator-samples/sample-wsdl/src/test/java/org/citrusframework/simulator/SimulatorWebServiceIT.java +++ b/simulator-samples/sample-wsdl/src/test/java/org/citrusframework/simulator/SimulatorWebServiceWithWsdlIT.java @@ -40,8 +40,8 @@ * @author Christoph Deppisch */ @Test -@ContextConfiguration(classes = SimulatorWebServiceIT.EndpointConfig.class) -public class SimulatorWebServiceIT extends TestNGCitrusSpringSupport { +@ContextConfiguration(classes = SimulatorWebServiceWithWsdlIT.EndpointConfig.class) +public class SimulatorWebServiceWithWsdlIT extends TestNGCitrusSpringSupport { @Autowired private WebServiceClient soapClient; diff --git a/simulator-starter/pom.xml b/simulator-starter/pom.xml index 4477337c9..a0e38c6d6 100644 --- a/simulator-starter/pom.xml +++ b/simulator-starter/pom.xml @@ -14,6 +14,11 @@ citrus-simulator-starter ${project.artifactId} + + + 6.2.7.Final + + @@ -36,10 +41,17 @@ org.springframework.boot spring-boot-starter-validation + + org.springframework.boot spring-boot-configuration-processor - true + provided + + + org.hibernate.orm + hibernate-jpamodelgen + provided @@ -48,6 +60,7 @@ h2 + jakarta.interceptor jakarta.interceptor-api @@ -57,6 +70,13 @@ jakarta.transaction-api + + + org.springdoc + springdoc-openapi-starter-common + 2.2.0 + + wsdl4j wsdl4j @@ -144,5 +164,61 @@ true + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + + + org.hibernate.orm + hibernate-jpamodelgen + ${hibernate.version} + + + org.projectlombok + lombok + ${lombok.version} + + + + + + + + + + IDE + + + org.hibernate.orm + hibernate-jpamodelgen + + + + diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/SimulatorAutoConfiguration.java b/simulator-starter/src/main/java/org/citrusframework/simulator/SimulatorAutoConfiguration.java index e17918533..668f01190 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/SimulatorAutoConfiguration.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/SimulatorAutoConfiguration.java @@ -55,7 +55,9 @@ */ @Configuration @ComponentScan(basePackages = { + // TODO: Remove when scenario controller has been migrated "org.citrusframework.simulator.controller", + "org.citrusframework.simulator.web.rest", "org.citrusframework.simulator.listener", "org.citrusframework.simulator.service", "org.citrusframework.simulator.endpoint", @@ -163,7 +165,7 @@ public JsonPathMappingDataDictionary outboundJsonDataDictionary() { return outboundJsonDataDictionary; } - + @Bean public QueryFilterAdapterFactory queryFilterAdapterFactory(SimulatorConfigurationProperties cfg) { return new QueryFilterAdapterFactory(cfg); diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/controller/ActivityController.java b/simulator-starter/src/main/java/org/citrusframework/simulator/controller/ActivityController.java deleted file mode 100644 index 681244d72..000000000 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/controller/ActivityController.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2006-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.citrusframework.simulator.controller; - -import java.time.Instant; -import java.util.Collection; -import org.citrusframework.simulator.model.ScenarioExecution; -import org.citrusframework.simulator.model.ScenarioExecution.Status; -import org.citrusframework.simulator.model.ScenarioExecutionFilter; -import org.citrusframework.simulator.service.ActivityService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("api/activity") -public class ActivityController { - - @Autowired - private ActivityService activityService; - - @RequestMapping(method = RequestMethod.GET) - public Collection getScenarioExecutions( - @RequestParam(value = "fromDate", required = false) Instant fromDate, - @RequestParam(value = "toDate", required = false) Instant toDate, - @RequestParam(value = "page", required = false) Integer page, - @RequestParam(value = "size", required = false) Integer size - ) { - return activityService.getScenarioExecutionsByStartDate(fromDate, toDate, page, size); - } - - @RequestMapping(method = RequestMethod.DELETE) - public void clearExecutions() { - activityService.clearScenarioExecutions(); - } - - @RequestMapping(method = RequestMethod.GET, value = "/scenario/{name}") - public Collection getScenarioExecutionsByName(@PathVariable("name") String name) { - return activityService.getScenarioExecutionsByName(name); - } - - @RequestMapping(method = RequestMethod.GET, value = "/status/{status}") - public Collection getScenarioExecutionsByStatus(@PathVariable("status") String status) { - return activityService.getScenarioExecutionsByStatus(Status.valueOf(status)); - } - - @RequestMapping(method = RequestMethod.GET, value = "/{id}") - public ScenarioExecution getScenarioExecution(@PathVariable("id") Long id) { - return activityService.getScenarioExecutionById(id); - } - - @RequestMapping(method = RequestMethod.POST) - public Collection getScenarioExecutions(@RequestBody ScenarioExecutionFilter filter) { - return activityService.getScenarioExecutions(filter); - } - -} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/controller/MessageController.java b/simulator-starter/src/main/java/org/citrusframework/simulator/controller/MessageController.java deleted file mode 100644 index 8ea1b03cb..000000000 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/controller/MessageController.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2006-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.citrusframework.simulator.controller; - -import org.citrusframework.simulator.model.Message; -import org.citrusframework.simulator.model.MessageFilter; -import org.citrusframework.simulator.service.MessageService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.Collection; - -@RestController -@RequestMapping("api/message") -public class MessageController { - - @Autowired - private MessageService messageService; - - @RequestMapping(method = RequestMethod.POST) - public Collection getMessages(@RequestBody MessageFilter filter) { - return messageService.getMessagesMatchingFilter(filter); - } - - @RequestMapping(method = RequestMethod.DELETE) - public void clearMessages() { - messageService.clearMessages(); - } - - @RequestMapping(method = RequestMethod.GET, value = "/{id}") - public Message getMessageById(@PathVariable("id") Long id) { - return messageService.getMessageById(id); - } - -} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/controller/SummaryController.java b/simulator-starter/src/main/java/org/citrusframework/simulator/controller/SummaryController.java deleted file mode 100644 index 930c7422b..000000000 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/controller/SummaryController.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2006-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.citrusframework.simulator.controller; - -import org.citrusframework.report.TestResults; -import org.citrusframework.simulator.listener.SimulatorStatusListener; -import org.citrusframework.simulator.model.TestResult; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@RestController -@RequestMapping("api/summary") -public class SummaryController { - - @Autowired - private SimulatorStatusListener statusListener; - - /** - * Get a summary of all tests results - * - * @return - */ - @RequestMapping(method = RequestMethod.GET, value = "/results") - public List getSummaryTestResults() { - return statusListener.getTestResultService(); - } - - /** - * Get a summary of all tests results - * - * @return - */ - @RequestMapping(method = RequestMethod.DELETE, value = "/results") - public TestResults clearSummaryTestResults() { - statusListener.clearResults(); - return new TestResults(); - } - - /** - * Get count of active scenarios - * - * @return - */ - @RequestMapping(method = RequestMethod.GET, value = "/active") - public Integer getSummaryActive() { - return statusListener.getCountActiveScenarios(); - } - -} - diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/http/SimulatorRestAutoConfiguration.java b/simulator-starter/src/main/java/org/citrusframework/simulator/http/SimulatorRestAutoConfiguration.java index b953e5018..e25c6d434 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/http/SimulatorRestAutoConfiguration.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/http/SimulatorRestAutoConfiguration.java @@ -96,6 +96,7 @@ public FilterRegistrationBean requestCachingFilter( urlMapping = urlMapping.substring(0, urlMapping.length() - 1); } filterRegistrationBean.setUrlPatterns(Collections.singleton(urlMapping)); + return filterRegistrationBean; } diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/listener/SimulatorStatusListener.java b/simulator-starter/src/main/java/org/citrusframework/simulator/listener/SimulatorStatusListener.java index cc4293805..1e55885f4 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/listener/SimulatorStatusListener.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/listener/SimulatorStatusListener.java @@ -16,7 +16,6 @@ package org.citrusframework.simulator.listener; -import org.apache.commons.lang.NotImplementedException; import org.citrusframework.DefaultTestCase; import org.citrusframework.TestAction; import org.citrusframework.TestCase; @@ -49,7 +48,9 @@ public class SimulatorStatusListener extends AbstractTestListener implements Tes private static final Logger logger = LoggerFactory.getLogger(SimulatorStatusListener.class); /** - * Currently running test + * Currently running test. + * + * TODO: Replace with metric. */ private Map runningTests = new ConcurrentHashMap<>(); @@ -85,7 +86,7 @@ public void onTestSuccess(TestCase test) { result = TestResult.success(test.getName(), test.getTestClass().getSimpleName()); } - testResultService.save(result); + testResultService.transformAndSave(result); executionService.completeScenarioExecutionSuccess(test); logger.info(result.toString()); @@ -100,7 +101,7 @@ public void onTestFailure(TestCase test, Throwable cause) { result = TestResult.failed(test.getName(), test.getTestClass().getSimpleName(), cause); } - testResultService.save(result); + testResultService.transformAndSave(result); executionService.completeScenarioExecutionFailure(test, cause); logger.info(result.toString()); @@ -128,17 +129,13 @@ public void onTestActionFinish(TestCase testCase, TestAction testAction) { } } - private boolean ignoreTestAction(TestAction testAction) { - return testAction.getClass().equals(SleepAction.class); - } - @Override public void onTestActionSkipped(TestCase testCase, TestAction testAction) { } private String[] getParameters(TestCase test) { - List parameterStrings = new ArrayList(); + List parameterStrings = new ArrayList<>(); if (test instanceof DefaultTestCase) { for (Map.Entry param : ((DefaultTestCase) test).getParameters().entrySet()) { @@ -146,41 +143,10 @@ private String[] getParameters(TestCase test) { } } - return parameterStrings.toArray(new String[parameterStrings.size()]); - } - - /** - * Gets the value of the testResults property. - * - * @return the testResults - */ - public List getTestResultService() { - return testResultService.findAll(); - } - - /** - * Gets the value of the runningTests property. - * - * @return the runningTests - */ - public Map getRunningTests() { - return runningTests; - } - - /** - * Deletes all test results. Shall be removed, but will be kept for now to ensure backward compatibility. - * - * @deprecated will be removed wihin the next breaking release! - */ - @Deprecated(forRemoval = true) - public void clearResults() { - throw new NotImplementedException("This method will be removed within the next breaking release!"); + return parameterStrings.toArray(new String[0]); } - /** - * Get the count of active scenarios - */ - public int getCountActiveScenarios() { - return runningTests.size(); + private boolean ignoreTestAction(TestAction testAction) { + return testAction.getClass().equals(SleepAction.class); } } diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/model/AbstractAuditingEntity.java b/simulator-starter/src/main/java/org/citrusframework/simulator/model/AbstractAuditingEntity.java index 96e4f8720..d92c300cc 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/model/AbstractAuditingEntity.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/model/AbstractAuditingEntity.java @@ -10,6 +10,7 @@ import java.io.Serializable; import java.time.Instant; +import java.time.ZonedDateTime; /** * Base abstract class for entities which will hold definitions for created and last modified by attributes. @@ -37,11 +38,6 @@ public void setCreatedDate(Instant createdDate) { this.createdDate = createdDate; } - public T createdDate(Instant createdDate) { - setCreatedDate(createdDate); - return (T) this; - } - public Instant getLastModifiedDate() { return lastModifiedDate; } @@ -50,8 +46,20 @@ public void setLastModifiedDate(Instant lastModifiedDate) { this.lastModifiedDate = lastModifiedDate; } - public T lastModifiedDate(Instant lastModifiedDate) { - setLastModifiedDate(lastModifiedDate); - return (T) this; + public static abstract class AuditingEntityBuilder, E extends AbstractAuditingEntity, A> { + + @SuppressWarnings("unchecked") + public B createdDate(ZonedDateTime createdDate) { + getEntity().setCreatedDate(createdDate.toInstant()); + return (B) this; + } + + @SuppressWarnings("unchecked") + public B lastModifiedDate(ZonedDateTime lastModifiedDate) { + getEntity().setLastModifiedDate(lastModifiedDate.toInstant()); + return (B) this; + } + + protected abstract E getEntity(); } } diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/model/MessageFilter.java b/simulator-starter/src/main/java/org/citrusframework/simulator/model/MessageFilter.java index cdeea8b97..6124c8127 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/model/MessageFilter.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/model/MessageFilter.java @@ -16,14 +16,11 @@ package org.citrusframework.simulator.model; -import lombok.Data; - import java.time.Instant; /** * Filter for filtering {@link Message}s */ -@Data public class MessageFilter { private Instant fromDate; private Instant toDate; @@ -43,4 +40,68 @@ public class MessageFilter { * filter. */ private String headerFilter; + + public Instant getFromDate() { + return fromDate; + } + + public void setFromDate(Instant fromDate) { + this.fromDate = fromDate; + } + + public Instant getToDate() { + return toDate; + } + + public void setToDate(Instant toDate) { + this.toDate = toDate; + } + + public Integer getPageNumber() { + return pageNumber; + } + + public void setPageNumber(Integer pageNumber) { + this.pageNumber = pageNumber; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public Boolean getDirectionInbound() { + return directionInbound; + } + + public void setDirectionInbound(Boolean directionInbound) { + this.directionInbound = directionInbound; + } + + public Boolean getDirectionOutbound() { + return directionOutbound; + } + + public void setDirectionOutbound(Boolean directionOutbound) { + this.directionOutbound = directionOutbound; + } + + public String getContainingText() { + return containingText; + } + + public void setContainingText(String containingText) { + this.containingText = containingText; + } + + public String getHeaderFilter() { + return headerFilter; + } + + public void setHeaderFilter(String headerFilter) { + this.headerFilter = headerFilter; + } } diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/model/TestParameter.java b/simulator-starter/src/main/java/org/citrusframework/simulator/model/TestParameter.java index aa99c5e07..b61d977d2 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/model/TestParameter.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/model/TestParameter.java @@ -29,6 +29,7 @@ import java.io.Serial; import java.io.Serializable; +import java.util.Objects; /** * Represents a parameter of a test result, holding a key-value pair of parameter details. It is linked to @@ -64,7 +65,7 @@ public class TestParameter extends AbstractAuditingEntity { + + private final TestParameter testParameter = new TestParameter(); + + public TestParameter build() { + return testParameter; + } + + public TestParameterBuilder key(String key) { + if (Objects.isNull(testParameter.testParameterId)) { + testParameter.testParameterId = new TestParameterId(); + } + + testParameter.testParameterId.key = key; + return this; + } + + public TestParameterBuilder value(String value) { + testParameter.value = value; + return this; + } + + @Override + protected TestParameter getEntity() { + return testParameter; + } + } } diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/model/TestResult.java b/simulator-starter/src/main/java/org/citrusframework/simulator/model/TestResult.java index 2f818eb37..ea23d5d7d 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/model/TestResult.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/model/TestResult.java @@ -24,6 +24,7 @@ import jakarta.persistence.Id; import jakarta.persistence.OneToMany; import jakarta.validation.constraints.NotEmpty; + import java.io.Serial; import java.io.Serializable; import java.util.Arrays; @@ -123,10 +124,19 @@ public TestResult(org.citrusframework.TestResult testResult) { failureType = testResult.getFailureType(); } + public static TestResultBuilder builder() { + return new TestResultBuilder(); + } + public Long getId() { return id; } + public TestResult id(Long id) { + this.id = id; + return this; + } + public Status getStatus() { return Status.fromId(status); } @@ -143,6 +153,10 @@ public Set getTestParameters() { return testParameters; } + public void addTestParameter(TestParameter testParameter) { + testParameters.add(testParameter); + } + public String getErrorMessage() { return errorMessage; } @@ -157,10 +171,10 @@ public String getFailureType() { private int convertToStatus(String resultName) { return Arrays.stream(Status.values()) - .filter(result -> result.name().equals(resultName)) - .findFirst() - .orElse(Status.UNKNOWN) - .id; + .filter(result -> result.name().equals(resultName)) + .findFirst() + .orElse(Status.UNKNOWN) + .id; } @Override @@ -171,9 +185,9 @@ public String toString() { ", status='" + getStatus() + "'" + ", testName='" + getTestName() + "'" + ", className='" + getClassName() + "'" + - ", errorMessage='" +getErrorMessage() + "'" + - ", failureStack='" +getFailureStack() + "'" + - ", failureType='" +getFailureType() + "'" + + ", errorMessage='" + getErrorMessage() + "'" + + ", failureStack='" + getFailureStack() + "'" + + ", failureType='" + getFailureType() + "'" + "}"; } @@ -198,4 +212,48 @@ public static Status fromId(int id) { .orElse(Status.UNKNOWN); } } + + public static class TestResultBuilder extends AuditingEntityBuilder { + + private final TestResult testResult = new TestResult(); + + public TestResult build() { + return testResult; + } + + public TestResultBuilder status(Integer status) { + testResult.status = status; + return this; + } + + public TestResultBuilder testName(String testName) { + testResult.testName = testName; + return this; + } + + public TestResultBuilder className(String className) { + testResult.className = className; + return this; + } + + public TestResultBuilder errorMessage(String errorMessage) { + testResult.errorMessage = errorMessage; + return this; + } + + public TestResultBuilder failureStack(String failureStack) { + testResult.failureStack = failureStack; + return this; + } + + public TestResultBuilder failureType(String failureType) { + testResult.failureType = failureType; + return this; + } + + @Override + protected TestResult getEntity() { + return testResult; + } + } } diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/repository/MessageRepositoryImpl.java b/simulator-starter/src/main/java/org/citrusframework/simulator/repository/MessageRepositoryImpl.java index bb880b8f0..e8feeb204 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/repository/MessageRepositoryImpl.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/repository/MessageRepositoryImpl.java @@ -68,12 +68,12 @@ private void addPayloadPredicate(MessageFilter filter, CriteriaBuilder criteriaB private void addDatePredicates(MessageFilter filter, CriteriaBuilder criteriaBuilder, Root message, List predicates) { if (filter.getFromDate() != null) { - predicates.add(criteriaBuilder.greaterThanOrEqualTo(message.get("date"), + predicates.add(criteriaBuilder.greaterThanOrEqualTo(message.get("createdDate"), filter.getFromDate())); } if (filter.getToDate() != null) { - predicates.add(criteriaBuilder.lessThanOrEqualTo(message.get("date"), + predicates.add(criteriaBuilder.lessThanOrEqualTo(message.get("createdDate"), filter.getToDate())); } } diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/repository/TestParameterRepository.java b/simulator-starter/src/main/java/org/citrusframework/simulator/repository/TestParameterRepository.java new file mode 100644 index 000000000..282b5a3a4 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/repository/TestParameterRepository.java @@ -0,0 +1,13 @@ +package org.citrusframework.simulator.repository; + +import org.citrusframework.simulator.model.TestParameter; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +/** + * Spring Data JPA repository for the TestParameter entity. + */ +@SuppressWarnings("unused") +@Repository +public interface TestParameterRepository extends JpaRepository, JpaSpecificationExecutor {} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/repository/TestResultRepository.java b/simulator-starter/src/main/java/org/citrusframework/simulator/repository/TestResultRepository.java index 79c27270e..c544657ce 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/repository/TestResultRepository.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/repository/TestResultRepository.java @@ -1,26 +1,22 @@ -/* - * Copyright 2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.citrusframework.simulator.repository; import org.citrusframework.simulator.model.TestResult; +import org.citrusframework.simulator.service.dto.TestResultByStatus; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +/** + * Spring Data JPA repository for the TestResult entity. + */ +@SuppressWarnings("unused") @Repository -public interface TestResultRepository extends JpaRepository { +public interface TestResultRepository extends JpaRepository, JpaSpecificationExecutor { + @Query("select new org.citrusframework.simulator.service.dto.TestResultByStatus(" + + "sum(case when t.status = 1 then 1 else 0 end), " + + "sum(case when t.status = 2 then 1 else 0 end)) " + + "from TestResult t") + TestResultByStatus countByStatus(); } diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/ActivityService.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/ActivityService.java index 293f5e74d..eabd240e6 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/service/ActivityService.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/ActivityService.java @@ -239,6 +239,6 @@ private long lookupScenarioExecutionId(TestCase testCase) { } private Instant getTimeNow() { - return LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant(); + return LocalDateTime.now().toInstant(ZoneOffset.UTC); } } diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/QueryService.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/QueryService.java new file mode 100644 index 000000000..d48c2ad93 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/QueryService.java @@ -0,0 +1,526 @@ +package org.citrusframework.simulator.service; + +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.SetJoin; +import jakarta.persistence.metamodel.SetAttribute; +import jakarta.persistence.metamodel.SingularAttribute; +import org.citrusframework.simulator.service.filter.Filter; +import org.citrusframework.simulator.service.filter.RangeFilter; +import org.citrusframework.simulator.service.filter.StringFilter; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.Set; +import java.util.function.Function; + +/** + * Base service for constructing and executing complex queries. + * + * @param the type of the entity which is queried. + */ +@Transactional(readOnly = true) +public abstract class QueryService { + + /** + * Helper function to return a specification for filtering on a single field, where equality, and null/non-null + * conditions are supported. + * + * @param filter the individual attribute filter coming from the frontend. + * @param field the JPA static metamodel representing the field. + * @param The type of the attribute which is filtered. + * @return a Specification + */ + protected Specification buildSpecification(Filter filter, SingularAttribute + field) { + return buildSpecification(filter, root -> root.get(field)); + } + + /** + * Helper function to return a specification for filtering on a single field, where equality, and null/non-null + * conditions are supported. + * + * @param filter the individual attribute filter coming from the frontend. + * @param metaclassFunction the function, which navigates from the current entity to a column, for which the filter applies. + * @param The type of the attribute which is filtered. + * @return a Specification + */ + protected Specification buildSpecification(Filter filter, Function, Expression> metaclassFunction) { + if (filter.getEquals() != null) { + return equalsSpecification(metaclassFunction, filter.getEquals()); + } else if (filter.getIn() != null) { + return valueIn(metaclassFunction, filter.getIn()); + } else if (filter.getNotIn() != null) { + return valueNotIn(metaclassFunction, filter.getNotIn()); + } else if (filter.getNotEquals() != null) { + return notEqualsSpecification(metaclassFunction, filter.getNotEquals()); + } else if (filter.getSpecified() != null) { + return byFieldSpecified(metaclassFunction, filter.getSpecified()); + } + return null; + } + + /** + * Helper function to return a specification for filtering on a {@link java.lang.String} field, where equality, containment, + * and null/non-null conditions are supported. + * + * @param filter the individual attribute filter coming from the frontend. + * @param field the JPA static metamodel representing the field. + * @return a Specification + */ + protected Specification buildStringSpecification(StringFilter filter, SingularAttribute field) { + return buildSpecification(filter, root -> root.get(field)); + } + + /** + * Helper function to return a specification for filtering on a {@link java.lang.String} field, where equality, containment, + * and null/non-null conditions are supported. + * + * @param filter the individual attribute filter coming from the frontend. + * @param metaclassFunction lambda, which based on a Root<ENTITY> returns Expression - basicaly picks a column + * @return a Specification + */ + protected Specification buildSpecification(StringFilter filter, Function, Expression> metaclassFunction) { + if (filter.getEquals() != null) { + return equalsSpecification(metaclassFunction, filter.getEquals()); + } else if (filter.getIn() != null) { + return valueIn(metaclassFunction, filter.getIn()); + } else if (filter.getNotIn() != null) { + return valueNotIn(metaclassFunction, filter.getNotIn()); + } else if (filter.getContains() != null) { + return likeUpperSpecification(metaclassFunction, filter.getContains()); + } else if (filter.getDoesNotContain() != null) { + return doesNotContainSpecification(metaclassFunction, filter.getDoesNotContain()); + } else if (filter.getNotEquals() != null) { + return notEqualsSpecification(metaclassFunction, filter.getNotEquals()); + } else if (filter.getSpecified() != null) { + return byFieldSpecified(metaclassFunction, filter.getSpecified()); + } + return null; + } + + /** + * Helper function to return a specification for filtering on a single {@link java.lang.Comparable}, where equality, less + * than, greater than and less-than-or-equal-to and greater-than-or-equal-to and null/non-null conditions are + * supported. + * + * @param The type of the attribute which is filtered. + * @param filter the individual attribute filter coming from the frontend. + * @param field the JPA static metamodel representing the field. + * @return a Specification + */ + protected > Specification buildRangeSpecification(RangeFilter filter, + SingularAttribute field) { + return buildSpecification(filter, root -> root.get(field)); + } + + /** + * Helper function to return a specification for filtering on a single {@link java.lang.Comparable}, where equality, less + * than, greater than and less-than-or-equal-to and greater-than-or-equal-to and null/non-null conditions are + * supported. + * + * @param The type of the attribute which is filtered. + * @param filter the individual attribute filter coming from the frontend. + * @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) { + 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) { + result = result.and(byFieldSpecified(metaclassFunction, filter.getSpecified())); + } + if (filter.getNotEquals() != null) { + result = result.and(notEqualsSpecification(metaclassFunction, filter.getNotEquals())); + } + if (filter.getNotIn() != null) { + result = result.and(valueNotIn(metaclassFunction, filter.getNotIn())); + } + if (filter.getGreaterThan() != null) { + result = result.and(greaterThan(metaclassFunction, filter.getGreaterThan())); + } + if (filter.getGreaterThanOrEqual() != null) { + result = result.and(greaterThanOrEqualTo(metaclassFunction, filter.getGreaterThanOrEqual())); + } + if (filter.getLessThan() != null) { + result = result.and(lessThan(metaclassFunction, filter.getLessThan())); + } + if (filter.getLessThanOrEqual() != null) { + result = result.and(lessThanOrEqualTo(metaclassFunction, filter.getLessThanOrEqual())); + } + return result; + } + + /** + * Helper function to return a specification for filtering on one-to-one or many-to-one reference. Usage: + *
+     *   Specification<Employee> specByProjectId = buildReferringEntitySpecification(criteria.getProjectId(),
+     * Employee_.project, Project_.id);
+     *   Specification<Employee> specByProjectName = buildReferringEntitySpecification(criteria.getProjectName(),
+     * Employee_.project, Project_.name);
+     * 
+ * + * @param filter the filter object which contains a value, which needs to match or a flag if nullness is + * checked. + * @param reference the attribute of the static metamodel for the referring entity. + * @param valueField the attribute of the static metamodel of the referred entity, where the equality should be + * checked. + * @param The type of the referenced entity. + * @param The type of the attribute which is filtered. + * @return a Specification + */ + protected Specification buildReferringEntitySpecification(Filter filter, + SingularAttribute reference, + SingularAttribute valueField) { + return buildSpecification(filter, root -> root.get(reference).get(valueField)); + } + + /** + * Helper function to return a specification for filtering on one-to-many or many-to-many reference. Usage: + *
+     *   Specification<Employee> specByEmployeeId = buildReferringEntitySpecification(criteria.getEmployeId(),
+     * Project_.employees, Employee_.id);
+     *   Specification<Employee> specByEmployeeName = buildReferringEntitySpecification(criteria.getEmployeName(),
+     * Project_.project, Project_.name);
+     * 
+ * + * @param filter the filter object which contains a value, which needs to match or a flag if emptiness is + * checked. + * @param reference the attribute of the static metamodel for the referring entity. + * @param valueField the attribute of the static metamodel of the referred entity, where the equality should be + * checked. + * @param The type of the referenced entity. + * @param The type of the attribute which is filtered. + * @return a Specification + */ + protected Specification buildReferringEntitySpecification(Filter filter, + SetAttribute reference, + SingularAttribute valueField) { + return buildReferringEntitySpecification(filter, root -> root.join(reference), entity -> entity.get(valueField)); + } + + /** + * Helper function to return a specification for filtering on one-to-many or many-to-many reference.Usage:
+     *   Specification<Employee> specByEmployeeId = buildReferringEntitySpecification(
+     *          criteria.getEmployeId(),
+     *          root -> root.get(Project_.company).join(Company_.employees),
+     *          entity -> entity.get(Employee_.id));
+     *   Specification<Employee> specByProjectName = buildReferringEntitySpecification(
+     *          criteria.getProjectName(),
+     *          root -> root.get(Project_.project)
+     *          entity -> entity.get(Project_.name));
+     * 
+ * + * @param filter the filter object which contains a value, which needs to match or a flag if emptiness is + * checked. + * @param functionToEntity the function, which joins he current entity to the entity set, on which the filtering is applied. + * @param entityToColumn the function, which of the static metamodel of the referred entity, where the equality should be + * checked. + * @param The type of the referenced entity. + * @param The type of the entity which is the last before the OTHER in the chain. + * @param The type of the attribute which is filtered. + * @return a Specification + */ + protected Specification buildReferringEntitySpecification(Filter filter, + Function, SetJoin> functionToEntity, + Function, Expression> entityToColumn) { + if (filter.getEquals() != null) { + return equalsSpecification(functionToEntity.andThen(entityToColumn), filter.getEquals()); + } else if (filter.getSpecified() != null) { + // Interestingly, 'functionToEntity' doesn't work, we need the longer lambda formula + return byFieldSpecified(functionToEntity::apply, filter.getSpecified()); + } + return null; + } + + /** + * Helper function to return a specification for filtering on one-to-many or many-to-many reference.Where equality, less + * than, greater than and less-than-or-equal-to and greater-than-or-equal-to and null/non-null conditions are + * supported. Usage: + *
+     *   Specification<Employee> specByEmployeeId = buildReferringEntitySpecification(criteria.getEmployeId(),
+     * Project_.employees, Employee_.id);
+     *   Specification<Employee> specByEmployeeName = buildReferringEntitySpecification(criteria.getEmployeName(),
+     * Project_.project, Project_.name);
+     * 
+ * + * @param The type of the attribute which is filtered. + * @param filter the filter object which contains a value, which needs to match or a flag if emptiness is + * checked. + * @param reference the attribute of the static metamodel for the referring entity. + * @param valueField the attribute of the static metamodel of the referred entity, where the equality should be + * checked. + * @param The type of the referenced entity. + * @return a Specification + */ + protected > Specification buildReferringEntitySpecification( + RangeFilter filter, + SetAttribute reference, + SingularAttribute valueField) { + return buildReferringEntitySpecification(filter, root -> root.join(reference), entity -> entity.get(valueField)); + } + + /** + * Helper function to return a specification for filtering on one-to-many or many-to-many reference.Where equality, less + * than, greater than and less-than-or-equal-to and greater-than-or-equal-to and null/non-null conditions are + * supported. Usage: + *

+     *   Specification<Employee> specByEmployeeId = buildReferringEntitySpecification(
+     *          criteria.getEmployeId(),
+     *          root -> root.get(Project_.company).join(Company_.employees),
+     *          entity -> entity.get(Employee_.id));
+     *   Specification<Employee> specByProjectName = buildReferringEntitySpecification(
+     *          criteria.getProjectName(),
+     *          root -> root.get(Project_.project)
+     *          entity -> entity.get(Project_.name));
+     * 
+     * 
+ * + * @param The type of the attribute which is filtered. + * @param filter the filter object which contains a value, which needs to match or a flag if emptiness is + * checked. + * @param functionToEntity the function, which joins he current entity to the entity set, on which the filtering is applied. + * @param entityToColumn the function, which of the static metamodel of the referred entity, where the equality should be + * checked. + * @param The type of the referenced entity. + * @param The type of the entity which is the last before the OTHER in the chain. + * @return a Specification + */ + protected > Specification buildReferringEntitySpecification( + RangeFilter filter, + Function, SetJoin> functionToEntity, + Function, Expression> entityToColumn) { + + Function, Expression> fused = functionToEntity.andThen(entityToColumn); + if (filter.getEquals() != null) { + return equalsSpecification(fused, filter.getEquals()); + } else if (filter.getIn() != null) { + return valueIn(fused, filter.getIn()); + } + Specification result = Specification.where(null); + if (filter.getSpecified() != null) { + // Interestingly, 'functionToEntity' doesn't work, we need the longer lambda formula + result = result.and(byFieldSpecified(functionToEntity::apply, filter.getSpecified())); + } + if (filter.getNotEquals() != null) { + result = result.and(notEqualsSpecification(fused, filter.getNotEquals())); + } + if (filter.getNotIn() != null) { + result = result.and(valueNotIn(fused, filter.getNotIn())); + } + if (filter.getGreaterThan() != null) { + result = result.and(greaterThan(fused, filter.getGreaterThan())); + } + if (filter.getGreaterThanOrEqual() != null) { + result = result.and(greaterThanOrEqualTo(fused, filter.getGreaterThanOrEqual())); + } + if (filter.getLessThan() != null) { + result = result.and(lessThan(fused, filter.getLessThan())); + } + if (filter.getLessThanOrEqual() != null) { + result = result.and(lessThanOrEqualTo(fused, filter.getLessThanOrEqual())); + } + return result; + } + + /** + * Generic method, which based on a Root<ENTITY> returns an Expression which type is the same as the given 'value' type. + * + * @param metaclassFunction function which returns the column which is used for filtering. + * @param value the actual value to filter for. + * @param The type of the attribute which is filtered. + * @return a Specification. + */ + protected Specification equalsSpecification(Function, Expression> metaclassFunction, X value) { + return (root, query, builder) -> builder.equal(metaclassFunction.apply(root), value); + } + + /** + * Generic method, which based on a Root<ENTITY> returns an Expression which type is the same as the given 'value' type. + * + * @param metaclassFunction function which returns the column which is used for filtering. + * @param value the actual value to exclude for. + * @param The type of the attribute which is filtered. + * @return a Specification. + */ + protected Specification notEqualsSpecification(Function, Expression> metaclassFunction, X value) { + return (root, query, builder) -> builder.not(builder.equal(metaclassFunction.apply(root), value)); + } + + /** + *

likeUpperSpecification.

+ * + * @param metaclassFunction a {@link java.util.function.Function} object. + * @param value a {@link java.lang.String} object. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected Specification likeUpperSpecification(Function, Expression> metaclassFunction, + String value) { + return (root, query, builder) -> builder.like(builder.upper(metaclassFunction.apply(root)), wrapLikeQuery(value)); + } + + /** + *

doesNotContainSpecification.

+ * + * @param metaclassFunction a {@link java.util.function.Function} object. + * @param value a {@link java.lang.String} object. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected Specification doesNotContainSpecification(Function, Expression> metaclassFunction, + String value) { + return (root, query, builder) -> builder.not(builder.like(builder.upper(metaclassFunction.apply(root)), wrapLikeQuery(value))); + } + + /** + *

byFieldSpecified.

+ * + * @param metaclassFunction a {@link java.util.function.Function} object. + * @param specified a boolean. + * @param a X object. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected Specification byFieldSpecified(Function, Expression> metaclassFunction, + boolean specified) { + return specified ? + (root, query, builder) -> builder.isNotNull(metaclassFunction.apply(root)) : + (root, query, builder) -> builder.isNull(metaclassFunction.apply(root)); + } + + /** + *

byFieldEmptiness.

+ * + * @param metaclassFunction a {@link java.util.function.Function} object. + * @param specified a boolean. + * @param a X object. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected Specification byFieldEmptiness(Function, Expression>> metaclassFunction, + boolean specified) { + return specified ? + (root, query, builder) -> builder.isNotEmpty(metaclassFunction.apply(root)) : + (root, query, builder) -> builder.isEmpty(metaclassFunction.apply(root)); + } + + /** + *

valueIn.

+ * + * @param metaclassFunction a {@link java.util.function.Function} object. + * @param values a {@link java.util.Collection} object. + * @param a X object. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected Specification valueIn(Function, Expression> metaclassFunction, + Collection values) { + return (root, query, builder) -> { + CriteriaBuilder.In in = builder.in(metaclassFunction.apply(root)); + for (X value : values) { + in = in.value(value); + } + return in; + }; + } + + /** + *

valueNotIn.

+ * + * @param metaclassFunction a {@link java.util.function.Function} object. + * @param values a {@link java.util.Collection} object. + * @param a X object. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected Specification valueNotIn(Function, Expression> metaclassFunction, + Collection values) { + return (root, query, builder) -> { + CriteriaBuilder.In in = builder.in(metaclassFunction.apply(root)); + for (X value : values) { + in = in.value(value); + } + return builder.not(in); + }; + } + + /** + *

greaterThanOrEqualTo.

+ * + * @param metaclassFunction a {@link java.util.function.Function} object. + * @param value a X object. + * @param a X object. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected > Specification greaterThanOrEqualTo(Function, Expression> metaclassFunction, + X value) { + return (root, query, builder) -> builder.greaterThanOrEqualTo(metaclassFunction.apply(root), value); + } + + /** + *

greaterThan.

+ * + * @param metaclassFunction a {@link java.util.function.Function} object. + * @param value a X object. + * @param a X object. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected > Specification greaterThan(Function, Expression> metaclassFunction, + X value) { + return (root, query, builder) -> builder.greaterThan(metaclassFunction.apply(root), value); + } + + /** + *

lessThanOrEqualTo.

+ * + * @param metaclassFunction a {@link java.util.function.Function} object. + * @param value a X object. + * @param a X object. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected > Specification lessThanOrEqualTo(Function, Expression> metaclassFunction, + X value) { + return (root, query, builder) -> builder.lessThanOrEqualTo(metaclassFunction.apply(root), value); + } + + /** + *

lessThan.

+ * + * @param metaclassFunction a {@link java.util.function.Function} object. + * @param value a X object. + * @param a X object. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected > Specification lessThan(Function, Expression> metaclassFunction, + X value) { + return (root, query, builder) -> builder.lessThan(metaclassFunction.apply(root), value); + } + + /** + *

wrapLikeQuery.

+ * + * @param txt a {@link java.lang.String} object. + * @return a {@link java.lang.String} object. + */ + protected String wrapLikeQuery(String txt) { + return "%" + txt.toUpperCase() + '%'; + } + + /** + *

distinct.

+ * + * @param distinct a boolean. + * @return a {@link org.springframework.data.jpa.domain.Specification} object. + */ + protected Specification distinct(boolean distinct) { + return (root, query, cb) -> { + query.distinct(distinct); + return null; + }; + } + +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestParameterQueryService.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestParameterQueryService.java new file mode 100644 index 000000000..d2b8e9374 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestParameterQueryService.java @@ -0,0 +1,110 @@ +package org.citrusframework.simulator.service; + +import jakarta.persistence.criteria.JoinType; +import org.citrusframework.simulator.model.TestParameter; +import org.citrusframework.simulator.model.TestParameter_; +import org.citrusframework.simulator.model.TestResult_; +import org.citrusframework.simulator.repository.TestParameterRepository; +import org.citrusframework.simulator.service.criteria.TestParameterCriteria; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * Service for executing complex queries for {@link TestParameter} entities in the database. + * The main input is a {@link TestParameterCriteria} which gets converted to {@link Specification}, + * in a way that all the filters must apply. + * It returns a {@link List} of {@link TestParameter} or a {@link Page} of {@link TestParameter} which fulfills the criteria. + */ +@Service +@Transactional(readOnly = true) +public class TestParameterQueryService extends QueryService { + + private final Logger log = LoggerFactory.getLogger(TestParameterQueryService.class); + + private final TestParameterRepository testParameterRepository; + + public TestParameterQueryService(TestParameterRepository testParameterRepository) { + this.testParameterRepository = testParameterRepository; + } + + /** + * Return a {@link List} of {@link TestParameter} which matches the criteria from the database. + * @param criteria The object which holds all the filters, which the entities should match. + * @return the matching entities. + */ + @Transactional(readOnly = true) + public List findByCriteria(TestParameterCriteria criteria) { + log.debug("find by criteria : {}", criteria); + final Specification specification = createSpecification(criteria); + return testParameterRepository.findAll(specification); + } + + /** + * Return a {@link Page} of {@link TestParameter} which matches the criteria from the database. + * @param criteria The object which holds all the filters, which the entities should match. + * @param page The page, which should be returned. + * @return the matching entities. + */ + @Transactional(readOnly = true) + public Page findByCriteria(TestParameterCriteria criteria, Pageable page) { + log.debug("find by criteria : {}, page: {}", criteria, page); + final Specification specification = createSpecification(criteria); + return testParameterRepository.findAll(specification, page); + } + + /** + * Return the number of matching entities in the database. + * @param criteria The object which holds all the filters, which the entities should match. + * @return the number of matching entities. + */ + @Transactional(readOnly = true) + public long countByCriteria(TestParameterCriteria criteria) { + log.debug("count by criteria : {}", criteria); + final Specification specification = createSpecification(criteria); + return testParameterRepository.count(specification); + } + + /** + * Function to convert {@link TestParameterCriteria} to a {@link Specification} + * @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(TestParameterCriteria criteria) { + Specification specification = Specification.where(null); + if (criteria != null) { + // This has to be called first, because the distinct method returns null + if (criteria.getDistinct() != null) { + specification = specification.and(distinct(criteria.getDistinct())); + } + if (criteria.getKey() != null) { + specification = specification.and(buildSpecification(criteria.getKey(), root -> root.get(TestParameter_.testParameterId).get("key"))); + } + if (criteria.getValue() != null) { + specification = specification.and(buildStringSpecification(criteria.getValue(), TestParameter_.value)); + } + if (criteria.getCreatedDate() != null) { + specification = specification.and(buildRangeSpecification(criteria.getCreatedDate(), TestParameter_.createdDate)); + } + if (criteria.getLastModifiedDate() != null) { + specification = specification.and(buildRangeSpecification(criteria.getLastModifiedDate(), TestParameter_.lastModifiedDate)); + } + if (criteria.getTestResultId() != null) { + specification = + specification.and( + buildSpecification( + criteria.getTestResultId(), + root -> root.join(TestParameter_.testResult, JoinType.LEFT).get(TestResult_.id) + ) + ); + } + } + return specification; + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestParameterService.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestParameterService.java new file mode 100644 index 000000000..71ed7efe1 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestParameterService.java @@ -0,0 +1,36 @@ +package org.citrusframework.simulator.service; + +import org.citrusframework.simulator.model.TestParameter; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +/** + * Service Interface for managing {@link org.citrusframework.simulator.model.TestParameter}. + */ +public interface TestParameterService { + /** + * Save a testParameter. + * + * @param testParameter the entity to save. + * @return the persisted entity. + */ + TestParameter save(TestParameter testParameter); + + /** + * Get all the testParameters. + * + * @param pageable the pagination information. + * @return the list of entities. + */ + Page findAll(Pageable pageable); + + /** + * Get the "id" testParameter. + * + * @param id the id of the entity. + * @return the entity. + */ + Optional findOne(Long testResultId, String key); +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestResultQueryService.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestResultQueryService.java new file mode 100644 index 000000000..edbb0aaa4 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestResultQueryService.java @@ -0,0 +1,125 @@ +package org.citrusframework.simulator.service; + +import jakarta.persistence.criteria.JoinType; +import org.citrusframework.simulator.model.TestParameter_; +import org.citrusframework.simulator.model.TestResult; +import org.citrusframework.simulator.model.TestResult_; +import org.citrusframework.simulator.repository.TestResultRepository; +import org.citrusframework.simulator.service.criteria.TestResultCriteria; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * Service for executing complex queries for {@link TestResult} entities in the database. + * The main input is a {@link TestResultCriteria} which gets converted to {@link Specification}, + * in a way that all the filters must apply. + * It returns a {@link List} of {@link TestResult} or a {@link Page} of {@link TestResult} which fulfills the criteria. + */ +@Service +@Transactional(readOnly = true) +public class TestResultQueryService extends QueryService { + + private final Logger logger = LoggerFactory.getLogger(TestResultQueryService.class); + + private final TestResultRepository testResultRepository; + + public TestResultQueryService(TestResultRepository testResultRepository) { + this.testResultRepository = testResultRepository; + } + + /** + * Return a {@link List} of {@link TestResult} which matches the criteria from the database. + * @param criteria The object which holds all the filters, which the entities should match. + * @return the matching entities. + */ + @Transactional(readOnly = true) + public List findByCriteria(TestResultCriteria criteria) { + logger.debug("find by criteria : {}", criteria); + final Specification specification = createSpecification(criteria); + return testResultRepository.findAll(specification); + } + + /** + * Return a {@link Page} of {@link TestResult} which matches the criteria from the database. + * @param criteria The object which holds all the filters, which the entities should match. + * @param page The page, which should be returned. + * @return the matching entities. + */ + @Transactional(readOnly = true) + public Page findByCriteria(TestResultCriteria criteria, Pageable page) { + logger.debug("find by criteria : {}, page: {}", criteria, page); + final Specification specification = createSpecification(criteria); + return testResultRepository.findAll(specification, page); + } + + /** + * Return the number of matching entities in the database. + * @param criteria The object which holds all the filters, which the entities should match. + * @return the number of matching entities. + */ + @Transactional(readOnly = true) + public long countByCriteria(TestResultCriteria criteria) { + logger.debug("count by criteria : {}", criteria); + final Specification specification = createSpecification(criteria); + return testResultRepository.count(specification); + } + + /** + * Function to convert {@link TestResultCriteria} to a {@link Specification} + * @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(TestResultCriteria criteria) { + Specification specification = Specification.where(null); + if (criteria != null) { + // This has to be called first, because the distinct method returns null + if (criteria.getDistinct() != null) { + specification = specification.and(distinct(criteria.getDistinct())); + } + if (criteria.getId() != null) { + specification = specification.and(buildRangeSpecification(criteria.getId(), TestResult_.id)); + } + if (criteria.getStatus() != null) { + specification = specification.and(buildRangeSpecification(criteria.getStatus(), TestResult_.status)); + } + if (criteria.getTestName() != null) { + specification = specification.and(buildStringSpecification(criteria.getTestName(), TestResult_.testName)); + } + if (criteria.getClassName() != null) { + specification = specification.and(buildStringSpecification(criteria.getClassName(), TestResult_.className)); + } + if (criteria.getErrorMessage() != null) { + specification = specification.and(buildStringSpecification(criteria.getErrorMessage(), TestResult_.errorMessage)); + } + if (criteria.getFailureStack() != null) { + specification = specification.and(buildStringSpecification(criteria.getFailureStack(), TestResult_.failureStack)); + } + if (criteria.getFailureType() != null) { + specification = specification.and(buildStringSpecification(criteria.getFailureType(), TestResult_.failureType)); + } + if (criteria.getCreatedDate() != null) { + specification = specification.and(buildRangeSpecification(criteria.getCreatedDate(), TestResult_.createdDate)); + } + if (criteria.getLastModifiedDate() != null) { + specification = specification.and(buildRangeSpecification(criteria.getLastModifiedDate(), TestResult_.lastModifiedDate)); + } + if (criteria.getTestParameterKey() != null) { + specification = + specification.and( + buildSpecification( + criteria.getTestParameterKey(), + root -> root.join(TestResult_.testParameters, JoinType.LEFT).get(TestParameter_.testParameterId).get("key") + ) + ); + } + } + return specification; + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestResultService.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestResultService.java index bdd6853f2..1566895c8 100644 --- a/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestResultService.java +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/TestResultService.java @@ -1,41 +1,54 @@ -/* - * Copyright 2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.citrusframework.simulator.service; -import org.citrusframework.TestResult; -import org.citrusframework.simulator.repository.TestResultRepository; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Service -public class TestResultService { +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; - private final TestResultRepository testResultRepository; +import java.util.Optional; - public TestResultService(TestResultRepository testResultRepository) { - this.testResultRepository = testResultRepository; - } - - public org.citrusframework.simulator.model.TestResult save(TestResult result) { - return testResultRepository.save(new org.citrusframework.simulator.model.TestResult(result)); - } - - public List findAll() { - return testResultRepository.findAll(); - } +/** + * Service Interface for managing {@link org.citrusframework.simulator.model.TestResult}. + */ +public interface TestResultService { + + /** + * Save a citrus testResult. + * + * @param testResult the entity to save. + * @return the persisted entity. + * @see org.citrusframework.TestResult + */ + TestResult transformAndSave(org.citrusframework.TestResult testResult); + + /** + * Save a testResult. + * + * @param testResult the entity to save. + * @return the persisted entity. + */ + TestResult save(TestResult testResult); + + /** + * Get all the testResults. + * + * @param pageable the pagination information. + * @return the list of entities. + */ + Page findAll(Pageable pageable); + + /** + * Get the "id" testResult. + * + * @param id the id of the entity. + * @return the entity. + */ + Optional findOne(Long id); + + /** + * Count the total {@link TestResult} by their status. + * + * @return the TestResult count. + */ + TestResultByStatus countByStatus(); } diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/criteria/Criteria.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/criteria/Criteria.java new file mode 100644 index 000000000..7a2e6f25b --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/criteria/Criteria.java @@ -0,0 +1,5 @@ +package org.citrusframework.simulator.service.criteria; + +public interface Criteria { + Criteria copy(); +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/criteria/TestParameterCriteria.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/criteria/TestParameterCriteria.java new file mode 100644 index 000000000..54dd4639a --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/criteria/TestParameterCriteria.java @@ -0,0 +1,176 @@ +package org.citrusframework.simulator.service.criteria; + +import org.citrusframework.simulator.service.filter.InstantFilter; +import org.citrusframework.simulator.service.filter.LongFilter; +import org.citrusframework.simulator.service.filter.StringFilter; +import org.springdoc.core.annotations.ParameterObject; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Criteria class for the {@link org.citrusframework.simulator.model.TestParameter} entity. This class is used + * in {@link org.citrusframework.simulator.web.rest.TestParameterResource} to receive all the possible filtering options + * from the Http GET request parameters. + *

+ * For example the following could be a valid request: + * {@code /test-parameters?id.greaterThan=5&attr1.contains=something&attr2.specified=false} + *

+ * As Spring is unable to properly convert the types, unless + * specific {@link org.citrusframework.simulator.service.filter.Filter} class are used, we need to use fix type specific + * filters. + */ +@ParameterObject +@SuppressWarnings("common-java:DuplicatedBlocks") +public class TestParameterCriteria implements Serializable, Criteria { + + private static final long serialVersionUID = 1L; + + private StringFilter key; + + private StringFilter value; + + private InstantFilter createdDate; + + private InstantFilter lastModifiedDate; + + private LongFilter testResultId; + + private Boolean distinct; + + public TestParameterCriteria() {} + + public TestParameterCriteria(TestParameterCriteria other) { + this.key = other.key == null ? null : other.key.copy(); + this.value = other.value == null ? null : other.value.copy(); + this.createdDate = other.createdDate == null ? null : other.createdDate.copy(); + this.lastModifiedDate = other.lastModifiedDate == null ? null : other.lastModifiedDate.copy(); + this.testResultId = other.testResultId == null ? null : other.testResultId.copy(); + this.distinct = other.distinct; + } + + @Override + public TestParameterCriteria copy() { + return new TestParameterCriteria(this); + } + + public StringFilter getKey() { + return key; + } + + public StringFilter key() { + if (key == null) { + key = new StringFilter(); + } + return key; + } + + public void setKey(StringFilter key) { + this.key = key; + } + + public StringFilter getValue() { + return value; + } + + public StringFilter value() { + if (value == null) { + value = new StringFilter(); + } + return value; + } + + public void setValue(StringFilter value) { + this.value = value; + } + + public InstantFilter getCreatedDate() { + return createdDate; + } + + public InstantFilter createdDate() { + if (createdDate == null) { + createdDate = new InstantFilter(); + } + return createdDate; + } + + public void setCreatedDate(InstantFilter createdDate) { + this.createdDate = createdDate; + } + + public InstantFilter getLastModifiedDate() { + return lastModifiedDate; + } + + public InstantFilter lastModifiedDate() { + if (lastModifiedDate == null) { + lastModifiedDate = new InstantFilter(); + } + return lastModifiedDate; + } + + public void setLastModifiedDate(InstantFilter lastModifiedDate) { + this.lastModifiedDate = lastModifiedDate; + } + + public LongFilter getTestResultId() { + return testResultId; + } + + public LongFilter testResultId() { + if (testResultId == null) { + testResultId = new LongFilter(); + } + return testResultId; + } + + public void setTestResultId(LongFilter testResultId) { + this.testResultId = testResultId; + } + + public Boolean getDistinct() { + return distinct; + } + + public void setDistinct(Boolean distinct) { + this.distinct = distinct; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final TestParameterCriteria that = (TestParameterCriteria) o; + return ( + Objects.equals(key, that.key) && + Objects.equals(value, that.value) && + Objects.equals(createdDate, that.createdDate) && + Objects.equals(lastModifiedDate, that.lastModifiedDate) && + Objects.equals(testResultId, that.testResultId) && + Objects.equals(distinct, that.distinct) + ); + } + + @Override + public int hashCode() { + return Objects.hash(key, value, createdDate, lastModifiedDate, testResultId, distinct); + } + + // prettier-ignore + @Override + public String toString() { + return "TestParameterCriteria{" + + (key != null ? "key=" + key + ", " : "") + + (value != null ? "value=" + value + ", " : "") + + (createdDate != null ? "createdDate=" + createdDate + ", " : "") + + (lastModifiedDate != null ? "lastModifiedDate=" + lastModifiedDate + ", " : "") + + (testResultId != null ? "testResultId=" + testResultId + ", " : "") + + (distinct != null ? "distinct=" + distinct + ", " : "") + + "}"; + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/criteria/TestResultCriteria.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/criteria/TestResultCriteria.java new file mode 100644 index 000000000..847f2e4c1 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/criteria/TestResultCriteria.java @@ -0,0 +1,289 @@ +package org.citrusframework.simulator.service.criteria; + +import org.citrusframework.simulator.service.filter.InstantFilter; +import org.citrusframework.simulator.service.filter.IntegerFilter; +import org.citrusframework.simulator.service.filter.LongFilter; +import org.citrusframework.simulator.service.filter.StringFilter; +import org.springdoc.core.annotations.ParameterObject; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Criteria class for the {@link org.citrusframework.simulator.model.TestResult} entity. This class is used + * in {@link org.citrusframework.simulator.web.rest.TestResultResource} to receive all the possible filtering options + * from the Http GET request parameters. + *

+ * For example the following could be a valid request: + * {@code /test-results?id.greaterThan=5&attr1.contains=something&attr2.specified=false} + *

+ * As Spring is unable to properly convert the types, unless + * specific {@link org.citrusframework.simulator.service.filter.Filter} class are used, we need to use fix type specific + * filters. + */ +@ParameterObject +@SuppressWarnings("common-java:DuplicatedBlocks") +public class TestResultCriteria implements Serializable, Criteria { + + private static final long serialVersionUID = 1L; + + private LongFilter id; + + private IntegerFilter status; + + private StringFilter testName; + + private StringFilter className; + + private StringFilter errorMessage; + + private StringFilter failureStack; + + private StringFilter failureType; + + private InstantFilter createdDate; + + private InstantFilter lastModifiedDate; + + private StringFilter testParameterKey; + + private Boolean distinct; + + public TestResultCriteria() {} + + public TestResultCriteria(TestResultCriteria other) { + this.id = other.id == null ? null : other.id.copy(); + this.status = other.status == null ? null : other.status.copy(); + this.testName = other.testName == null ? null : other.testName.copy(); + this.className = other.className == null ? null : other.className.copy(); + this.errorMessage = other.errorMessage == null ? null : other.errorMessage.copy(); + this.failureStack = other.failureStack == null ? null : other.failureStack.copy(); + this.failureType = other.failureType == null ? null : other.failureType.copy(); + this.createdDate = other.createdDate == null ? null : other.createdDate.copy(); + this.lastModifiedDate = other.lastModifiedDate == null ? null : other.lastModifiedDate.copy(); + this.testParameterKey = other.testParameterKey == null ? null : other.testParameterKey.copy(); + this.distinct = other.distinct; + } + + @Override + public TestResultCriteria copy() { + return new TestResultCriteria(this); + } + + public LongFilter getId() { + return id; + } + + public LongFilter id() { + if (id == null) { + id = new LongFilter(); + } + return id; + } + + public void setId(LongFilter id) { + this.id = id; + } + + public IntegerFilter getStatus() { + return status; + } + + public IntegerFilter status() { + if (status == null) { + status = new IntegerFilter(); + } + return status; + } + + public void setStatus(IntegerFilter status) { + this.status = status; + } + + public StringFilter getTestName() { + return testName; + } + + public StringFilter testName() { + if (testName == null) { + testName = new StringFilter(); + } + return testName; + } + + public void setTestName(StringFilter testName) { + this.testName = testName; + } + + public StringFilter getClassName() { + return className; + } + + public StringFilter className() { + if (className == null) { + className = new StringFilter(); + } + return className; + } + + public void setClassName(StringFilter className) { + this.className = className; + } + + public StringFilter getErrorMessage() { + return errorMessage; + } + + public StringFilter errorMessage() { + if (errorMessage == null) { + errorMessage = new StringFilter(); + } + return errorMessage; + } + + public void setErrorMessage(StringFilter errorMessage) { + this.errorMessage = errorMessage; + } + + public StringFilter getFailureStack() { + return failureStack; + } + + public StringFilter failureStack() { + if (failureStack == null) { + failureStack = new StringFilter(); + } + return failureStack; + } + + public void setFailureStack(StringFilter failureStack) { + this.failureStack = failureStack; + } + + public StringFilter getFailureType() { + return failureType; + } + + public StringFilter failureType() { + if (failureType == null) { + failureType = new StringFilter(); + } + return failureType; + } + + public void setFailureType(StringFilter failureType) { + this.failureType = failureType; + } + + public InstantFilter getCreatedDate() { + return createdDate; + } + + public InstantFilter createdDate() { + if (createdDate == null) { + createdDate = new InstantFilter(); + } + return createdDate; + } + + public void setCreatedDate(InstantFilter createdDate) { + this.createdDate = createdDate; + } + + public InstantFilter getLastModifiedDate() { + return lastModifiedDate; + } + + public InstantFilter lastModifiedDate() { + if (lastModifiedDate == null) { + lastModifiedDate = new InstantFilter(); + } + return lastModifiedDate; + } + + public void setLastModifiedDate(InstantFilter lastModifiedDate) { + this.lastModifiedDate = lastModifiedDate; + } + + public StringFilter getTestParameterKey() { + return testParameterKey; + } + + public StringFilter testParameterId() { + if (testParameterKey == null) { + testParameterKey = new StringFilter(); + } + return testParameterKey; + } + + public void setTestParameterKey(StringFilter testParameterKey) { + this.testParameterKey = testParameterKey; + } + + public Boolean getDistinct() { + return distinct; + } + + public void setDistinct(Boolean distinct) { + this.distinct = distinct; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final TestResultCriteria that = (TestResultCriteria) o; + return ( + Objects.equals(id, that.id) && + Objects.equals(status, that.status) && + Objects.equals(testName, that.testName) && + Objects.equals(className, that.className) && + Objects.equals(errorMessage, that.errorMessage) && + Objects.equals(failureStack, that.failureStack) && + Objects.equals(failureType, that.failureType) && + Objects.equals(createdDate, that.createdDate) && + Objects.equals(lastModifiedDate, that.lastModifiedDate) && + Objects.equals(testParameterKey, that.testParameterKey) && + Objects.equals(distinct, that.distinct) + ); + } + + @Override + public int hashCode() { + return Objects.hash( + id, + status, + testName, + className, + errorMessage, + failureStack, + failureType, + createdDate, + lastModifiedDate, + testParameterKey, + distinct + ); + } + + // prettier-ignore + @Override + public String toString() { + return "TestResultCriteria{" + + (id != null ? "id=" + id + ", " : "") + + (status != null ? "status=" + status + ", " : "") + + (testName != null ? "testName=" + testName + ", " : "") + + (className != null ? "className=" + className + ", " : "") + + (errorMessage != null ? "errorMessage=" + errorMessage + ", " : "") + + (failureStack != null ? "failureStack=" + failureStack + ", " : "") + + (failureType != null ? "failureType=" + failureType + ", " : "") + + (createdDate != null ? "createdDate=" + createdDate + ", " : "") + + (lastModifiedDate != null ? "lastModifiedDate=" + lastModifiedDate + ", " : "") + + (testParameterKey != null ? "testParameterId=" + testParameterKey + ", " : "") + + (distinct != null ? "distinct=" + distinct + ", " : "") + + "}"; + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/dto/TestResultByStatus.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/dto/TestResultByStatus.java new file mode 100644 index 000000000..73e6794af --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/dto/TestResultByStatus.java @@ -0,0 +1,10 @@ +package org.citrusframework.simulator.service.dto; + +import java.util.Objects; + +public record TestResultByStatus(Long successful, Long failed, Long total) { + + public TestResultByStatus(Long successful, Long failed) { + this(Objects.isNull(successful) ? 0 : successful, Objects.isNull(failed)?0: failed, Objects.isNull(successful) || Objects.isNull(failed) ? 0: successful + failed); + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/Filter.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/Filter.java new file mode 100644 index 000000000..92c81f36d --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/Filter.java @@ -0,0 +1,200 @@ +package org.citrusframework.simulator.service.filter; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Base class for the various attribute filters. It can be added to a criteria class as a member, to support the + * following query parameters: + *

+ *      fieldName.equals='something'
+ *      fieldName.notEquals='somethingElse'
+ *      fieldName.specified=true
+ *      fieldName.specified=false
+ *      fieldName.in='something','other'
+ *      fieldName.notIn='something','other'
+ * 
+ */ +public class Filter implements Serializable { + + private static final long serialVersionUID = 1L; + private FIELD_TYPE equals; + private FIELD_TYPE notEquals; + private Boolean specified; + private List in; + private List notIn; + + /** + *

Constructor for Filter.

+ */ + public Filter() { + } + + /** + *

Constructor for Filter.

+ * + * @param filter a {@link Filter} object. + */ + public Filter(Filter filter) { + equals = filter.equals; + notEquals = filter.notEquals; + specified = filter.specified; + in = filter.in == null ? null : new ArrayList<>(filter.in); + notIn = filter.notIn == null ? null : new ArrayList<>(filter.notIn); + } + + /** + *

copy.

+ * + * @return a {@link Filter} object. + */ + public Filter copy() { + return new Filter<>(this); + } + + /** + *

Getter for the field equals.

+ * + * @return a FIELD_TYPE object. + */ + public FIELD_TYPE getEquals() { + return equals; + } + + /** + *

Setter for the field equals.

+ * + * @param equals a FIELD_TYPE object. + * @return a {@link Filter} object. + */ + public Filter setEquals(FIELD_TYPE equals) { + this.equals = equals; + return this; + } + + /** + *

Getter for the field notEquals.

+ * + * @return a FIELD_TYPE object. + */ + public FIELD_TYPE getNotEquals() { + return notEquals; + } + + /** + *

Setter for the field notEquals.

+ * + * @param notEquals a FIELD_TYPE object. + * @return a {@link Filter} object. + */ + public Filter setNotEquals(FIELD_TYPE notEquals) { + this.notEquals = notEquals; + return this; + } + + /** + *

Getter for the field specified.

+ * + * @return a {@link java.lang.Boolean} object. + */ + public Boolean getSpecified() { + return specified; + } + + /** + *

Setter for the field specified.

+ * + * @param specified a {@link java.lang.Boolean} object. + * @return a {@link Filter} object. + */ + public Filter setSpecified(Boolean specified) { + this.specified = specified; + return this; + } + + /** + *

Getter for the field in.

+ * + * @return a {@link java.util.List} object. + */ + public List getIn() { + return in; + } + + /** + *

Setter for the field in.

+ * + * @param in a {@link java.util.List} object. + * @return a {@link Filter} object. + */ + public Filter setIn(List in) { + this.in = in; + return this; + } + + /** + *

Getter for the field notIn.

+ * + * @return a {@link java.util.List} object. + */ + public List getNotIn() { + return notIn; + } + + /** + *

Setter for the field notIn.

+ * + * @param notIn a {@link java.util.List} object. + * @return a {@link Filter} object. + */ + public Filter setNotIn(List notIn) { + this.notIn = notIn; + return this; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Filter filter = (Filter) o; + return Objects.equals(equals, filter.equals) && + Objects.equals(notEquals, filter.notEquals) && + Objects.equals(specified, filter.specified) && + Objects.equals(in, filter.in) && + Objects.equals(notIn, filter.notIn); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return Objects.hash(equals, notEquals, specified, in, notIn); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return getFilterName() + " [" + + (getEquals() != null ? "equals=" + getEquals() + ", " : "") + + (getNotEquals() != null ? "notEquals=" + getNotEquals() + ", " : "") + + (getSpecified() != null ? "specified=" + getSpecified() + ", " : "") + + (getIn() != null ? "in=" + getIn() + ", " : "") + + (getNotIn() != null ? "notIn=" + getNotIn() : "") + + "]"; + } + + /** + *

getFilterName.

+ * + * @return a {@link java.lang.String} object. + */ + protected String getFilterName() { + return getClass().getSimpleName(); + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/InstantFilter.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/InstantFilter.java new file mode 100644 index 000000000..bb5b0f3f9 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/InstantFilter.java @@ -0,0 +1,101 @@ +package org.citrusframework.simulator.service.filter; + +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.Instant; +import java.util.List; + +/** + * Filter class for {@link java.time.Instant} type attributes. + * + * @see RangeFilter + */ +public class InstantFilter extends RangeFilter { + + private static final long serialVersionUID = 1L; + + /** + *

Constructor for InstantFilter.

+ */ + public InstantFilter() { + } + + /** + *

Constructor for InstantFilter.

+ * + * @param filter a {@link InstantFilter} object. + */ + public InstantFilter(InstantFilter filter) { + super(filter); + } + + /** {@inheritDoc} */ + @Override + public InstantFilter copy() { + return new InstantFilter(this); + } + + /** {@inheritDoc} */ + @Override + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + public InstantFilter setEquals(Instant equals) { + super.setEquals(equals); + return this; + } + + /** {@inheritDoc} */ + @Override + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + public InstantFilter setNotEquals(Instant equals) { + super.setNotEquals(equals); + return this; + } + + /** {@inheritDoc} */ + @Override + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + public InstantFilter setIn(List in) { + super.setIn(in); + return this; + } + + /** {@inheritDoc} */ + @Override + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + public InstantFilter setNotIn(List notIn) { + super.setNotIn(notIn); + return this; + } + + /** {@inheritDoc} */ + @Override + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + public InstantFilter setGreaterThan(Instant equals) { + super.setGreaterThan(equals); + return this; + } + + /** {@inheritDoc} */ + @Override + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + public InstantFilter setLessThan(Instant equals) { + super.setLessThan(equals); + return this; + } + + /** {@inheritDoc} */ + @Override + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + public InstantFilter setGreaterThanOrEqual(Instant equals) { + super.setGreaterThanOrEqual(equals); + return this; + } + + /** {@inheritDoc} */ + @Override + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + public InstantFilter setLessThanOrEqual(Instant equals) { + super.setLessThanOrEqual(equals); + return this; + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/IntegerFilter.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/IntegerFilter.java new file mode 100644 index 000000000..2d0e1374b --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/IntegerFilter.java @@ -0,0 +1,35 @@ +package org.citrusframework.simulator.service.filter; + +/** + * Filter class for {@link java.lang.Integer} type attributes. + * + * @see RangeFilter + */ +public class IntegerFilter extends RangeFilter { + + private static final long serialVersionUID = 1L; + + /** + *

Constructor for IntegerFilter.

+ */ + public IntegerFilter() { + } + + /** + *

Constructor for IntegerFilter.

+ * + * @param filter a {@link IntegerFilter} object. + */ + public IntegerFilter(IntegerFilter filter) { + super(filter); + } + + /** + *

copy.

+ * + * @return a {@link IntegerFilter} object. + */ + public IntegerFilter copy() { + return new IntegerFilter(this); + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/LongFilter.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/LongFilter.java new file mode 100644 index 000000000..579398c33 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/LongFilter.java @@ -0,0 +1,35 @@ +package org.citrusframework.simulator.service.filter; + +/** + * Filter class for {@link java.lang.Long} type attributes. + * + * @see RangeFilter + */ +public class LongFilter extends RangeFilter { + + private static final long serialVersionUID = 1L; + + /** + *

Constructor for LongFilter.

+ */ + public LongFilter() { + } + + /** + *

Constructor for LongFilter.

+ * + * @param filter a {@link LongFilter} object. + */ + public LongFilter(LongFilter filter) { + super(filter); + } + + /** + *

copy.

+ * + * @return a {@link LongFilter} object. + */ + public LongFilter copy() { + return new LongFilter(this); + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/RangeFilter.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/RangeFilter.java new file mode 100644 index 000000000..7c2da9665 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/RangeFilter.java @@ -0,0 +1,182 @@ +package org.citrusframework.simulator.service.filter; + +import java.util.Objects; + +/** + * Filter class for Comparable types, where less than / greater than / etc relations could be interpreted. It can be + * added to a criteria class as a member, to support the following query parameters: + *
+ *      fieldName.equals=42
+ *      fieldName.notEquals=42
+ *      fieldName.specified=true
+ *      fieldName.specified=false
+ *      fieldName.in=43,42
+ *      fieldName.notIn=43,42
+ *      fieldName.greaterThan=41
+ *      fieldName.lessThan=44
+ *      fieldName.greaterThanOrEqual=42
+ *      fieldName.lessThanOrEqual=44
+ * 
+ * Due to problems with the type conversions, the descendant classes should be used, where the generic type parameter + * is materialized. + * + * @param the type of filter. + * @see IntegerFilter + * @see LongFilter + * @see InstantFilter + */ +public class RangeFilter> extends Filter { + + private static final long serialVersionUID = 1L; + + private FIELD_TYPE greaterThan; + private FIELD_TYPE lessThan; + private FIELD_TYPE greaterThanOrEqual; + private FIELD_TYPE lessThanOrEqual; + + /** + *

Constructor for RangeFilter.

+ */ + public RangeFilter() { + } + + /** + *

Constructor for RangeFilter.

+ * + * @param filter a {@link RangeFilter} object. + */ + public RangeFilter(RangeFilter filter) { + super(filter); + greaterThan = filter.greaterThan; + lessThan = filter.lessThan; + greaterThanOrEqual = filter.greaterThanOrEqual; + lessThanOrEqual = filter.lessThanOrEqual; + } + + /** {@inheritDoc} */ + @Override + public RangeFilter copy() { + return new RangeFilter<>(this); + } + + /** + *

Getter for the field greaterThan.

+ * + * @return a FIELD_TYPE object. + */ + public FIELD_TYPE getGreaterThan() { + return greaterThan; + } + + /** + *

Setter for the field greaterThan.

+ * + * @param greaterThan a FIELD_TYPE object. + * @return a {@link RangeFilter} object. + */ + public RangeFilter setGreaterThan(FIELD_TYPE greaterThan) { + this.greaterThan = greaterThan; + return this; + } + + /** + *

Getter for the field lessThan.

+ * + * @return a FIELD_TYPE object. + */ + public FIELD_TYPE getLessThan() { + return lessThan; + } + + /** + *

Setter for the field lessThan.

+ * + * @param lessThan a FIELD_TYPE object. + * @return a {@link RangeFilter} object. + */ + public RangeFilter setLessThan(FIELD_TYPE lessThan) { + this.lessThan = lessThan; + return this; + } + + /** + *

Getter for the field greaterThanOrEqual.

+ * + * @return a FIELD_TYPE object. + */ + public FIELD_TYPE getGreaterThanOrEqual() { + return greaterThanOrEqual; + } + + /** + *

Setter for the field greaterThanOrEqual.

+ * + * @param greaterThanOrEqual a FIELD_TYPE object. + * @return a {@link RangeFilter} object. + */ + public RangeFilter setGreaterThanOrEqual(FIELD_TYPE greaterThanOrEqual) { + this.greaterThanOrEqual = greaterThanOrEqual; + return this; + } + + /** + *

Getter for the field lessThanOrEqual.

+ * + * @return a FIELD_TYPE object. + */ + public FIELD_TYPE getLessThanOrEqual() { + return lessThanOrEqual; + } + + /** + *

Setter for the field lessThanOrEqual.

+ * + * @param lessThanOrEqual a FIELD_TYPE object. + * @return a {@link RangeFilter} object. + */ + public RangeFilter setLessThanOrEqual(FIELD_TYPE lessThanOrEqual) { + this.lessThanOrEqual = lessThanOrEqual; + return this; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + RangeFilter that = (RangeFilter) o; + return Objects.equals(greaterThan, that.greaterThan) && + Objects.equals(lessThan, that.lessThan) && + Objects.equals(greaterThanOrEqual, that.greaterThanOrEqual) && + Objects.equals(lessThanOrEqual, that.lessThanOrEqual); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return getFilterName() + " [" + + (getEquals() != null ? "equals=" + getEquals() + ", " : "") + + (getNotEquals() != null ? "notEquals=" + getNotEquals() + ", " : "") + + (getSpecified() != null ? "specified=" + getSpecified() + ", " : "") + + (getIn() != null ? "in=" + getIn() + ", " : "") + + (getNotIn() != null ? "notIn=" + getNotIn() + ", " : "") + + (getGreaterThan() != null ? "greaterThan=" + getGreaterThan() + ", " : "") + + (getLessThan() != null ? "lessThan=" + getLessThan() + ", " : "") + + (getGreaterThanOrEqual() != null ? "greaterThanOrEqual=" + getGreaterThanOrEqual() + ", " : "") + + (getLessThanOrEqual() != null ? "lessThanOrEqual=" + getLessThanOrEqual() : "") + + "]"; + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/StringFilter.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/StringFilter.java new file mode 100644 index 000000000..02393a4f4 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/filter/StringFilter.java @@ -0,0 +1,125 @@ +package org.citrusframework.simulator.service.filter; + +import java.util.Objects; + +/** + * Class for filtering attributes with {@link java.lang.String} type. + * It can be added to a criteria class as a member, to support the following query parameters: + * + * fieldName.equals='something' + * fieldName.notEquals='something' + * fieldName.specified=true + * fieldName.specified=false + * fieldName.in='something','other' + * fieldName.notIn='something','other' + * fieldName.contains='thing' + * fieldName.doesNotContain='thing' + * + */ +public class StringFilter extends Filter { + + private static final long serialVersionUID = 1L; + + private String contains; + private String doesNotContain; + + /** + *

Constructor for StringFilter.

+ */ + public StringFilter() { + } + + /** + *

Constructor for StringFilter.

+ * + * @param filter a {@link StringFilter} object. + */ + public StringFilter(StringFilter filter) { + super(filter); + contains = filter.contains; + doesNotContain = filter.doesNotContain; + } + + /** {@inheritDoc} */ + @Override + public StringFilter copy() { + return new StringFilter(this); + } + + /** + *

Getter for the field contains.

+ * + * @return a {@link java.lang.String} object. + */ + public String getContains() { + return contains; + } + + /** + *

Setter for the field contains.

+ * + * @param contains a {@link java.lang.String} object. + * @return a {@link StringFilter} object. + */ + public StringFilter setContains(String contains) { + this.contains = contains; + return this; + } + + /** + *

Getter for the field doesNotContain.

+ * + * @return a {@link java.lang.String} object. + */ + public String getDoesNotContain() { + return doesNotContain; + } + + /** + *

Setter for the field doesNotContain.

+ * + * @param doesNotContain a {@link java.lang.String} object. + * @return a {@link StringFilter} object. + */ + public StringFilter setDoesNotContain(String doesNotContain) { + this.doesNotContain = doesNotContain; + return this; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + StringFilter that = (StringFilter) o; + return Objects.equals(contains, that.contains) && + Objects.equals(doesNotContain, that.doesNotContain); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), contains, doesNotContain); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return getFilterName() + " [" + + (getEquals() != null ? "equals=" + getEquals() + ", " : "") + + (getNotEquals() != null ? "notEquals=" + getNotEquals() + ", " : "") + + (getSpecified() != null ? "specified=" + getSpecified() + ", " : "") + + (getIn() != null ? "in=" + getIn() + ", " : "") + + (getNotIn() != null ? "notIn=" + getNotIn() + ", " : "") + + (getContains() != null ? "contains=" + getContains() + ", " : "") + + (getDoesNotContain() != null ? "doesNotContain=" + getDoesNotContain() : "") + + "]"; + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/impl/TestParameterServiceImpl.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/impl/TestParameterServiceImpl.java new file mode 100644 index 000000000..83c41591e --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/impl/TestParameterServiceImpl.java @@ -0,0 +1,53 @@ +package org.citrusframework.simulator.service.impl; + +import org.citrusframework.simulator.model.TestParameter; +import org.citrusframework.simulator.repository.TestParameterRepository; +import org.citrusframework.simulator.service.TestParameterService; +import org.citrusframework.simulator.service.TestResultService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +/** + * Service Implementation for managing {@link org.citrusframework.simulator.model.TestParameter}. + */ +@Service +@Transactional +public class TestParameterServiceImpl implements TestParameterService { + + private final Logger logger = LoggerFactory.getLogger(TestParameterServiceImpl.class); + + private final TestResultService testResultService; + private final TestParameterRepository testParameterRepository; + + public TestParameterServiceImpl(TestResultService testResultService, TestParameterRepository testParameterRepository) { + this.testResultService = testResultService; + this.testParameterRepository = testParameterRepository; + } + + @Override + public TestParameter save(TestParameter testParameter) { + logger.debug("Request to save TestParameter : {}", testParameter); + return testParameterRepository.save(testParameter); + } + + @Override + @Transactional(readOnly = true) + public Page findAll(Pageable pageable) { + logger.debug("Request to get all TestParameters"); + return testParameterRepository.findAll(pageable); + } + + @Override + @Transactional(readOnly = true) + public Optional findOne(Long testResultId, String key) { + logger.debug("Request to get TestParameter '{}' of TestResult : {}", key, testResultId); +return testResultService.findOne(testResultId) + .flatMap(testResult -> testParameterRepository.findById(new TestParameter.TestParameterId(key, testResult))); + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/service/impl/TestResultServiceImpl.java b/simulator-starter/src/main/java/org/citrusframework/simulator/service/impl/TestResultServiceImpl.java new file mode 100644 index 000000000..7c84544f6 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/service/impl/TestResultServiceImpl.java @@ -0,0 +1,63 @@ +package org.citrusframework.simulator.service.impl; + +import org.citrusframework.simulator.model.TestResult; +import org.citrusframework.simulator.repository.TestResultRepository; +import org.citrusframework.simulator.service.TestResultService; +import org.citrusframework.simulator.service.dto.TestResultByStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +/** + * Service Implementation for managing {@link org.citrusframework.simulator.model.TestResult}. + */ +@Service +@Transactional +public class TestResultServiceImpl implements TestResultService { + + private final Logger logger = LoggerFactory.getLogger(TestResultServiceImpl.class); + + private final TestResultRepository testResultRepository; + + public TestResultServiceImpl(TestResultRepository testResultRepository) { + this.testResultRepository = testResultRepository; + } + + @Override + public TestResult transformAndSave(org.citrusframework.TestResult testResult) { + logger.debug("Request to save citrus TestResult : {}", testResult); + return save(new TestResult(testResult)); + } + + @Override + public TestResult save(TestResult testResult) { + logger.debug("Request to save TestResult : {}", testResult); + return testResultRepository.save(testResult); + } + + @Override + @Transactional(readOnly = true) + public Page findAll(Pageable pageable) { + logger.debug("Request to get all TestResults"); + return testResultRepository.findAll(pageable); + } + + @Override + @Transactional(readOnly = true) + public Optional findOne(Long id) { + logger.debug("Request to get TestResult : {}", id); + return testResultRepository.findById(id); + } + + @Override + @Transactional(readOnly = true) + public TestResultByStatus countByStatus() { + logger.debug("count total by status"); + return testResultRepository.countByStatus(); + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/web/rest/TestParameterResource.java b/simulator-starter/src/main/java/org/citrusframework/simulator/web/rest/TestParameterResource.java new file mode 100644 index 000000000..5e4913e4f --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/web/rest/TestParameterResource.java @@ -0,0 +1,85 @@ +package org.citrusframework.simulator.web.rest; + +import org.citrusframework.simulator.model.TestParameter; +import org.citrusframework.simulator.service.TestParameterQueryService; +import org.citrusframework.simulator.service.TestParameterService; +import org.citrusframework.simulator.service.criteria.TestParameterCriteria; +import org.citrusframework.simulator.web.util.PaginationUtil; +import org.citrusframework.simulator.web.util.ResponseUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import java.util.List; +import java.util.Optional; + +/** + * REST controller for managing {@link org.citrusframework.simulator.model.TestParameter}. + */ +@RestController +@RequestMapping("/api") +public class TestParameterResource { + + private final Logger logger = LoggerFactory.getLogger(TestParameterResource.class); + + private final TestParameterService testParameterService; + + private final TestParameterQueryService testParameterQueryService; + + public TestParameterResource(TestParameterService testParameterService, TestParameterQueryService testParameterQueryService) { + this.testParameterService = testParameterService; + this.testParameterQueryService = testParameterQueryService; + } + + /** + * {@code GET /test-parameters} : get all the testParameters. + * + * @param pageable the pagination information. + * @param criteria the criteria which the requested entities should match. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of testParameters in body. + */ + @GetMapping("/test-parameters") + public ResponseEntity> getAllTestParameters( + TestParameterCriteria criteria, + @org.springdoc.core.annotations.ParameterObject Pageable pageable + ) { + logger.debug("REST request to get TestParameters by criteria: {}", criteria); + + Page page = testParameterQueryService.findByCriteria(criteria, pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } + + /** + * {@code GET /test-parameters/count} : count all the testParameters. + * + * @param criteria the criteria which the requested entities should match. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the count in body. + */ + @GetMapping("/test-parameters/count") + public ResponseEntity countTestParameters(TestParameterCriteria criteria) { + logger.debug("REST request to count TestParameters by criteria: {}", criteria); + return ResponseEntity.ok().body(testParameterQueryService.countByCriteria(criteria)); + } + + /** + * {@code GET /test-parameters/:id} : get the "id" testParameter. + * + * @param id the id of the testParameter to retrieve. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the testParameter, or with status {@code 404 (Not Found)}. + */ + @GetMapping("/test-parameters/{testResultId}/{key}") + public ResponseEntity getTestParameter(@PathVariable Long testResultId, @PathVariable String key) { + logger.debug("REST request to get TestParameter '{}' of TestResult: {}", key, testResultId); + Optional testParameter = testParameterService.findOne(testResultId, key); + return ResponseUtil.wrapOrNotFound(testParameter); + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/web/rest/TestResultResource.java b/simulator-starter/src/main/java/org/citrusframework/simulator/web/rest/TestResultResource.java new file mode 100644 index 000000000..aab7f553b --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/web/rest/TestResultResource.java @@ -0,0 +1,97 @@ +package org.citrusframework.simulator.web.rest; + +import org.citrusframework.simulator.model.TestResult; +import org.citrusframework.simulator.service.TestResultQueryService; +import org.citrusframework.simulator.service.TestResultService; +import org.citrusframework.simulator.service.criteria.TestResultCriteria; +import org.citrusframework.simulator.service.dto.TestResultByStatus; +import org.citrusframework.simulator.web.util.PaginationUtil; +import org.citrusframework.simulator.web.util.ResponseUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import java.util.List; +import java.util.Optional; + +/** + * REST controller for managing {@link org.citrusframework.simulator.model.TestResult}. + */ +@RestController +@RequestMapping("/api") +public class TestResultResource { + + private final Logger logger = LoggerFactory.getLogger(TestResultResource.class); + + private final TestResultService testResultService; + + private final TestResultQueryService testResultQueryService; + + public TestResultResource(TestResultService testResultService, TestResultQueryService testResultQueryService) { + this.testResultService = testResultService; + this.testResultQueryService = testResultQueryService; + } + + /** + * {@code GET /test-results} : get all the testResults. + * + * @param pageable the pagination information. + * @param criteria the criteria which the requested entities should match. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of testResults in body. + */ + @GetMapping("/test-results") + public ResponseEntity> getAllTestResults( + TestResultCriteria criteria, + @org.springdoc.core.annotations.ParameterObject Pageable pageable + ) { + logger.debug("REST request to get TestResults by criteria: {}", criteria); + + Page page = testResultQueryService.findByCriteria(criteria, pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } + + /** + * {@code GET /test-results/count} : count all the testResults. + * + * @param criteria the criteria which the requested entities should match. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the count in body. + */ + @GetMapping("/test-results/count") + public ResponseEntity countTestResults(TestResultCriteria criteria) { + logger.debug("REST request to count TestResults by criteria: {}", criteria); + return ResponseEntity.ok().body(testResultQueryService.countByCriteria(criteria)); + } + + /** + * {@code GET /test-results/count-by-status} : count all the testResults by their status. + * + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the count in body. + */ + @GetMapping("/test-results/count-by-status") + public ResponseEntity countTestResultsByStatus() { + logger.debug("REST request to count total TestResults by status"); + return ResponseEntity.ok().body(testResultService.countByStatus()); + } + + /** + * {@code GET /test-results/:id} : get the "id" testResult. + * + * @param id the id of the testResult to retrieve. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the testResult, or with status {@code 404 (Not Found)}. + */ + @GetMapping("/test-results/{id}") + public ResponseEntity getTestResult(@PathVariable Long id) { + logger.debug("REST request to get TestResult : {}", id); + Optional testResult = testResultService.findOne(id); + return ResponseUtil.wrapOrNotFound(testResult); + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/web/util/PaginationUtil.java b/simulator-starter/src/main/java/org/citrusframework/simulator/web/util/PaginationUtil.java new file mode 100644 index 000000000..a12ec24e9 --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/web/util/PaginationUtil.java @@ -0,0 +1,62 @@ +package org.citrusframework.simulator.web.util; + +import org.springframework.data.domain.Page; +import org.springframework.http.HttpHeaders; +import org.springframework.web.util.UriComponentsBuilder; + +import java.text.MessageFormat; + + +/** + * Utility class for handling pagination. + * + *

+ * Pagination uses the same principles as the GitHub API, + * and follow RFC 5988 (Link header). + */ +public interface PaginationUtil { + + String HEADER_X_TOTAL_COUNT = "X-Total-Count"; + String HEADER_LINK_FORMAT = "<{0}>; rel=\"{1}\""; + + /** + * Generate pagination headers for a Spring Data {@link org.springframework.data.domain.Page} object. + * + * @param uriBuilder The URI builder. + * @param page The page. + * @param The type of object. + * @return http header. + */ + static HttpHeaders generatePaginationHttpHeaders(UriComponentsBuilder uriBuilder, Page page) { + HttpHeaders headers = new HttpHeaders(); + headers.add(HEADER_X_TOTAL_COUNT, Long.toString(page.getTotalElements())); + int pageNumber = page.getNumber(); + int pageSize = page.getSize(); + StringBuilder link = new StringBuilder(); + if (pageNumber < page.getTotalPages() - 1) { + link.append(prepareLink(uriBuilder, pageNumber + 1, pageSize, "next")) + .append(","); + } + if (pageNumber > 0) { + link.append(prepareLink(uriBuilder, pageNumber - 1, pageSize, "prev")) + .append(","); + } + link.append(prepareLink(uriBuilder, page.getTotalPages() - 1, pageSize, "last")) + .append(",") + .append(prepareLink(uriBuilder, 0, pageSize, "first")); + headers.add(HttpHeaders.LINK, link.toString()); + return headers; + } + + private static String prepareLink(UriComponentsBuilder uriBuilder, int pageNumber, int pageSize, String relType) { + return MessageFormat.format(HEADER_LINK_FORMAT, preparePageUri(uriBuilder, pageNumber, pageSize), relType); + } + + private static String preparePageUri(UriComponentsBuilder uriBuilder, int pageNumber, int pageSize) { + return uriBuilder.replaceQueryParam("page", Integer.toString(pageNumber)) + .replaceQueryParam("size", Integer.toString(pageSize)) + .toUriString() + .replace(",", "%2C") + .replace(";", "%3B"); + } +} diff --git a/simulator-starter/src/main/java/org/citrusframework/simulator/web/util/ResponseUtil.java b/simulator-starter/src/main/java/org/citrusframework/simulator/web/util/ResponseUtil.java new file mode 100644 index 000000000..3ed02565c --- /dev/null +++ b/simulator-starter/src/main/java/org/citrusframework/simulator/web/util/ResponseUtil.java @@ -0,0 +1,41 @@ +package org.citrusframework.simulator.web.util; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.server.ResponseStatusException; + +import java.util.Optional; + + +/** + * Utility class for ResponseEntity creation. + */ +public interface ResponseUtil { + + /** + * Wrap the optional into a {@link org.springframework.http.ResponseEntity} with an {@link org.springframework.http.HttpStatus#OK} status, or if it's empty, it + * returns a {@link org.springframework.http.ResponseEntity} with {@link org.springframework.http.HttpStatus#NOT_FOUND}. + * + * @param type of the response + * @param maybeResponse response to return if present + * @return response containing {@code maybeResponse} if present or {@link org.springframework.http.HttpStatus#NOT_FOUND} + */ + static ResponseEntity wrapOrNotFound(Optional maybeResponse) { + return wrapOrNotFound(maybeResponse, null); + } + + /** + * Wrap the optional into a {@link org.springframework.http.ResponseEntity} with an {@link org.springframework.http.HttpStatus#OK} status with the headers, or if it's + * empty, throws a {@link org.springframework.web.server.ResponseStatusException} with status {@link org.springframework.http.HttpStatus#NOT_FOUND}. + * + * @param type of the response + * @param maybeResponse response to return if present + * @param header headers to be added to the response + * @return response containing {@code maybeResponse} if present + */ + static ResponseEntity wrapOrNotFound(Optional maybeResponse, HttpHeaders header) { + return maybeResponse.map(response -> ResponseEntity.ok().headers(header).body(response)) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/IntegrationTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/IntegrationTest.java new file mode 100644 index 000000000..5fc15912e --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/IntegrationTest.java @@ -0,0 +1,20 @@ +package org.citrusframework.simulator; + +import org.citrusframework.simulator.test.TestApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Base composite annotation for integration tests. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@SpringBootTest(classes = { TestApplication.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public @interface IntegrationTest { +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/repository/TestResultRepositoryIT.java b/simulator-starter/src/test/java/org/citrusframework/simulator/repository/TestResultRepositoryIT.java new file mode 100644 index 000000000..ed6ce6866 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/repository/TestResultRepositoryIT.java @@ -0,0 +1,56 @@ +package org.citrusframework.simulator.repository; + +import org.citrusframework.simulator.IntegrationTest; +import org.citrusframework.simulator.model.TestResult; +import org.citrusframework.simulator.service.dto.TestResultByStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Testing custom methods in {@link TestResultRepository}. + */ +@IntegrationTest +class TestResultRepositoryIT { + + @Autowired + private TestResultRepository testResultRepository; + + private List testResults; + + @BeforeEach + void beforeEachSetup(){ + testResults = testResultRepository.saveAll( + List.of( + TestResult.builder() + .testName("Test-1") + .className(getClass().getSimpleName()) + .status(TestResult.Status.SUCCESS.getId()).build(), + TestResult.builder() + .testName("Test-2") + .className(getClass().getSimpleName()) + .status(TestResult.Status.FAILURE.getId()).build() + ) + ); + } + @Test + @Transactional + void countByStatus(){ + TestResultByStatus testResultByStatus = testResultRepository.countByStatus(); + + assertEquals(2, testResultByStatus.total()); + assertEquals(1, testResultByStatus.successful()); + assertEquals(1, testResultByStatus.failed()); + } + + @AfterEach + void afterEachTeardown(){ + testResultRepository.deleteAll(testResults); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/MessageRepositoryTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/MessageRepositoryTest.java deleted file mode 100644 index 68b750640..000000000 --- a/simulator-starter/src/test/java/org/citrusframework/simulator/service/MessageRepositoryTest.java +++ /dev/null @@ -1,197 +0,0 @@ -package org.citrusframework.simulator.service; - -import org.citrusframework.simulator.SimulatorAutoConfiguration; -import org.citrusframework.simulator.config.SimulatorConfigurationProperties; -import org.citrusframework.simulator.model.Message; -import org.citrusframework.simulator.model.Message.Direction; -import org.citrusframework.simulator.model.MessageFilter; -import org.citrusframework.simulator.repository.MessageRepository; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; -import org.testng.Assert; - -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -@DataJpaTest -@ContextConfiguration(classes = { SimulatorAutoConfiguration.class }) -class MessageRepositoryTest extends AbstractTestNGSpringContextTests{ - - private static final QueryFilterAdapterFactory QUERY_FILTER_ADAPTER_FACTORY = new QueryFilterAdapterFactory( - new SimulatorConfigurationProperties()); - - private static final String PAYLOAD = "This is a test message!"; - private static final String HEADER_NAME = "h1"; - private static final String HEADER_NAME2 = "h2"; - - @Autowired - private MessageService service; - - @Autowired - private MessageRepository messageRepository; - - @Test - void testFindByHeader() { - String uid = createTestMessage(); - - MessageFilter filter = new MessageFilter(); - filter.setDirectionOutbound(false); - filter.setHeaderFilter(HEADER_NAME + ":" + uid); - - MessageFilter filterAdapter = QUERY_FILTER_ADAPTER_FACTORY.getQueryAdapter(filter); - - List result = messageRepository.find(filterAdapter); - - Assert.assertEquals(1, result.size()); - Assert.assertEquals(uid, result.get(0).getCitrusMessageId()); - Assert.assertEquals(PAYLOAD, result.get(0).getPayload()); - - filter.setHeaderFilter(HEADER_NAME + ":" + uid + "_3"); - result = messageRepository.find(filterAdapter); - Assert.assertEquals(0, result.size()); - } - - @Test - void testFindByHeaderMulti() { - String uid = createTestMessage(); - - MessageFilter filter = new MessageFilter(); - filter.setDirectionOutbound(false); - filter.setHeaderFilter(HEADER_NAME + ":" + uid + ";" + HEADER_NAME2 + ":" + uid + "_2"); - - MessageFilter filterAdapter = QUERY_FILTER_ADAPTER_FACTORY.getQueryAdapter(filter); - - List result = messageRepository.find(filterAdapter); - - Assert.assertEquals(1, result.size()); - Assert.assertEquals(uid, result.get(0).getCitrusMessageId()); - Assert.assertEquals(PAYLOAD, result.get(0).getPayload()); - - filter.setHeaderFilter(HEADER_NAME + ":" + uid + ";" + HEADER_NAME2 + ":" + uid + "_3"); - - result = messageRepository.find(filterAdapter); - Assert.assertEquals(0, result.size()); - } - - @Test - void testFindByHeaderLike() { - String innerUid = UUID.randomUUID().toString(); - String uid = createTestMessage("PRE" + innerUid + "POST", PAYLOAD); - - MessageFilter filter = new MessageFilter(); - filter.setDirectionOutbound(false); - - filter.setHeaderFilter(HEADER_NAME + ":" + "%" + innerUid + "%"); - - MessageFilter filterAdapter = QUERY_FILTER_ADAPTER_FACTORY.getQueryAdapter(filter); - List result = messageRepository.find(filterAdapter); - - Assert.assertEquals(1, result.size()); - Assert.assertEquals(uid, result.get(0).getCitrusMessageId()); - Assert.assertEquals(PAYLOAD, result.get(0).getPayload()); - } - - @Test - void testFindByPayloadLike() { - String uid = UUID.randomUUID().toString(); - String specificPayload = "Pay" + uid + "LOAD"; - uid = createTestMessage(uid, specificPayload); - - MessageFilter filter = new MessageFilter(); - filter.setDirectionOutbound(false); - filter.setContainingText("%" + uid + "%"); - - MessageFilter filterAdapter = QUERY_FILTER_ADAPTER_FACTORY.getQueryAdapter(filter); - List result = messageRepository.find(filterAdapter); - - Assert.assertEquals(1, result.size()); - Assert.assertEquals(uid, result.get(0).getCitrusMessageId()); - Assert.assertEquals(specificPayload, result.get(0).getPayload()); - } - - @Test - void testFindByPayload() { - String uid = UUID.randomUUID().toString(); - String payload = PAYLOAD+" "+uid; - uid = createTestMessage(uid, payload); - - MessageFilter filter = new MessageFilter(); - filter.setDirectionOutbound(false); - filter.setContainingText("%"+uid+"%"); - - MessageFilter filterAdapter = QUERY_FILTER_ADAPTER_FACTORY.getQueryAdapter(filter); - - List result = messageRepository.find(filterAdapter); - - Assert.assertEquals(1, result.size()); - Assert.assertEquals(uid, result.get(0).getCitrusMessageId()); - Assert.assertEquals(payload, result.get(0).getPayload()); - } - - @Test - void testFindByAllParams() { - Instant startSavingDate = now(); - String uid = createTestMessage(); - Instant endSavingDate = now(); - - // Filter by all valid - MessageFilter filter = new MessageFilter(); - filter.setFromDate(startSavingDate); - filter.setToDate(endSavingDate); - filter.setDirectionOutbound(false); - filter.setContainingText(PAYLOAD); - filter.setHeaderFilter(HEADER_NAME + ":" + uid); - - MessageFilter filterAdapter = QUERY_FILTER_ADAPTER_FACTORY.getQueryAdapter(filter); - List result = messageRepository.find(filterAdapter); - - Assert.assertEquals(1, result.size()); - Assert.assertEquals(PAYLOAD, result.get(0).getPayload()); - } - - @Test - void testPaging() { - String uniquePayload = "PagingPayload" + UUID.randomUUID().toString(); - for (int i = 0; i < 100; i++) { - createTestMessage(UUID.randomUUID().toString(), uniquePayload); - } - - MessageFilter filter = new MessageFilter(); - filter.setDirectionOutbound(false); - filter.setContainingText(uniquePayload); - filter.setPageNumber(0); - filter.setPageSize(33); - - MessageFilter filterAdapter = QUERY_FILTER_ADAPTER_FACTORY.getQueryAdapter(filter); - List result = messageRepository.find(filterAdapter); - - Assert.assertEquals(33, result.size()); - } - - private Instant now() { - return LocalDateTime.now().toInstant(ZoneOffset.UTC); - } - - private String createTestMessage() { - return createTestMessage(UUID.randomUUID().toString(), PAYLOAD); - } - - private String createTestMessage(String uid, String payload) { - - Map headers = new HashMap(); - headers.put(HEADER_NAME, uid); - headers.put(HEADER_NAME2, uid + "_2"); - - service.saveMessage(Direction.INBOUND, payload, uid, headers); - - return uid; - } -} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/MessageServiceTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/MessageServiceTest.java index adca5d9eb..f1d79ff54 100644 --- a/simulator-starter/src/test/java/org/citrusframework/simulator/service/MessageServiceTest.java +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/MessageServiceTest.java @@ -24,12 +24,10 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; -import org.springframework.data.domain.Pageable; import org.testng.Assert; import org.testng.annotations.AfterMethod; import java.time.Instant; -import java.util.Collection; import java.util.Collections; import java.util.List; @@ -41,8 +39,6 @@ public class MessageServiceTest { new SimulatorConfigurationProperties()); private MessageRepository messageRepository; - private ArgumentCaptor> directionsCaptor; - private ArgumentCaptor pageableCaptor; private MessageService sut; private ArgumentCaptor messageFilterCaptor; @@ -52,8 +48,6 @@ public class MessageServiceTest { public void init() { messageFilterCaptor = ArgumentCaptor.forClass(MessageFilter.class); messageRepository = Mockito.mock(MessageRepository.class); - directionsCaptor = ArgumentCaptor.forClass(Collection.class); - pageableCaptor = ArgumentCaptor.forClass(Pageable.class); sut = new MessageService(messageRepository, queryFilterAdapterFactory); } @@ -116,5 +110,4 @@ private void assertDirectionMatches(boolean inbound, boolean outbound) { Assert.assertEquals(messageFilterCaptor.getValue().getDirectionInbound(), (Boolean) inbound); Assert.assertEquals(messageFilterCaptor.getValue().getDirectionOutbound(), (Boolean) outbound); } - } diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/ScenarioExecutionRepositoryTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/ScenarioExecutionRepositoryTest.java deleted file mode 100644 index 9aff6a0e6..000000000 --- a/simulator-starter/src/test/java/org/citrusframework/simulator/service/ScenarioExecutionRepositoryTest.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2006-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.citrusframework.simulator.service; - -import jakarta.persistence.EntityManager; -import org.citrusframework.DefaultTestCase; -import org.citrusframework.simulator.SimulatorAutoConfiguration; -import org.citrusframework.simulator.config.SimulatorConfigurationProperties; -import org.citrusframework.simulator.model.Message.Direction; -import org.citrusframework.simulator.model.ScenarioExecution; -import org.citrusframework.simulator.model.ScenarioExecution.Status; -import org.citrusframework.simulator.model.ScenarioExecutionFilter; -import org.citrusframework.simulator.repository.ScenarioExecutionRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; -import org.testng.annotations.Test; - -import java.time.Instant; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static org.testng.AssertJUnit.assertEquals; - -@DataJpaTest -@ContextConfiguration(classes = {SimulatorAutoConfiguration.class}) -public class ScenarioExecutionRepositoryTest extends AbstractTestNGSpringContextTests { - - private static final String TEST_SCENARIO= "test-scenario"; - - private static final SimulatorConfigurationProperties PROPERTIES = new SimulatorConfigurationProperties(); - - private static final QueryFilterAdapterFactory queryFilterAdapterFactory = new QueryFilterAdapterFactory(PROPERTIES); - - private static final String PAYLOAD = "test-payload"; - - private static final String IN_HEADER_NAME1 = "IH1"; - private static final String IN_HEADER_NAME2 = "IH2"; - - private static final String OUT_HEADER_NAME1 = "OH1"; - private static final String OUT_HEADER_NAME2 = "OH2"; - - @Autowired - private ActivityService activityService; - - @Autowired - private EntityManager entityManager; - - @Autowired - private ScenarioExecutionRepository scenarioExecutionRepository; - - @Test - void testFindByScenarioName() { - String inUid = UUID.randomUUID().toString(); - String outUid = UUID.randomUUID().toString(); - - String uniqueScenarioName = TEST_SCENARIO + UUID.randomUUID().toString(); - createTestScenarioExecution(uniqueScenarioName, inUid, PAYLOAD, outUid, PAYLOAD, Status.SUCCESS); - - ScenarioExecutionFilter scenarioExecutionFilter = new ScenarioExecutionFilter(); - scenarioExecutionFilter.setScenarioName(uniqueScenarioName); - - ScenarioExecutionFilter queryFilter = queryFilterAdapterFactory.getQueryAdapter(scenarioExecutionFilter); - - assertEquals(1, scenarioExecutionRepository.find(queryFilter).size()); - } - - @Test - void testFindByScenarioStatus() { - String inUid = UUID.randomUUID().toString(); - String outUid = UUID.randomUUID().toString(); - - String uniqueScenarioName = TEST_SCENARIO + UUID.randomUUID(); - createTestScenarioExecution(uniqueScenarioName, inUid + 1, PAYLOAD, outUid + 1, PAYLOAD, Status.SUCCESS); - Long failedScenarioExecutionId = createTestScenarioExecution(uniqueScenarioName, inUid + 2, PAYLOAD, outUid + 2, PAYLOAD, Status.FAILED); - - ScenarioExecutionFilter scenarioExecutionFilter = new ScenarioExecutionFilter(); - scenarioExecutionFilter.setExecutionStatus(new Status[] {Status.FAILED}); - - ScenarioExecutionFilter queryFilter = queryFilterAdapterFactory.getQueryAdapter(scenarioExecutionFilter); - - List result = scenarioExecutionRepository.find(queryFilter); - assertEquals(1, result.size()); - assertEquals(failedScenarioExecutionId, result.get(0).getExecutionId()); - } - - @Test - void testFindByHeader() { - String inUid = UUID.randomUUID().toString(); - String outUid = UUID.randomUUID().toString(); - createTestScenarioExecution(inUid, PAYLOAD, outUid, PAYLOAD); - - ScenarioExecutionFilter scenarioExecutionFilter = new ScenarioExecutionFilter(); - scenarioExecutionFilter.setHeaderFilter(IN_HEADER_NAME1 + ":" + inUid); - - - ScenarioExecutionFilter queryFilter = queryFilterAdapterFactory.getQueryAdapter(scenarioExecutionFilter); - - assertEquals(1, scenarioExecutionRepository.find(queryFilter).size()); - - scenarioExecutionFilter.setHeaderFilter(IN_HEADER_NAME1 + ":" + inUid + "mod"); - - assertEquals(0, scenarioExecutionRepository.find(queryFilter).size()); - } - - @Test - void testFindByHeaderMulti() { - String inUid = UUID.randomUUID().toString(); - String outUid = UUID.randomUUID().toString(); - createTestScenarioExecution(inUid, PAYLOAD, outUid, PAYLOAD); - - ScenarioExecutionFilter scenarioExecutionFilter = new ScenarioExecutionFilter(); - scenarioExecutionFilter.setHeaderFilter(IN_HEADER_NAME1 + ":" + inUid + ";" + IN_HEADER_NAME2 + ":" + inUid + "_2"); - - ScenarioExecutionFilter queryFilter = queryFilterAdapterFactory.getQueryAdapter(scenarioExecutionFilter); - - assertEquals(1, scenarioExecutionRepository.find(queryFilter).size()); - - scenarioExecutionFilter.setHeaderFilter(IN_HEADER_NAME1 + ":" + inUid + ";" + IN_HEADER_NAME2 + ":" + inUid + "_3"); - - assertEquals(0, scenarioExecutionRepository.find(queryFilter).size()); - } - - @Test - void testFindByPayload() { - String inUid = UUID.randomUUID().toString(); - String outUid = UUID.randomUUID().toString(); - String inPayload = PAYLOAD + inUid + "-in"; - String outPayload = PAYLOAD + outUid + "-out"; - - createTestScenarioExecution(inUid, inPayload, outUid, outPayload); - - ScenarioExecutionFilter scenarioExecutionFilter = new ScenarioExecutionFilter(); - - ScenarioExecutionFilter queryFilter = queryFilterAdapterFactory.getQueryAdapter(scenarioExecutionFilter); - - scenarioExecutionFilter.setContainingText(inPayload); - assertEquals(1, scenarioExecutionRepository.find(queryFilter).size()); - - scenarioExecutionFilter.setContainingText(inPayload + "mod"); - assertEquals(0, scenarioExecutionRepository.find(queryFilter).size()); - - scenarioExecutionFilter.setDirectionInbound(false); - scenarioExecutionFilter.setContainingText(inPayload); - assertEquals(0, scenarioExecutionRepository.find(queryFilter).size()); - - scenarioExecutionFilter.setDirectionInbound(true); - scenarioExecutionFilter.setContainingText(inPayload); - assertEquals(1, scenarioExecutionRepository.find(queryFilter).size()); - - scenarioExecutionFilter.setDirectionOutbound(true); - scenarioExecutionFilter.setContainingText(outPayload); - assertEquals(1, scenarioExecutionRepository.find(queryFilter).size()); - - scenarioExecutionFilter.setDirectionOutbound(false); - scenarioExecutionFilter.setContainingText(outPayload); - assertEquals(0, scenarioExecutionRepository.find(queryFilter).size()); - } - - @Test - void testPaging() { - String uniquePayload = "PagingPayload" + UUID.randomUUID().toString(); - for (int i = 0; i < 100; i++) { - createTestScenarioExecution(UUID.randomUUID().toString(), uniquePayload + "-in", UUID.randomUUID().toString(), uniquePayload + "-out"); - } - - ScenarioExecutionFilter scenarioExecutionFilter = new ScenarioExecutionFilter(); - scenarioExecutionFilter.setPageNumber(0); - scenarioExecutionFilter.setPageSize(33); - - ScenarioExecutionFilter queryFilter = queryFilterAdapterFactory.getQueryAdapter(scenarioExecutionFilter); - List result = scenarioExecutionRepository.find(queryFilter); - - assertEquals(result.size(), 33); - } - - @Test - void testFindByDate() { - String uniquePayload = "FindByDatePayload" + UUID.randomUUID(); - - Instant t1 = Instant.now(); - - int batch1Size = 100; - for (int i = 0; i < batch1Size; i++) { - createTestScenarioExecution(UUID.randomUUID().toString(), uniquePayload + "-in", UUID.randomUUID().toString(), uniquePayload + "-out"); - } - - entityManager.flush(); - - Instant t2 = Instant.now(); - - int batch2Size = 50; - for (int i = 0; i < batch2Size; i++) { - createTestScenarioExecution(UUID.randomUUID().toString(), uniquePayload + "-in", UUID.randomUUID().toString(), uniquePayload + "-out"); - } - - entityManager.flush(); - - Instant t3 = Instant.now(); - - ScenarioExecutionFilter scenarioExecutionFilter = new ScenarioExecutionFilter(); - scenarioExecutionFilter.setPageNumber(0); - scenarioExecutionFilter.setPageSize(1000); - - ScenarioExecutionFilter queryFilter = queryFilterAdapterFactory.getQueryAdapter(scenarioExecutionFilter); - - scenarioExecutionFilter.setFromDate(t1); - scenarioExecutionFilter.setToDate(t2); - assertEquals(batch1Size, scenarioExecutionRepository.find(queryFilter).size()); - - scenarioExecutionFilter.setFromDate(t1); - scenarioExecutionFilter.setToDate(t3); - assertEquals(batch1Size + batch2Size, scenarioExecutionRepository.find(queryFilter).size()); - - scenarioExecutionFilter.setFromDate(t2); - scenarioExecutionFilter.setToDate(t3); - assertEquals(batch2Size, scenarioExecutionRepository.find(queryFilter).size()); - } - - private Long createTestScenarioExecution(String inUid, String inPayload, String outUid, String outPayload) { - return createTestScenarioExecution(TEST_SCENARIO, inUid, inPayload, outUid, outPayload, Status.SUCCESS); - } - - private Long createTestScenarioExecution(String scenarioName, String inUid, String inPayload, String outUid, String outPayload, Status status) { - Map inHeaders = new HashMap<>(); - inHeaders.put(IN_HEADER_NAME1, inUid); - inHeaders.put(IN_HEADER_NAME2, inUid + "_2"); - - ScenarioExecution scenarioExecution = activityService.createExecutionScenario(scenarioName, Collections.emptySet()); - activityService.saveScenarioMessage(scenarioExecution.getExecutionId(), Direction.INBOUND, inPayload, inUid, inHeaders); - - Map outHeaders = new HashMap<>(); - outHeaders.put(OUT_HEADER_NAME1, outUid); - outHeaders.put(OUT_HEADER_NAME2, outUid + "_2"); - activityService.saveScenarioMessage(scenarioExecution.getExecutionId(), Direction.OUTBOUND, outPayload, outUid, outHeaders); - - DefaultTestCase testCase = new DefaultTestCase(); - testCase.getVariableDefinitions().put(ScenarioExecution.EXECUTION_ID, scenarioExecution.getExecutionId()); - - if (Status.SUCCESS == status) { - activityService.completeScenarioExecutionSuccess(testCase); - } else if (Status.FAILED == status) { - activityService.completeScenarioExecutionFailure(testCase, null); - } - - return scenarioExecution.getExecutionId(); - } -} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/criteria/TestParameterCriteriaTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/criteria/TestParameterCriteriaTest.java new file mode 100644 index 000000000..658f63033 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/criteria/TestParameterCriteriaTest.java @@ -0,0 +1,92 @@ +package org.citrusframework.simulator.service.criteria; + +import org.citrusframework.simulator.service.filter.InstantFilter; +import org.citrusframework.simulator.service.filter.LongFilter; +import org.citrusframework.simulator.service.filter.StringFilter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class TestParameterCriteriaTest { + + private TestParameterCriteria criteria; + + @BeforeEach + void beforeEachSetup() { + criteria = new TestParameterCriteria(); + } + + @Test + void testKey() { + assertNull(criteria.getKey()); + + StringFilter keyFilter = criteria.key(); + assertNotNull(keyFilter); + assertSame(keyFilter, criteria.getKey()); + + StringFilter mockKeyFilter = mock(StringFilter.class); + criteria.setKey(mockKeyFilter); + assertSame(mockKeyFilter, criteria.key()); + } + + @Test + void testValue() { + assertNull(criteria.getValue()); + + StringFilter valueFilter = criteria.value(); + assertNotNull(valueFilter); + assertSame(valueFilter, criteria.getValue()); + + StringFilter mockValueFilter = mock(StringFilter.class); + criteria.setValue(mockValueFilter); + assertSame(mockValueFilter, criteria.value()); + } + + @Test + void testCreatedDate() { + assertNull(criteria.getCreatedDate()); + + InstantFilter createdDateFilter = criteria.createdDate(); + assertNotNull(createdDateFilter); + assertSame(createdDateFilter, criteria.getCreatedDate()); + + InstantFilter mockCreatedDateFilter = mock(InstantFilter.class); + criteria.setCreatedDate(mockCreatedDateFilter); + assertSame(mockCreatedDateFilter, criteria.createdDate()); + } + + @Test + void testLastModifiedDate() { + assertNull(criteria.getLastModifiedDate()); + + InstantFilter lastModifiedDateFilter = criteria.lastModifiedDate(); + assertNotNull(lastModifiedDateFilter); + assertSame(lastModifiedDateFilter, criteria.getLastModifiedDate()); + + InstantFilter mockLastModifiedDateFilter = mock(InstantFilter.class); + criteria.setLastModifiedDate(mockLastModifiedDateFilter); + assertSame(mockLastModifiedDateFilter, criteria.lastModifiedDate()); + } + + @Test + void testTestResultId() { + assertNull(criteria.getTestResultId()); + + LongFilter testResultIdFilter = criteria.testResultId(); + assertNotNull(testResultIdFilter); + assertSame(testResultIdFilter, criteria.getTestResultId()); + + LongFilter mockTestResultIdFilter = mock(LongFilter.class); + criteria.setTestResultId(mockTestResultIdFilter); + assertSame(mockTestResultIdFilter, criteria.testResultId()); + } + + @Test + void testCopy() { + TestParameterCriteria copiedCriteria = criteria.copy(); + assertNotSame(copiedCriteria, criteria); + assertEquals(copiedCriteria, criteria); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/criteria/TestResultCriteriaTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/criteria/TestResultCriteriaTest.java new file mode 100644 index 000000000..0c9d76982 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/criteria/TestResultCriteriaTest.java @@ -0,0 +1,162 @@ +package org.citrusframework.simulator.service.criteria; + +import org.citrusframework.simulator.service.filter.InstantFilter; +import org.citrusframework.simulator.service.filter.IntegerFilter; +import org.citrusframework.simulator.service.filter.LongFilter; +import org.citrusframework.simulator.service.filter.StringFilter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; + +class TestResultCriteriaTest { + + private TestResultCriteria criteria; + + @BeforeEach + void beforeEachSetup() { + criteria = new TestResultCriteria(); + } + + @Test + void testId() { + assertNull(criteria.getId()); + + LongFilter idFilter = criteria.id(); + assertNotNull(idFilter); + assertSame(idFilter, criteria.getId()); + + LongFilter mockIdFilter = mock(LongFilter.class); + criteria.setId(mockIdFilter); + assertSame(mockIdFilter, criteria.id()); + } + + @Test + void testStatus() { + assertNull(criteria.getStatus()); + + IntegerFilter statusFilter = criteria.status(); + assertNotNull(statusFilter); + assertSame(statusFilter, criteria.getStatus()); + + IntegerFilter mockStatusFilter = mock(IntegerFilter.class); + criteria.setStatus(mockStatusFilter); + assertSame(mockStatusFilter, criteria.status()); + } + + @Test + void testTestName() { + assertNull(criteria.getTestName()); + + StringFilter testNameFilter = criteria.testName(); + assertNotNull(testNameFilter); + assertSame(testNameFilter, criteria.getTestName()); + + StringFilter mockTestNameFilter = mock(StringFilter.class); + criteria.setTestName(mockTestNameFilter); + assertSame(mockTestNameFilter, criteria.testName()); + } + + @Test + void testClassName() { + assertNull(criteria.getClassName()); + + StringFilter classNameFilter = criteria.className(); + assertNotNull(classNameFilter); + assertSame(classNameFilter, criteria.getClassName()); + + StringFilter mockClassNameFilter = mock(StringFilter.class); + criteria.setClassName(mockClassNameFilter); + assertSame(mockClassNameFilter, criteria.className()); + } + + @Test + void testErrorMessage() { + assertNull(criteria.getErrorMessage()); + + StringFilter errorMessageFilter = criteria.errorMessage(); + assertNotNull(errorMessageFilter); + assertSame(errorMessageFilter, criteria.getErrorMessage()); + + StringFilter mockErrorMessageFilter = mock(StringFilter.class); + criteria.setErrorMessage(mockErrorMessageFilter); + assertSame(mockErrorMessageFilter, criteria.errorMessage()); + } + + @Test + void testFailureStack() { + assertNull(criteria.getFailureStack()); + + StringFilter failureStackFilter = criteria.failureStack(); + assertNotNull(failureStackFilter); + assertSame(failureStackFilter, criteria.getFailureStack()); + + StringFilter mockFailureStackFilter = mock(StringFilter.class); + criteria.setFailureStack(mockFailureStackFilter); + assertSame(mockFailureStackFilter, criteria.failureStack()); + } + + @Test + void testFailureType() { + assertNull(criteria.getFailureType()); + + StringFilter failureTypeFilter = criteria.failureType(); + assertNotNull(failureTypeFilter); + assertSame(failureTypeFilter, criteria.getFailureType()); + + StringFilter mockFailureTypeFilter = mock(StringFilter.class); + criteria.setFailureType(mockFailureTypeFilter); + assertSame(mockFailureTypeFilter, criteria.failureType()); + } + + @Test + void testCreatedDate() { + assertNull(criteria.getCreatedDate()); + + InstantFilter createdDateFilter = criteria.createdDate(); + assertNotNull(createdDateFilter); + assertSame(createdDateFilter, criteria.getCreatedDate()); + + InstantFilter mockCreatedDateFilter = mock(InstantFilter.class); + criteria.setCreatedDate(mockCreatedDateFilter); + assertSame(mockCreatedDateFilter, criteria.createdDate()); + } + + @Test + void testLastModifiedDate() { + assertNull(criteria.getLastModifiedDate()); + + InstantFilter lastModifiedDateFilter = criteria.lastModifiedDate(); + assertNotNull(lastModifiedDateFilter); + assertSame(lastModifiedDateFilter, criteria.getLastModifiedDate()); + + InstantFilter mockLastModifiedDateFilter = mock(InstantFilter.class); + criteria.setLastModifiedDate(mockLastModifiedDateFilter); + assertSame(mockLastModifiedDateFilter, criteria.lastModifiedDate()); + } + + @Test + void testTestParameterKey() { + assertNull(criteria.getTestParameterKey()); + + StringFilter testParameterKeyFilter = criteria.testParameterId(); + assertNotNull(testParameterKeyFilter); + assertSame(testParameterKeyFilter, criteria.getTestParameterKey()); + + StringFilter mockTestParameterKeyFilter = mock(StringFilter.class); + criteria.setTestParameterKey(mockTestParameterKeyFilter); + assertSame(mockTestParameterKeyFilter, criteria.testParameterId()); + } + + @Test + void testCopy() { + TestResultCriteria copiedCriteria = criteria.copy(); + assertNotSame(copiedCriteria, criteria); + assertEquals(copiedCriteria, criteria); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/dto/TestResultByStatusTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/dto/TestResultByStatusTest.java new file mode 100644 index 000000000..df889a6ef --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/dto/TestResultByStatusTest.java @@ -0,0 +1,37 @@ +package org.citrusframework.simulator.service.dto; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TestResultByStatusTest { + + static Stream constructorCalculatesTotal() { + return Stream.of( + Arguments.of(0, 0, 0), + Arguments.of(0, 1, 1), + Arguments.of(1, 0, 1), + Arguments.of(1, 2, 3), + Arguments.of(2, 3, 5) + ); + } + + @MethodSource + @ParameterizedTest + void constructorCalculatesTotal(int succeeded, int failed, int total) { + assertEquals(total, new TestResultByStatus((long) succeeded, (long) failed).total()); + } + + @Test + void constructorIsNullResistant() { + TestResultByStatus testResultByStatus = new TestResultByStatus(null, null); + assertEquals(0, testResultByStatus.total()); + assertEquals(0, testResultByStatus.successful()); + assertEquals(0, testResultByStatus.failed()); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/FilterTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/FilterTest.java new file mode 100644 index 000000000..766ff2bc7 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/FilterTest.java @@ -0,0 +1,96 @@ +package org.citrusframework.simulator.service.filter; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class FilterTest { + + @Test + void testConstructor() { + Filter filter = new Filter<>(); + assertNull(filter.getEquals()); + assertNull(filter.getNotEquals()); + assertNull(filter.getSpecified()); + assertNull(filter.getIn()); + assertNull(filter.getNotIn()); + } + + @Test + void testCopyConstructor() { + Filter original = new Filter<>(); + original.setEquals("testEquals"); + original.setNotEquals("testNotEquals"); + original.setSpecified(true); + original.setIn(Arrays.asList("test1", "test2")); + original.setNotIn(Arrays.asList("test3", "test4")); + + Filter copy = new Filter<>(original); + + assertEquals(original, copy); + assertNotSame(original.getIn(), copy.getIn()); + assertNotSame(original.getNotIn(), copy.getNotIn()); + } + + @Test + void testGettersAndSetters() { + Filter filter = new Filter<>(); + String testValue = "testValue"; + List testList = Arrays.asList("test1", "test2"); + + filter.setEquals(testValue); + assertEquals(testValue, filter.getEquals()); + + filter.setNotEquals(testValue); + assertEquals(testValue, filter.getNotEquals()); + + filter.setSpecified(true); + assertTrue(filter.getSpecified()); + + filter.setIn(testList); + assertEquals(testList, filter.getIn()); + + filter.setNotIn(testList); + assertEquals(testList, filter.getNotIn()); + } + + @Test + void testCopy() { + Filter original = new Filter<>(); + original.setEquals("testEquals"); + Filter copy = original.copy(); + + assertEquals(original, copy); + assertNotSame(original, copy); + } + + @Test + void testEqualsAndHashCode() { + Filter filter1 = new Filter<>(); + Filter filter2 = new Filter<>(); + + assertEquals(filter1, filter2); + assertEquals(filter1.hashCode(), filter2.hashCode()); + + filter1.setEquals("test"); + assertNotEquals(filter1, filter2); + + filter2.setEquals("test"); + assertEquals(filter1, filter2); + assertEquals(filter1.hashCode(), filter2.hashCode()); + } + + @Test + void testToString() { + Filter filter = new Filter<>(); + filter.setEquals("testEquals"); + assertTrue(filter.toString().contains("testEquals")); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/InstantFilterTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/InstantFilterTest.java new file mode 100644 index 000000000..263a22c91 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/InstantFilterTest.java @@ -0,0 +1,75 @@ +package org.citrusframework.simulator.service.filter; + +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class InstantFilterTest { + + @Test + void testConstructor() { + InstantFilter filter = new InstantFilter(); + assertNull(filter.getEquals()); + assertNull(filter.getNotEquals()); + assertNull(filter.getGreaterThan()); + assertNull(filter.getLessThan()); + assertNull(filter.getGreaterThanOrEqual()); + assertNull(filter.getLessThanOrEqual()); + assertNull(filter.getIn()); + assertNull(filter.getNotIn()); + } + + @Test + void testCopyConstructor() { + InstantFilter original = new InstantFilter(); + Instant now = Instant.now(); + original.setEquals(now); + original.setNotEquals(now.plusSeconds(10)); + original.setGreaterThan(now.plusSeconds(20)); + original.setLessThan(now.plusSeconds(30)); + original.setGreaterThanOrEqual(now.plusSeconds(40)); + original.setLessThanOrEqual(now.plusSeconds(50)); + original.setIn(Arrays.asList(now, now.plusSeconds(60))); + original.setNotIn(Arrays.asList(now.plusSeconds(70), now.plusSeconds(80))); + + InstantFilter copy = new InstantFilter(original); + + assertEquals(original, copy); + assertNotSame(original.getIn(), copy.getIn()); + assertNotSame(original.getNotIn(), copy.getNotIn()); + } + + @Test + void testCopy() { + InstantFilter original = new InstantFilter(); + Instant now = Instant.now(); + original.setEquals(now); + InstantFilter copy = original.copy(); + + assertEquals(original, copy); + assertNotSame(original, copy); + } + + @Test + void testSetters() { + InstantFilter filter = new InstantFilter(); + Instant now = Instant.now(); + List testList = Arrays.asList(now, now.plusSeconds(10)); + + assertTrue(filter.setEquals(now) instanceof InstantFilter); + assertTrue(filter.setNotEquals(now) instanceof InstantFilter); + assertTrue(filter.setGreaterThan(now) instanceof InstantFilter); + assertTrue(filter.setLessThan(now) instanceof InstantFilter); + assertTrue(filter.setGreaterThanOrEqual(now) instanceof InstantFilter); + assertTrue(filter.setLessThanOrEqual(now) instanceof InstantFilter); + assertTrue(filter.setIn(testList) instanceof InstantFilter); + assertTrue(filter.setNotIn(testList) instanceof InstantFilter); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/IntegerFilterTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/IntegerFilterTest.java new file mode 100644 index 000000000..b1c7e0483 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/IntegerFilterTest.java @@ -0,0 +1,54 @@ +package org.citrusframework.simulator.service.filter; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; + +class IntegerFilterTest { + + @Test + void testConstructor() { + IntegerFilter filter = new IntegerFilter(); + assertNull(filter.getEquals()); + assertNull(filter.getNotEquals()); + assertNull(filter.getGreaterThan()); + assertNull(filter.getLessThan()); + assertNull(filter.getGreaterThanOrEqual()); + assertNull(filter.getLessThanOrEqual()); + assertNull(filter.getIn()); + assertNull(filter.getNotIn()); + } + + @Test + void testCopyConstructor() { + IntegerFilter original = new IntegerFilter(); + original.setEquals(1); + original.setNotEquals(2); + original.setGreaterThan(3); + original.setLessThan(4); + original.setGreaterThanOrEqual(5); + original.setLessThanOrEqual(6); + original.setIn(Arrays.asList(7, 8)); + original.setNotIn(Arrays.asList(9, 10)); + + IntegerFilter copy = new IntegerFilter(original); + + assertEquals(original, copy); + assertNotSame(original.getIn(), copy.getIn()); + assertNotSame(original.getNotIn(), copy.getNotIn()); + } + + @Test + void testCopy() { + IntegerFilter original = new IntegerFilter(); + original.setEquals(1); + IntegerFilter copy = original.copy(); + + assertEquals(original, copy); + assertNotSame(original, copy); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/LongFilterTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/LongFilterTest.java new file mode 100644 index 000000000..bfd294c7d --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/LongFilterTest.java @@ -0,0 +1,54 @@ +package org.citrusframework.simulator.service.filter; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; + +class LongFilterTest { + + @Test + void testConstructor() { + LongFilter filter = new LongFilter(); + assertNull(filter.getEquals()); + assertNull(filter.getNotEquals()); + assertNull(filter.getGreaterThan()); + assertNull(filter.getLessThan()); + assertNull(filter.getGreaterThanOrEqual()); + assertNull(filter.getLessThanOrEqual()); + assertNull(filter.getIn()); + assertNull(filter.getNotIn()); + } + + @Test + void testCopyConstructor() { + LongFilter original = new LongFilter(); + original.setEquals(1L); + original.setNotEquals(2L); + original.setGreaterThan(3L); + original.setLessThan(4L); + original.setGreaterThanOrEqual(5L); + original.setLessThanOrEqual(6L); + original.setIn(Arrays.asList(7L, 8L)); + original.setNotIn(Arrays.asList(9L, 10L)); + + LongFilter copy = new LongFilter(original); + + assertEquals(original, copy); + assertNotSame(original.getIn(), copy.getIn()); + assertNotSame(original.getNotIn(), copy.getNotIn()); + } + + @Test + void testCopy() { + LongFilter original = new LongFilter(); + original.setEquals(1L); + LongFilter copy = original.copy(); + + assertEquals(original, copy); + assertNotSame(original, copy); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/RangeFilterTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/RangeFilterTest.java new file mode 100644 index 000000000..9433d43e9 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/RangeFilterTest.java @@ -0,0 +1,111 @@ +package org.citrusframework.simulator.service.filter; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class RangeFilterTest { + + @Test + void testConstructor() { + RangeFilter filter = new RangeFilter<>(); + assertNull(filter.getEquals()); + assertNull(filter.getNotEquals()); + assertNull(filter.getGreaterThan()); + assertNull(filter.getLessThan()); + assertNull(filter.getGreaterThanOrEqual()); + assertNull(filter.getLessThanOrEqual()); + assertNull(filter.getIn()); + assertNull(filter.getNotIn()); + } + + @Test + void testCopyConstructor() { + RangeFilter original = new RangeFilter<>(); + original.setEquals("testEquals"); + original.setNotEquals("testNotEquals"); + original.setGreaterThan("testGreaterThan"); + original.setLessThan("testLessThan"); + original.setGreaterThanOrEqual("testGreaterThanOrEqual"); + original.setLessThanOrEqual("testLessThanOrEqual"); + original.setIn(Arrays.asList("test1", "test2")); + original.setNotIn(Arrays.asList("test3", "test4")); + + RangeFilter copy = new RangeFilter<>(original); + + assertEquals(original, copy); + assertNotSame(original.getIn(), copy.getIn()); + assertNotSame(original.getNotIn(), copy.getNotIn()); + } + + @Test + void testGettersAndSetters() { + RangeFilter filter = new RangeFilter<>(); + String testValue = "testValue"; + List testList = Arrays.asList("test1", "test2"); + + filter.setEquals(testValue); + assertEquals(testValue, filter.getEquals()); + + filter.setNotEquals(testValue); + assertEquals(testValue, filter.getNotEquals()); + + filter.setGreaterThan(testValue); + assertEquals(testValue, filter.getGreaterThan()); + + filter.setLessThan(testValue); + assertEquals(testValue, filter.getLessThan()); + + filter.setGreaterThanOrEqual(testValue); + assertEquals(testValue, filter.getGreaterThanOrEqual()); + + filter.setLessThanOrEqual(testValue); + assertEquals(testValue, filter.getLessThanOrEqual()); + + filter.setIn(testList); + assertEquals(testList, filter.getIn()); + + filter.setNotIn(testList); + assertEquals(testList, filter.getNotIn()); + } + + @Test + void testEqualsAndHashCode() { + RangeFilter filter1 = new RangeFilter<>(); + RangeFilter filter2 = new RangeFilter<>(); + + assertEquals(filter1, filter2); + assertEquals(filter1.hashCode(), filter2.hashCode()); + + filter1.setEquals("test"); + assertNotEquals(filter1, filter2); + + filter2.setEquals("test"); + assertEquals(filter1, filter2); + assertEquals(filter1.hashCode(), filter2.hashCode()); + } + + @Test + void testToString() { + RangeFilter filter = new RangeFilter<>(); + filter.setEquals("testEquals"); + assertTrue(filter.toString().contains("testEquals")); + } + + @Test + void testCopy() { + RangeFilter original = new RangeFilter<>(); + original.setEquals("testEquals"); + RangeFilter copy = original.copy(); + + assertEquals(original, copy); + assertNotSame(original, copy); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/StringFilterTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/StringFilterTest.java new file mode 100644 index 000000000..b06a08926 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/filter/StringFilterTest.java @@ -0,0 +1,101 @@ +package org.citrusframework.simulator.service.filter; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class StringFilterTest { + + @Test + void testConstructor() { + StringFilter filter = new StringFilter(); + assertNull(filter.getEquals()); + assertNull(filter.getNotEquals()); + assertNull(filter.getIn()); + assertNull(filter.getNotIn()); + assertNull(filter.getContains()); + assertNull(filter.getDoesNotContain()); + } + + @Test + void testCopyConstructor() { + StringFilter original = new StringFilter(); + original.setEquals("testEquals"); + original.setNotEquals("testNotEquals"); + original.setIn(Arrays.asList("test1", "test2")); + original.setNotIn(Arrays.asList("test3", "test4")); + original.setContains("testContains"); + original.setDoesNotContain("testDoesNotContain"); + + StringFilter copy = new StringFilter(original); + + assertEquals(original, copy); + assertNotSame(original.getIn(), copy.getIn()); + assertNotSame(original.getNotIn(), copy.getNotIn()); + } + + @Test + void testGettersAndSetters() { + StringFilter filter = new StringFilter(); + String testValue = "testValue"; + List testList = Arrays.asList("test1", "test2"); + + filter.setEquals(testValue); + assertEquals(testValue, filter.getEquals()); + + filter.setNotEquals(testValue); + assertEquals(testValue, filter.getNotEquals()); + + filter.setIn(testList); + assertEquals(testList, filter.getIn()); + + filter.setNotIn(testList); + assertEquals(testList, filter.getNotIn()); + + filter.setContains(testValue); + assertEquals(testValue, filter.getContains()); + + filter.setDoesNotContain(testValue); + assertEquals(testValue, filter.getDoesNotContain()); + } + + @Test + void testCopy() { + StringFilter original = new StringFilter(); + original.setEquals("testEquals"); + StringFilter copy = original.copy(); + + assertEquals(original, copy); + assertNotSame(original, copy); + } + + @Test + void testEqualsAndHashCode() { + StringFilter filter1 = new StringFilter(); + StringFilter filter2 = new StringFilter(); + + assertEquals(filter1, filter2); + assertEquals(filter1.hashCode(), filter2.hashCode()); + + filter1.setEquals("test"); + assertNotEquals(filter1, filter2); + + filter2.setEquals("test"); + assertEquals(filter1, filter2); + assertEquals(filter1.hashCode(), filter2.hashCode()); + } + + @Test + void testToString() { + StringFilter filter = new StringFilter(); + filter.setEquals("testEquals"); + assertTrue(filter.toString().contains("testEquals")); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/impl/TestParameterServiceImplTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/impl/TestParameterServiceImplTest.java new file mode 100644 index 000000000..f375205f8 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/impl/TestParameterServiceImplTest.java @@ -0,0 +1,102 @@ +package org.citrusframework.simulator.service.impl; + +import org.citrusframework.simulator.model.TestParameter; +import org.citrusframework.simulator.model.TestResult; +import org.citrusframework.simulator.repository.TestParameterRepository; +import org.citrusframework.simulator.service.TestResultService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +@ExtendWith(MockitoExtension.class) +class TestParameterServiceImplTest { + + @Mock + private TestResultService testResultServiceMock; + + @Mock + private TestParameterRepository testParameterRepositoryMock; + + private TestParameterServiceImpl fixture; + + @BeforeEach + void beforeEachSetup() { + fixture = new TestParameterServiceImpl(testResultServiceMock, testParameterRepositoryMock); + } + + @Test + void testSave() { + TestParameter testParameter = new TestParameter(); + doReturn(testParameter).when(testParameterRepositoryMock).save(testParameter); + + TestParameter result = fixture.save(testParameter); + + assertEquals(testParameter, result); + } + + @Test + void testFindAll() { + Pageable pageable = mock(Pageable.class); + Page page = mock(Page.class); + doReturn(page).when(testParameterRepositoryMock).findAll(pageable); + + Page result = fixture.findAll(pageable); + + assertEquals(page, result); + } + + @Test + void testFindOneWithExistingTestResult() { + Long testResultId = 1L; + String key = "key"; + + TestResult testResult = new TestResult().id(testResultId); + Optional optionalTestResult = Optional.of(testResult); + doReturn(optionalTestResult).when(testResultServiceMock).findOne(testResultId); + + TestParameter testParameter = new TestParameter(); + doReturn(Optional.of(testParameter)).when(testParameterRepositoryMock).findById(any(TestParameter.TestParameterId.class)); + + Optional maybeTestParameter = fixture.findOne(testResultId, key); + + assertTrue(maybeTestParameter.isPresent()); + assertEquals(testParameter, maybeTestParameter.get()); + + ArgumentCaptor testParameterIdArgumentCaptor = ArgumentCaptor.forClass(TestParameter.TestParameterId.class); + verify(testParameterRepositoryMock).findById(testParameterIdArgumentCaptor.capture()); + + TestParameter.TestParameterId testParameterId = testParameterIdArgumentCaptor.getValue(); + assertEquals(testResultId, testParameterId.testResultId); + assertEquals(key, testParameterId.key); + } + + @Test + void testFindOneWithoutTestResult() { + Long testResultId = 1L; + String key = "key"; + + doReturn(Optional.empty()).when(testResultServiceMock).findOne(testResultId); + + Optional maybeTestParameter = fixture.findOne(testResultId, key); + + assertFalse(maybeTestParameter.isPresent()); + + verifyNoInteractions(testParameterRepositoryMock); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/service/impl/TestResultServiceImplTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/service/impl/TestResultServiceImplTest.java new file mode 100644 index 000000000..3fe38c220 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/service/impl/TestResultServiceImplTest.java @@ -0,0 +1,90 @@ +package org.citrusframework.simulator.service.impl; + +import org.citrusframework.simulator.model.TestResult; +import org.citrusframework.simulator.repository.TestResultRepository; +import org.citrusframework.simulator.service.dto.TestResultByStatus; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +class TestResultServiceImplTest { + + @Mock + private TestResultRepository testResultRepository; + + private TestResultServiceImpl fixture; + + @BeforeEach + void beforeEachSetup() { + fixture = new TestResultServiceImpl(testResultRepository); + } + + @Test + void testTransformAndSave() { + org.citrusframework.TestResult citrusTestResult = org.citrusframework.TestResult.success("TestResult", TestResultServiceImpl.class.getSimpleName()); + + TestResult testResult = new TestResult(citrusTestResult); + doReturn(testResult).when(testResultRepository).save(any(TestResult.class)); + + TestResult result = fixture.transformAndSave(citrusTestResult); + + assertEquals(testResult, result); + } + + @Test + void testSave() { + TestResult testResult = new TestResult(); + doReturn(testResult).when(testResultRepository).save(testResult); + + TestResult result = fixture.save(testResult); + + assertEquals(testResult, result); + } + + @Test + void testFindAll() { + Pageable pageable = mock(Pageable.class); + Page page = mock(Page.class); + doReturn(page).when(testResultRepository).findAll(pageable); + + Page result = fixture.findAll(pageable); + + assertEquals(page, result); + } + + @Test + void testFindOne() { + Long id = 1L; + + TestResult testResult = new TestResult(); + Optional optionalTestResult = Optional.of(testResult); + doReturn(optionalTestResult).when(testResultRepository).findById(id); + + Optional maybeTestResult = fixture.findOne(id); + + assertTrue(maybeTestResult.isPresent()); + assertEquals(testResult, maybeTestResult.get()); + } + + @Test + void testCountByStatus() { + TestResultByStatus testResultByStatus = new TestResultByStatus(1L, 1L); + doReturn(testResultByStatus).when(testResultRepository).countByStatus(); + + TestResultByStatus result = fixture.countByStatus(); + assertEquals(testResultByStatus, result); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/test/TestApplication.java b/simulator-starter/src/test/java/org/citrusframework/simulator/test/TestApplication.java new file mode 100644 index 000000000..a39ca08b0 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/test/TestApplication.java @@ -0,0 +1,17 @@ +package org.citrusframework.simulator.test; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Note that this class may not be placed into the root package {@code org.citrusframework.simulator}. If done so, the + * {@link SpringBootApplication} annotation would scan the classpath given the package and that would defy the sense + * of all {@link org.springframework.boot.autoconfigure.AutoConfiguration}. + */ +@SpringBootApplication +public class TestApplication { + + public static void main(String[] args) { + SpringApplication.run(TestApplication.class, args); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/web/rest/TestParameterResourceIT.java b/simulator-starter/src/test/java/org/citrusframework/simulator/web/rest/TestParameterResourceIT.java new file mode 100644 index 000000000..49785389e --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/web/rest/TestParameterResourceIT.java @@ -0,0 +1,510 @@ +package org.citrusframework.simulator.web.rest; + +import jakarta.persistence.EntityManager; +import org.citrusframework.simulator.IntegrationTest; +import org.citrusframework.simulator.model.TestParameter; +import org.citrusframework.simulator.model.TestResult; +import org.citrusframework.simulator.repository.TestParameterRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +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.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import static org.citrusframework.simulator.web.rest.TestUtil.findAll; +import static org.citrusframework.simulator.web.rest.TestUtil.sameInstant; +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Integration tests for the {@link TestParameterResource} REST controller. + */ +@IntegrationTest +@AutoConfigureMockMvc +class TestParameterResourceIT { + + private static final String DEFAULT_KEY = "AAAAAAAAAA"; + private static final String UPDATED_KEY = "BBBBBBBBBB"; + + private static final String DEFAULT_VALUE = "AAAAAAAAAA"; + private static final String UPDATED_VALUE = "BBBBBBBBBB"; + + private static final ZonedDateTime DEFAULT_CREATED_DATE = ZonedDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneOffset.UTC); + private static final ZonedDateTime UPDATED_CREATED_DATE = ZonedDateTime.now(ZoneId.systemDefault()).withNano(0); + private static final ZonedDateTime SMALLER_CREATED_DATE = ZonedDateTime.ofInstant(Instant.ofEpochMilli(-1L), ZoneOffset.UTC); + + private static final ZonedDateTime DEFAULT_LAST_MODIFIED_DATE = ZonedDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneOffset.UTC); + private static final ZonedDateTime UPDATED_LAST_MODIFIED_DATE = ZonedDateTime.now(ZoneId.systemDefault()).withNano(0); + private static final ZonedDateTime SMALLER_LAST_MODIFIED_DATE = ZonedDateTime.ofInstant(Instant.ofEpochMilli(-1L), ZoneOffset.UTC); + + private static final String ENTITY_API_URL = "/api/test-parameters"; + private static final String ENTITY_API_URL_ID = ENTITY_API_URL + "/{testResultId}/{key}"; + + @Autowired + private TestParameterRepository testParameterRepository; + + @Autowired + private EntityManager em; + + @Autowired + private MockMvc restTestParameterMockMvc; + + private TestParameter testParameter; + + /** + * Create an entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static TestParameter createEntity(EntityManager em) { + TestParameter testParameter = TestParameter.builder() + .key(DEFAULT_KEY) + .value(DEFAULT_VALUE) + .createdDate(DEFAULT_CREATED_DATE) + .lastModifiedDate(DEFAULT_LAST_MODIFIED_DATE) + .build(); + + TestResult testResult; + if (findAll(em, TestResult.class).isEmpty()) { + testResult = TestResultResourceIT.createEntity(em); + em.persist(testResult); + em.flush(); + } else { + testResult = findAll(em, TestResult.class).get(0); + } + testParameter.setTestResult(testResult); + + return testParameter; + } + + @BeforeEach + public void initTest() { + testParameter = createEntity(em); + } + + @Test + @Transactional + void getAllTestParameters() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList + restTestParameterMockMvc + .perform(get(ENTITY_API_URL + "?createDate=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].key").value(hasItem(DEFAULT_KEY))) + .andExpect(jsonPath("$.[*].value").value(hasItem(DEFAULT_VALUE))) + .andExpect(jsonPath("$.[*].createdDate").value(hasItem(sameInstant(DEFAULT_CREATED_DATE)))) + .andExpect(jsonPath("$.[*].lastModifiedDate").value(hasItem(sameInstant(DEFAULT_LAST_MODIFIED_DATE)))); + } + + @Test + @Transactional + void getTestParameter() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get the testParameter + restTestParameterMockMvc + .perform(get(ENTITY_API_URL_ID, testParameter.getTestResult().getId(), testParameter.getKey())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.key").value(DEFAULT_KEY)) + .andExpect(jsonPath("$.value").value(DEFAULT_VALUE)) + .andExpect(jsonPath("$.createdDate").value(sameInstant(DEFAULT_CREATED_DATE))) + .andExpect(jsonPath("$.lastModifiedDate").value(sameInstant(DEFAULT_LAST_MODIFIED_DATE))); + } + + @Test + @Transactional + void getAllTestParametersByKeyIsEqualToSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where key equals to DEFAULT_KEY + defaultTestParameterShouldBeFound("key.equals=" + DEFAULT_KEY); + + // Get all the testParameterList where key equals to UPDATED_KEY + defaultTestParameterShouldNotBeFound("key.equals=" + UPDATED_KEY); + } + + @Test + @Transactional + void getAllTestParametersByKeyIsInShouldWork() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where key in DEFAULT_KEY or UPDATED_KEY + defaultTestParameterShouldBeFound("key.in=" + DEFAULT_KEY + "," + UPDATED_KEY); + + // Get all the testParameterList where key equals to UPDATED_KEY + defaultTestParameterShouldNotBeFound("key.in=" + UPDATED_KEY); + } + + @Test + @Transactional + void getAllTestParametersByKeyIsNullOrNotNull() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where key is not null + defaultTestParameterShouldBeFound("key.specified=true"); + + // Get all the testParameterList where key is null + defaultTestParameterShouldNotBeFound("key.specified=false"); + } + + @Test + @Transactional + void getAllTestParametersByKeyContainsSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where key contains DEFAULT_KEY + defaultTestParameterShouldBeFound("key.contains=" + DEFAULT_KEY); + + // Get all the testParameterList where key contains UPDATED_KEY + defaultTestParameterShouldNotBeFound("key.contains=" + UPDATED_KEY); + } + + @Test + @Transactional + void getAllTestParametersByKeyNotContainsSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where key does not contain DEFAULT_KEY + defaultTestParameterShouldNotBeFound("key.doesNotContain=" + DEFAULT_KEY); + + // Get all the testParameterList where key does not contain UPDATED_KEY + defaultTestParameterShouldBeFound("key.doesNotContain=" + UPDATED_KEY); + } + + @Test + @Transactional + void getAllTestParametersByValueIsEqualToSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where value equals to DEFAULT_VALUE + defaultTestParameterShouldBeFound("value.equals=" + DEFAULT_VALUE); + + // Get all the testParameterList where value equals to UPDATED_VALUE + defaultTestParameterShouldNotBeFound("value.equals=" + UPDATED_VALUE); + } + + @Test + @Transactional + void getAllTestParametersByValueIsInShouldWork() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where value in DEFAULT_VALUE or UPDATED_VALUE + defaultTestParameterShouldBeFound("value.in=" + DEFAULT_VALUE + "," + UPDATED_VALUE); + + // Get all the testParameterList where value equals to UPDATED_VALUE + defaultTestParameterShouldNotBeFound("value.in=" + UPDATED_VALUE); + } + + @Test + @Transactional + void getAllTestParametersByValueIsNullOrNotNull() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where value is not null + defaultTestParameterShouldBeFound("value.specified=true"); + + // Get all the testParameterList where value is null + defaultTestParameterShouldNotBeFound("value.specified=false"); + } + + @Test + @Transactional + void getAllTestParametersByValueContainsSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where value contains DEFAULT_VALUE + defaultTestParameterShouldBeFound("value.contains=" + DEFAULT_VALUE); + + // Get all the testParameterList where value contains UPDATED_VALUE + defaultTestParameterShouldNotBeFound("value.contains=" + UPDATED_VALUE); + } + + @Test + @Transactional + void getAllTestParametersByValueNotContainsSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where value does not contain DEFAULT_VALUE + defaultTestParameterShouldNotBeFound("value.doesNotContain=" + DEFAULT_VALUE); + + // Get all the testParameterList where value does not contain UPDATED_VALUE + defaultTestParameterShouldBeFound("value.doesNotContain=" + UPDATED_VALUE); + } + + @Test + @Transactional + void getAllTestParametersByCreatedDateIsEqualToSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where createdDate equals to DEFAULT_CREATED_DATE + defaultTestParameterShouldBeFound("createdDate.equals=" + DEFAULT_CREATED_DATE); + + // Get all the testParameterList where createdDate equals to UPDATED_CREATED_DATE + defaultTestParameterShouldNotBeFound("createdDate.equals=" + UPDATED_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByCreatedDateIsInShouldWork() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where createdDate in DEFAULT_CREATED_DATE or UPDATED_CREATED_DATE + defaultTestParameterShouldBeFound("createdDate.in=" + DEFAULT_CREATED_DATE + "," + UPDATED_CREATED_DATE); + + // Get all the testParameterList where createdDate equals to UPDATED_CREATED_DATE + defaultTestParameterShouldNotBeFound("createdDate.in=" + UPDATED_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByCreatedDateIsNullOrNotNull() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where createdDate is not null + defaultTestParameterShouldBeFound("createdDate.specified=true"); + + // Get all the testParameterList where createdDate is null + defaultTestParameterShouldNotBeFound("createdDate.specified=false"); + } + + @Test + @Transactional + void getAllTestParametersByCreatedDateIsGreaterThanOrEqualToSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where createdDate is greater than or equal to DEFAULT_CREATED_DATE + defaultTestParameterShouldBeFound("createdDate.greaterThanOrEqual=" + DEFAULT_CREATED_DATE); + + // Get all the testParameterList where createdDate is greater than or equal to UPDATED_CREATED_DATE + defaultTestParameterShouldNotBeFound("createdDate.greaterThanOrEqual=" + UPDATED_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByCreatedDateIsLessThanOrEqualToSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where createdDate is less than or equal to DEFAULT_CREATED_DATE + defaultTestParameterShouldBeFound("createdDate.lessThanOrEqual=" + DEFAULT_CREATED_DATE); + + // Get all the testParameterList where createdDate is less than or equal to SMALLER_CREATED_DATE + defaultTestParameterShouldNotBeFound("createdDate.lessThanOrEqual=" + SMALLER_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByCreatedDateIsLessThanSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where createdDate is less than DEFAULT_CREATED_DATE + defaultTestParameterShouldNotBeFound("createdDate.lessThan=" + DEFAULT_CREATED_DATE); + + // Get all the testParameterList where createdDate is less than UPDATED_CREATED_DATE + defaultTestParameterShouldBeFound("createdDate.lessThan=" + UPDATED_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByCreatedDateIsGreaterThanSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where createdDate is greater than DEFAULT_CREATED_DATE + defaultTestParameterShouldNotBeFound("createdDate.greaterThan=" + DEFAULT_CREATED_DATE); + + // Get all the testParameterList where createdDate is greater than SMALLER_CREATED_DATE + defaultTestParameterShouldBeFound("createdDate.greaterThan=" + SMALLER_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByLastModifiedDateIsEqualToSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where lastModifiedDate equals to DEFAULT_LAST_MODIFIED_DATE + defaultTestParameterShouldBeFound("lastModifiedDate.equals=" + DEFAULT_LAST_MODIFIED_DATE); + + // Get all the testParameterList where lastModifiedDate equals to UPDATED_LAST_MODIFIED_DATE + defaultTestParameterShouldNotBeFound("lastModifiedDate.equals=" + UPDATED_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByLastModifiedDateIsInShouldWork() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where lastModifiedDate in DEFAULT_LAST_MODIFIED_DATE or UPDATED_LAST_MODIFIED_DATE + defaultTestParameterShouldBeFound("lastModifiedDate.in=" + DEFAULT_LAST_MODIFIED_DATE + "," + UPDATED_LAST_MODIFIED_DATE); + + // Get all the testParameterList where lastModifiedDate equals to UPDATED_LAST_MODIFIED_DATE + defaultTestParameterShouldNotBeFound("lastModifiedDate.in=" + UPDATED_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByLastModifiedDateIsNullOrNotNull() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where lastModifiedDate is not null + defaultTestParameterShouldBeFound("lastModifiedDate.specified=true"); + + // Get all the testParameterList where lastModifiedDate is null + defaultTestParameterShouldNotBeFound("lastModifiedDate.specified=false"); + } + + @Test + @Transactional + void getAllTestParametersByLastModifiedDateIsGreaterThanOrEqualToSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where lastModifiedDate is greater than or equal to DEFAULT_LAST_MODIFIED_DATE + defaultTestParameterShouldBeFound("lastModifiedDate.greaterThanOrEqual=" + DEFAULT_LAST_MODIFIED_DATE); + + // Get all the testParameterList where lastModifiedDate is greater than or equal to UPDATED_LAST_MODIFIED_DATE + defaultTestParameterShouldNotBeFound("lastModifiedDate.greaterThanOrEqual=" + UPDATED_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByLastModifiedDateIsLessThanOrEqualToSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where lastModifiedDate is less than or equal to DEFAULT_LAST_MODIFIED_DATE + defaultTestParameterShouldBeFound("lastModifiedDate.lessThanOrEqual=" + DEFAULT_LAST_MODIFIED_DATE); + + // Get all the testParameterList where lastModifiedDate is less than or equal to SMALLER_LAST_MODIFIED_DATE + defaultTestParameterShouldNotBeFound("lastModifiedDate.lessThanOrEqual=" + SMALLER_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByLastModifiedDateIsLessThanSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where lastModifiedDate is less than DEFAULT_LAST_MODIFIED_DATE + defaultTestParameterShouldNotBeFound("lastModifiedDate.lessThan=" + DEFAULT_LAST_MODIFIED_DATE); + + // Get all the testParameterList where lastModifiedDate is less than UPDATED_LAST_MODIFIED_DATE + defaultTestParameterShouldBeFound("lastModifiedDate.lessThan=" + UPDATED_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByLastModifiedDateIsGreaterThanSomething() throws Exception { + // Initialize the database + testParameterRepository.saveAndFlush(testParameter); + + // Get all the testParameterList where lastModifiedDate is greater than DEFAULT_LAST_MODIFIED_DATE + defaultTestParameterShouldNotBeFound("lastModifiedDate.greaterThan=" + DEFAULT_LAST_MODIFIED_DATE); + + // Get all the testParameterList where lastModifiedDate is greater than SMALLER_LAST_MODIFIED_DATE + defaultTestParameterShouldBeFound("lastModifiedDate.greaterThan=" + SMALLER_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestParametersByTestResultIsEqualToSomething() throws Exception { + TestResult testResult; + if (TestUtil.findAll(em, TestResult.class).isEmpty()) { + testParameterRepository.saveAndFlush(testParameter); + testResult = TestResultResourceIT.createEntity(em); + } else { + testResult = TestUtil.findAll(em, TestResult.class).get(0); + } + em.persist(testResult); + em.flush(); + testParameter.setTestResult(testResult); + testParameterRepository.saveAndFlush(testParameter); + Long testResultId = testResult.getId(); + // Get all the testParameterList where testResult equals to testResultId + defaultTestParameterShouldBeFound("testResultId.equals=" + testResultId); + + // Get all the testParameterList where testResult equals to (testResultId + 1) + defaultTestParameterShouldNotBeFound("testResultId.equals=" + (testResultId + 1)); + } + + /** + * Executes the search, and checks that the default entity is returned. + */ + private void defaultTestParameterShouldBeFound(String filter) throws Exception { + restTestParameterMockMvc + .perform(get(ENTITY_API_URL + "?createDate=id,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].key").value(hasItem(DEFAULT_KEY))) + .andExpect(jsonPath("$.[*].value").value(hasItem(DEFAULT_VALUE))) + .andExpect(jsonPath("$.[*].createdDate").value(hasItem(sameInstant(DEFAULT_CREATED_DATE)))) + .andExpect(jsonPath("$.[*].lastModifiedDate").value(hasItem(sameInstant(DEFAULT_LAST_MODIFIED_DATE)))); + + // Check, that the count call also returns 1 + restTestParameterMockMvc + .perform(get(ENTITY_API_URL + "/count?createDate=id,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(content().string("1")); + } + + /** + * Executes the search, and checks that the default entity is not returned. + */ + private void defaultTestParameterShouldNotBeFound(String filter) throws Exception { + restTestParameterMockMvc + .perform(get(ENTITY_API_URL + "?createDate=id,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$").isEmpty()); + + // Check, that the count call also returns 0 + restTestParameterMockMvc + .perform(get(ENTITY_API_URL + "/count?createDate=id,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(content().string("0")); + } + + @Test + @Transactional + void getNonExistingTestParameter() throws Exception { + // Get the testParameter + restTestParameterMockMvc.perform(get(ENTITY_API_URL_ID, testParameter.getTestResult().getId(), Long.MAX_VALUE)).andExpect(status().isNotFound()); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/web/rest/TestResultResourceIT.java b/simulator-starter/src/test/java/org/citrusframework/simulator/web/rest/TestResultResourceIT.java new file mode 100644 index 000000000..601bc8f36 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/web/rest/TestResultResourceIT.java @@ -0,0 +1,781 @@ +package org.citrusframework.simulator.web.rest; + +import jakarta.persistence.EntityManager; +import org.citrusframework.simulator.IntegrationTest; +import org.citrusframework.simulator.model.TestParameter; +import org.citrusframework.simulator.model.TestResult; +import org.citrusframework.simulator.repository.TestResultRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +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.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import static org.citrusframework.simulator.web.rest.TestUtil.sameInstant; +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Integration tests for the {@link TestResultResource} REST controller. + */ +@IntegrationTest +@AutoConfigureMockMvc +class TestResultResourceIT { + + private static final TestResult.Status DEFAULT_STATUS = TestResult.Status.SUCCESS; // Integer value: 1 + private static final TestResult.Status UPDATED_STATUS = TestResult.Status.FAILURE; // Integer value: 2 + + private static final String DEFAULT_TEST_NAME = "AAAAAAAAAA"; + private static final String UPDATED_TEST_NAME = "BBBBBBBBBB"; + + private static final String DEFAULT_CLASS_NAME = "AAAAAAAAAA"; + private static final String UPDATED_CLASS_NAME = "BBBBBBBBBB"; + + private static final String DEFAULT_ERROR_MESSAGE = "AAAAAAAAAA"; + private static final String UPDATED_ERROR_MESSAGE = "BBBBBBBBBB"; + + private static final String DEFAULT_FAILURE_STACK = "AAAAAAAAAA"; + private static final String UPDATED_FAILURE_STACK = "BBBBBBBBBB"; + + private static final String DEFAULT_FAILURE_TYPE = "AAAAAAAAAA"; + private static final String UPDATED_FAILURE_TYPE = "BBBBBBBBBB"; + + private static final ZonedDateTime DEFAULT_CREATED_DATE = ZonedDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneOffset.UTC); + private static final ZonedDateTime UPDATED_CREATED_DATE = ZonedDateTime.now(ZoneId.systemDefault()).withNano(0); + private static final ZonedDateTime SMALLER_CREATED_DATE = ZonedDateTime.ofInstant(Instant.ofEpochMilli(-1L), ZoneOffset.UTC); + + private static final ZonedDateTime DEFAULT_LAST_MODIFIED_DATE = ZonedDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneOffset.UTC); + private static final ZonedDateTime UPDATED_LAST_MODIFIED_DATE = ZonedDateTime.now(ZoneId.systemDefault()).withNano(0); + private static final ZonedDateTime SMALLER_LAST_MODIFIED_DATE = ZonedDateTime.ofInstant(Instant.ofEpochMilli(-1L), ZoneOffset.UTC); + + private static final String ENTITY_API_URL = "/api/test-results"; + private static final String ENTITY_API_URL_ID = ENTITY_API_URL + "/{id}"; + + @Autowired + private TestResultRepository testResultRepository; + + @Autowired + private EntityManager em; + + @Autowired + private MockMvc restTestResultMockMvc; + + private TestResult testResult; + + /** + * Create an entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static TestResult createEntity(EntityManager em) { + TestResult testResult = TestResult.builder() + .status(DEFAULT_STATUS.getId()) + .testName(DEFAULT_TEST_NAME) + .className(DEFAULT_CLASS_NAME) + .errorMessage(DEFAULT_ERROR_MESSAGE) + .failureStack(DEFAULT_FAILURE_STACK) + .failureType(DEFAULT_FAILURE_TYPE) + .createdDate(DEFAULT_CREATED_DATE) + .lastModifiedDate(DEFAULT_LAST_MODIFIED_DATE) + .build(); + return testResult; + } + + @BeforeEach + public void initTest() { + testResult = createEntity(em); + } + + @Test + @Transactional + void getAllTestResults() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList + restTestResultMockMvc + .perform(get(ENTITY_API_URL + "?sort=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(testResult.getId().intValue()))) + .andExpect(jsonPath("$.[*].status").value(hasItem(DEFAULT_STATUS.toString()))) + .andExpect(jsonPath("$.[*].testName").value(hasItem(DEFAULT_TEST_NAME))) + .andExpect(jsonPath("$.[*].className").value(hasItem(DEFAULT_CLASS_NAME))) + .andExpect(jsonPath("$.[*].errorMessage").value(hasItem(DEFAULT_ERROR_MESSAGE))) + .andExpect(jsonPath("$.[*].failureStack").value(hasItem(DEFAULT_FAILURE_STACK))) + .andExpect(jsonPath("$.[*].failureType").value(hasItem(DEFAULT_FAILURE_TYPE))) + .andExpect(jsonPath("$.[*].createdDate").value(hasItem(sameInstant(DEFAULT_CREATED_DATE)))) + .andExpect(jsonPath("$.[*].lastModifiedDate").value(hasItem(sameInstant(DEFAULT_LAST_MODIFIED_DATE)))); + } + + @Test + @Transactional + void getTestResult() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get the testResult + restTestResultMockMvc + .perform(get(ENTITY_API_URL_ID, testResult.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.id").value(testResult.getId().intValue())) + .andExpect(jsonPath("$.status").value(DEFAULT_STATUS.toString())) + .andExpect(jsonPath("$.testName").value(DEFAULT_TEST_NAME)) + .andExpect(jsonPath("$.className").value(DEFAULT_CLASS_NAME)) + .andExpect(jsonPath("$.errorMessage").value(DEFAULT_ERROR_MESSAGE)) + .andExpect(jsonPath("$.failureStack").value(DEFAULT_FAILURE_STACK)) + .andExpect(jsonPath("$.failureType").value(DEFAULT_FAILURE_TYPE)) + .andExpect(jsonPath("$.createdDate").value(sameInstant(DEFAULT_CREATED_DATE))) + .andExpect(jsonPath("$.lastModifiedDate").value(sameInstant(DEFAULT_LAST_MODIFIED_DATE))); + } + + @Test + @Transactional + void getTestResultsByIdFiltering() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + Long id = testResult.getId(); + + defaultTestResultShouldBeFound("id.equals=" + id); + defaultTestResultShouldNotBeFound("id.notEquals=" + id); + + defaultTestResultShouldBeFound("id.greaterThanOrEqual=" + id); + defaultTestResultShouldNotBeFound("id.greaterThan=" + id); + + defaultTestResultShouldBeFound("id.lessThanOrEqual=" + id); + defaultTestResultShouldNotBeFound("id.lessThan=" + id); + } + + @Test + @Transactional + void getAllTestResultsByStatusIsEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where status equals to DEFAULT_STATUS + defaultTestResultShouldBeFound("status.equals=" + DEFAULT_STATUS.getId()); + + // Get all the testResultList where status equals to UPDATED_STATUS + defaultTestResultShouldNotBeFound("status.equals=" + UPDATED_STATUS.getId()); + } + + @Test + @Transactional + void getAllTestResultsByStatusIsInShouldWork() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where status in DEFAULT_STATUS or UPDATED_STATUS + defaultTestResultShouldBeFound("status.in=" + DEFAULT_STATUS.getId() + "," + UPDATED_STATUS.getId()); + + // Get all the testResultList where status equals to UPDATED_STATUS + defaultTestResultShouldNotBeFound("status.in=" + UPDATED_STATUS.getId()); + } + + @Test + @Transactional + void getAllTestResultsByStatusIsNullOrNotNull() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where status is not null + defaultTestResultShouldBeFound("status.specified=true"); + + // Get all the testResultList where status is null + defaultTestResultShouldNotBeFound("status.specified=false"); + } + + @Test + @Transactional + void getAllTestResultsByTestNameIsEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where testName equals to DEFAULT_TEST_NAME + defaultTestResultShouldBeFound("testName.equals=" + DEFAULT_TEST_NAME); + + // Get all the testResultList where testName equals to UPDATED_TEST_NAME + defaultTestResultShouldNotBeFound("testName.equals=" + UPDATED_TEST_NAME); + } + + @Test + @Transactional + void getAllTestResultsByTestNameIsInShouldWork() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where testName in DEFAULT_TEST_NAME or UPDATED_TEST_NAME + defaultTestResultShouldBeFound("testName.in=" + DEFAULT_TEST_NAME + "," + UPDATED_TEST_NAME); + + // Get all the testResultList where testName equals to UPDATED_TEST_NAME + defaultTestResultShouldNotBeFound("testName.in=" + UPDATED_TEST_NAME); + } + + @Test + @Transactional + void getAllTestResultsByTestNameIsNullOrNotNull() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where testName is not null + defaultTestResultShouldBeFound("testName.specified=true"); + + // Get all the testResultList where testName is null + defaultTestResultShouldNotBeFound("testName.specified=false"); + } + + @Test + @Transactional + void getAllTestResultsByTestNameContainsSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where testName contains DEFAULT_TEST_NAME + defaultTestResultShouldBeFound("testName.contains=" + DEFAULT_TEST_NAME); + + // Get all the testResultList where testName contains UPDATED_TEST_NAME + defaultTestResultShouldNotBeFound("testName.contains=" + UPDATED_TEST_NAME); + } + + @Test + @Transactional + void getAllTestResultsByTestNameNotContainsSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where testName does not contain DEFAULT_TEST_NAME + defaultTestResultShouldNotBeFound("testName.doesNotContain=" + DEFAULT_TEST_NAME); + + // Get all the testResultList where testName does not contain UPDATED_TEST_NAME + defaultTestResultShouldBeFound("testName.doesNotContain=" + UPDATED_TEST_NAME); + } + + @Test + @Transactional + void getAllTestResultsByClassNameIsEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where className equals to DEFAULT_CLASS_NAME + defaultTestResultShouldBeFound("className.equals=" + DEFAULT_CLASS_NAME); + + // Get all the testResultList where className equals to UPDATED_CLASS_NAME + defaultTestResultShouldNotBeFound("className.equals=" + UPDATED_CLASS_NAME); + } + + @Test + @Transactional + void getAllTestResultsByClassNameIsInShouldWork() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where className in DEFAULT_CLASS_NAME or UPDATED_CLASS_NAME + defaultTestResultShouldBeFound("className.in=" + DEFAULT_CLASS_NAME + "," + UPDATED_CLASS_NAME); + + // Get all the testResultList where className equals to UPDATED_CLASS_NAME + defaultTestResultShouldNotBeFound("className.in=" + UPDATED_CLASS_NAME); + } + + @Test + @Transactional + void getAllTestResultsByClassNameIsNullOrNotNull() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where className is not null + defaultTestResultShouldBeFound("className.specified=true"); + + // Get all the testResultList where className is null + defaultTestResultShouldNotBeFound("className.specified=false"); + } + + @Test + @Transactional + void getAllTestResultsByClassNameContainsSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where className contains DEFAULT_CLASS_NAME + defaultTestResultShouldBeFound("className.contains=" + DEFAULT_CLASS_NAME); + + // Get all the testResultList where className contains UPDATED_CLASS_NAME + defaultTestResultShouldNotBeFound("className.contains=" + UPDATED_CLASS_NAME); + } + + @Test + @Transactional + void getAllTestResultsByClassNameNotContainsSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where className does not contain DEFAULT_CLASS_NAME + defaultTestResultShouldNotBeFound("className.doesNotContain=" + DEFAULT_CLASS_NAME); + + // Get all the testResultList where className does not contain UPDATED_CLASS_NAME + defaultTestResultShouldBeFound("className.doesNotContain=" + UPDATED_CLASS_NAME); + } + + @Test + @Transactional + void getAllTestResultsByErrorMessageIsEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where errorMessage equals to DEFAULT_ERROR_MESSAGE + defaultTestResultShouldBeFound("errorMessage.equals=" + DEFAULT_ERROR_MESSAGE); + + // Get all the testResultList where errorMessage equals to UPDATED_ERROR_MESSAGE + defaultTestResultShouldNotBeFound("errorMessage.equals=" + UPDATED_ERROR_MESSAGE); + } + + @Test + @Transactional + void getAllTestResultsByErrorMessageIsInShouldWork() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where errorMessage in DEFAULT_ERROR_MESSAGE or UPDATED_ERROR_MESSAGE + defaultTestResultShouldBeFound("errorMessage.in=" + DEFAULT_ERROR_MESSAGE + "," + UPDATED_ERROR_MESSAGE); + + // Get all the testResultList where errorMessage equals to UPDATED_ERROR_MESSAGE + defaultTestResultShouldNotBeFound("errorMessage.in=" + UPDATED_ERROR_MESSAGE); + } + + @Test + @Transactional + void getAllTestResultsByErrorMessageIsNullOrNotNull() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where errorMessage is not null + defaultTestResultShouldBeFound("errorMessage.specified=true"); + + // Get all the testResultList where errorMessage is null + defaultTestResultShouldNotBeFound("errorMessage.specified=false"); + } + + @Test + @Transactional + void getAllTestResultsByErrorMessageContainsSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where errorMessage contains DEFAULT_ERROR_MESSAGE + defaultTestResultShouldBeFound("errorMessage.contains=" + DEFAULT_ERROR_MESSAGE); + + // Get all the testResultList where errorMessage contains UPDATED_ERROR_MESSAGE + defaultTestResultShouldNotBeFound("errorMessage.contains=" + UPDATED_ERROR_MESSAGE); + } + + @Test + @Transactional + void getAllTestResultsByErrorMessageNotContainsSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where errorMessage does not contain DEFAULT_ERROR_MESSAGE + defaultTestResultShouldNotBeFound("errorMessage.doesNotContain=" + DEFAULT_ERROR_MESSAGE); + + // Get all the testResultList where errorMessage does not contain UPDATED_ERROR_MESSAGE + defaultTestResultShouldBeFound("errorMessage.doesNotContain=" + UPDATED_ERROR_MESSAGE); + } + + @Test + @Transactional + void getAllTestResultsByFailureStackIsEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where failureStack equals to DEFAULT_FAILURE_STACK + defaultTestResultShouldBeFound("failureStack.equals=" + DEFAULT_FAILURE_STACK); + + // Get all the testResultList where failureStack equals to UPDATED_FAILURE_STACK + defaultTestResultShouldNotBeFound("failureStack.equals=" + UPDATED_FAILURE_STACK); + } + + @Test + @Transactional + void getAllTestResultsByFailureStackIsInShouldWork() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where failureStack in DEFAULT_FAILURE_STACK or UPDATED_FAILURE_STACK + defaultTestResultShouldBeFound("failureStack.in=" + DEFAULT_FAILURE_STACK + "," + UPDATED_FAILURE_STACK); + + // Get all the testResultList where failureStack equals to UPDATED_FAILURE_STACK + defaultTestResultShouldNotBeFound("failureStack.in=" + UPDATED_FAILURE_STACK); + } + + @Test + @Transactional + void getAllTestResultsByFailureStackIsNullOrNotNull() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where failureStack is not null + defaultTestResultShouldBeFound("failureStack.specified=true"); + + // Get all the testResultList where failureStack is null + defaultTestResultShouldNotBeFound("failureStack.specified=false"); + } + + @Test + @Transactional + void getAllTestResultsByFailureStackContainsSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where failureStack contains DEFAULT_FAILURE_STACK + defaultTestResultShouldBeFound("failureStack.contains=" + DEFAULT_FAILURE_STACK); + + // Get all the testResultList where failureStack contains UPDATED_FAILURE_STACK + defaultTestResultShouldNotBeFound("failureStack.contains=" + UPDATED_FAILURE_STACK); + } + + @Test + @Transactional + void getAllTestResultsByFailureStackNotContainsSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where failureStack does not contain DEFAULT_FAILURE_STACK + defaultTestResultShouldNotBeFound("failureStack.doesNotContain=" + DEFAULT_FAILURE_STACK); + + // Get all the testResultList where failureStack does not contain UPDATED_FAILURE_STACK + defaultTestResultShouldBeFound("failureStack.doesNotContain=" + UPDATED_FAILURE_STACK); + } + + @Test + @Transactional + void getAllTestResultsByFailureTypeIsEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where failureType equals to DEFAULT_FAILURE_TYPE + defaultTestResultShouldBeFound("failureType.equals=" + DEFAULT_FAILURE_TYPE); + + // Get all the testResultList where failureType equals to UPDATED_FAILURE_TYPE + defaultTestResultShouldNotBeFound("failureType.equals=" + UPDATED_FAILURE_TYPE); + } + + @Test + @Transactional + void getAllTestResultsByFailureTypeIsInShouldWork() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where failureType in DEFAULT_FAILURE_TYPE or UPDATED_FAILURE_TYPE + defaultTestResultShouldBeFound("failureType.in=" + DEFAULT_FAILURE_TYPE + "," + UPDATED_FAILURE_TYPE); + + // Get all the testResultList where failureType equals to UPDATED_FAILURE_TYPE + defaultTestResultShouldNotBeFound("failureType.in=" + UPDATED_FAILURE_TYPE); + } + + @Test + @Transactional + void getAllTestResultsByFailureTypeIsNullOrNotNull() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where failureType is not null + defaultTestResultShouldBeFound("failureType.specified=true"); + + // Get all the testResultList where failureType is null + defaultTestResultShouldNotBeFound("failureType.specified=false"); + } + + @Test + @Transactional + void getAllTestResultsByFailureTypeContainsSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where failureType contains DEFAULT_FAILURE_TYPE + defaultTestResultShouldBeFound("failureType.contains=" + DEFAULT_FAILURE_TYPE); + + // Get all the testResultList where failureType contains UPDATED_FAILURE_TYPE + defaultTestResultShouldNotBeFound("failureType.contains=" + UPDATED_FAILURE_TYPE); + } + + @Test + @Transactional + void getAllTestResultsByFailureTypeNotContainsSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where failureType does not contain DEFAULT_FAILURE_TYPE + defaultTestResultShouldNotBeFound("failureType.doesNotContain=" + DEFAULT_FAILURE_TYPE); + + // Get all the testResultList where failureType does not contain UPDATED_FAILURE_TYPE + defaultTestResultShouldBeFound("failureType.doesNotContain=" + UPDATED_FAILURE_TYPE); + } + + @Test + @Transactional + void getAllTestResultsByCreatedDateIsEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where createdDate equals to DEFAULT_CREATED_DATE + defaultTestResultShouldBeFound("createdDate.equals=" + DEFAULT_CREATED_DATE); + + // Get all the testResultList where createdDate equals to UPDATED_CREATED_DATE + defaultTestResultShouldNotBeFound("createdDate.equals=" + UPDATED_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByCreatedDateIsInShouldWork() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where createdDate in DEFAULT_CREATED_DATE or UPDATED_CREATED_DATE + defaultTestResultShouldBeFound("createdDate.in=" + DEFAULT_CREATED_DATE + "," + UPDATED_CREATED_DATE); + + // Get all the testResultList where createdDate equals to UPDATED_CREATED_DATE + defaultTestResultShouldNotBeFound("createdDate.in=" + UPDATED_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByCreatedDateIsNullOrNotNull() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where createdDate is not null + defaultTestResultShouldBeFound("createdDate.specified=true"); + + // Get all the testResultList where createdDate is null + defaultTestResultShouldNotBeFound("createdDate.specified=false"); + } + + @Test + @Transactional + void getAllTestResultsByCreatedDateIsGreaterThanOrEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where createdDate is greater than or equal to DEFAULT_CREATED_DATE + defaultTestResultShouldBeFound("createdDate.greaterThanOrEqual=" + DEFAULT_CREATED_DATE); + + // Get all the testResultList where createdDate is greater than or equal to UPDATED_CREATED_DATE + defaultTestResultShouldNotBeFound("createdDate.greaterThanOrEqual=" + UPDATED_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByCreatedDateIsLessThanOrEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where createdDate is less than or equal to DEFAULT_CREATED_DATE + defaultTestResultShouldBeFound("createdDate.lessThanOrEqual=" + DEFAULT_CREATED_DATE); + + // Get all the testResultList where createdDate is less than or equal to SMALLER_CREATED_DATE + defaultTestResultShouldNotBeFound("createdDate.lessThanOrEqual=" + SMALLER_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByCreatedDateIsLessThanSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where createdDate is less than DEFAULT_CREATED_DATE + defaultTestResultShouldNotBeFound("createdDate.lessThan=" + DEFAULT_CREATED_DATE); + + // Get all the testResultList where createdDate is less than UPDATED_CREATED_DATE + defaultTestResultShouldBeFound("createdDate.lessThan=" + UPDATED_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByCreatedDateIsGreaterThanSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where createdDate is greater than DEFAULT_CREATED_DATE + defaultTestResultShouldNotBeFound("createdDate.greaterThan=" + DEFAULT_CREATED_DATE); + + // Get all the testResultList where createdDate is greater than SMALLER_CREATED_DATE + defaultTestResultShouldBeFound("createdDate.greaterThan=" + SMALLER_CREATED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByLastModifiedDateIsEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where lastModifiedDate equals to DEFAULT_LAST_MODIFIED_DATE + defaultTestResultShouldBeFound("lastModifiedDate.equals=" + DEFAULT_LAST_MODIFIED_DATE); + + // Get all the testResultList where lastModifiedDate equals to UPDATED_LAST_MODIFIED_DATE + defaultTestResultShouldNotBeFound("lastModifiedDate.equals=" + UPDATED_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByLastModifiedDateIsInShouldWork() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where lastModifiedDate in DEFAULT_LAST_MODIFIED_DATE or UPDATED_LAST_MODIFIED_DATE + defaultTestResultShouldBeFound("lastModifiedDate.in=" + DEFAULT_LAST_MODIFIED_DATE + "," + UPDATED_LAST_MODIFIED_DATE); + + // Get all the testResultList where lastModifiedDate equals to UPDATED_LAST_MODIFIED_DATE + defaultTestResultShouldNotBeFound("lastModifiedDate.in=" + UPDATED_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByLastModifiedDateIsNullOrNotNull() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where lastModifiedDate is not null + defaultTestResultShouldBeFound("lastModifiedDate.specified=true"); + + // Get all the testResultList where lastModifiedDate is null + defaultTestResultShouldNotBeFound("lastModifiedDate.specified=false"); + } + + @Test + @Transactional + void getAllTestResultsByLastModifiedDateIsGreaterThanOrEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where lastModifiedDate is greater than or equal to DEFAULT_LAST_MODIFIED_DATE + defaultTestResultShouldBeFound("lastModifiedDate.greaterThanOrEqual=" + DEFAULT_LAST_MODIFIED_DATE); + + // Get all the testResultList where lastModifiedDate is greater than or equal to UPDATED_LAST_MODIFIED_DATE + defaultTestResultShouldNotBeFound("lastModifiedDate.greaterThanOrEqual=" + UPDATED_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByLastModifiedDateIsLessThanOrEqualToSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where lastModifiedDate is less than or equal to DEFAULT_LAST_MODIFIED_DATE + defaultTestResultShouldBeFound("lastModifiedDate.lessThanOrEqual=" + DEFAULT_LAST_MODIFIED_DATE); + + // Get all the testResultList where lastModifiedDate is less than or equal to SMALLER_LAST_MODIFIED_DATE + defaultTestResultShouldNotBeFound("lastModifiedDate.lessThanOrEqual=" + SMALLER_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByLastModifiedDateIsLessThanSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where lastModifiedDate is less than DEFAULT_LAST_MODIFIED_DATE + defaultTestResultShouldNotBeFound("lastModifiedDate.lessThan=" + DEFAULT_LAST_MODIFIED_DATE); + + // Get all the testResultList where lastModifiedDate is less than UPDATED_LAST_MODIFIED_DATE + defaultTestResultShouldBeFound("lastModifiedDate.lessThan=" + UPDATED_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByLastModifiedDateIsGreaterThanSomething() throws Exception { + // Initialize the database + testResultRepository.saveAndFlush(testResult); + + // Get all the testResultList where lastModifiedDate is greater than DEFAULT_LAST_MODIFIED_DATE + defaultTestResultShouldNotBeFound("lastModifiedDate.greaterThan=" + DEFAULT_LAST_MODIFIED_DATE); + + // Get all the testResultList where lastModifiedDate is greater than SMALLER_LAST_MODIFIED_DATE + defaultTestResultShouldBeFound("lastModifiedDate.greaterThan=" + SMALLER_LAST_MODIFIED_DATE); + } + + @Test + @Transactional + void getAllTestResultsByTestParameterIsEqualToSomething() throws Exception { + TestParameter testParameter; + if (TestUtil.findAll(em, TestParameter.class).isEmpty()) { + testResultRepository.saveAndFlush(testResult); + testParameter = TestParameterResourceIT.createEntity(em); + } else { + testParameter = TestUtil.findAll(em, TestParameter.class).get(0); + } + em.persist(testParameter); + em.flush(); + testResult.addTestParameter(testParameter); + testResultRepository.saveAndFlush(testResult); + String testParameterKey = testParameter.getKey(); + // Get all the testResultList where testParameter equals to testParameterKey + defaultTestResultShouldBeFound("testParameterKey.equals=" + testParameterKey); + + // Get all the testResultList where testParameter equals to (testParameterKey + 1) + defaultTestResultShouldNotBeFound("testParameterKey.equals=" + (testParameterKey + 1)); + } + + /** + * Executes the search, and checks that the default entity is returned. + */ + private void defaultTestResultShouldBeFound(String filter) throws Exception { + restTestResultMockMvc + .perform(get(ENTITY_API_URL + "?sort=id,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(testResult.getId().intValue()))) + .andExpect(jsonPath("$.[*].status").value(hasItem(DEFAULT_STATUS.toString()))) + .andExpect(jsonPath("$.[*].testName").value(hasItem(DEFAULT_TEST_NAME))) + .andExpect(jsonPath("$.[*].className").value(hasItem(DEFAULT_CLASS_NAME))) + .andExpect(jsonPath("$.[*].errorMessage").value(hasItem(DEFAULT_ERROR_MESSAGE))) + .andExpect(jsonPath("$.[*].failureStack").value(hasItem(DEFAULT_FAILURE_STACK))) + .andExpect(jsonPath("$.[*].failureType").value(hasItem(DEFAULT_FAILURE_TYPE))) + .andExpect(jsonPath("$.[*].createdDate").value(hasItem(sameInstant(DEFAULT_CREATED_DATE)))) + .andExpect(jsonPath("$.[*].lastModifiedDate").value(hasItem(sameInstant(DEFAULT_LAST_MODIFIED_DATE)))); + + // Check, that the count call also returns 1 + restTestResultMockMvc + .perform(get(ENTITY_API_URL + "/count?sort=id,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(content().string("1")); + } + + /** + * Executes the search, and checks that the default entity is not returned. + */ + private void defaultTestResultShouldNotBeFound(String filter) throws Exception { + restTestResultMockMvc + .perform(get(ENTITY_API_URL + "?sort=id,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$").isEmpty()); + + // Check, that the count call also returns 0 + restTestResultMockMvc + .perform(get(ENTITY_API_URL + "/count?sort=id,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(content().string("0")); + } + + @Test + @Transactional + void getNonExistingTestResult() throws Exception { + // Get the testResult + restTestResultMockMvc.perform(get(ENTITY_API_URL_ID, Long.MAX_VALUE)).andExpect(status().isNotFound()); + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/web/rest/TestUtil.java b/simulator-starter/src/test/java/org/citrusframework/simulator/web/rest/TestUtil.java new file mode 100644 index 000000000..84d94d627 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/web/rest/TestUtil.java @@ -0,0 +1,76 @@ +package org.citrusframework.simulator.web.rest; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; +import java.util.List; + +/** + * Utility class for testing REST controllers. + */ +public class TestUtil { + + /** + * Executes a query on the EntityManager finding all stored objects. + * + * @param The type of objects to be searched + * @param em The instance of the EntityManager + * @param clazz The class type to be searched + * @return A list of all found objects + */ + public static List findAll(EntityManager em, Class clazz) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(clazz); + Root rootEntry = cq.from(clazz); + CriteriaQuery all = cq.select(rootEntry); + TypedQuery allQuery = em.createQuery(all); + return allQuery.getResultList(); + } + + /** + * Creates a matcher that matches when the examined string represents the same instant as the reference datetime. + * + * @param date the reference datetime against which the examined string is checked. + */ + public static ZonedDateTimeMatcher sameInstant(ZonedDateTime date) { + return new ZonedDateTimeMatcher(date); + } + + /** + * A matcher that tests that the examined string represents the same instant as the reference datetime. + */ + public static class ZonedDateTimeMatcher extends TypeSafeDiagnosingMatcher { + + private final ZonedDateTime date; + + public ZonedDateTimeMatcher(ZonedDateTime date) { + this.date = date; + } + + @Override + protected boolean matchesSafely(String item, Description mismatchDescription) { + try { + if (!date.isEqual(ZonedDateTime.parse(item))) { + mismatchDescription.appendText("was ").appendValue(item); + return false; + } + return true; + } catch (DateTimeParseException e) { + mismatchDescription.appendText("was ").appendValue(item).appendText(", which could not be parsed as a ZonedDateTime"); + return false; + } + } + + @Override + public void describeTo(Description description) { + description.appendText("a String representing the same Instant as ").appendValue(date); + } + } +} diff --git a/simulator-starter/src/test/java/org/citrusframework/simulator/web/util/ResponseUtilTest.java b/simulator-starter/src/test/java/org/citrusframework/simulator/web/util/ResponseUtilTest.java new file mode 100644 index 000000000..95501d712 --- /dev/null +++ b/simulator-starter/src/test/java/org/citrusframework/simulator/web/util/ResponseUtilTest.java @@ -0,0 +1,77 @@ +package org.citrusframework.simulator.web.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.server.ResponseStatusException; + +class ResponseUtilTest { + + @Test + void wrapOrNotFoundWithPresentOptionalShouldReturnResponseEntityWithOkStatus() { + String expectedBody = "test"; + ResponseEntity response = ResponseUtil.wrapOrNotFound(Optional.of(expectedBody)); + + assertNotNull(response); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(expectedBody, response.getBody()); + } + + @Test + void wrapOrNotFoundWithEmptyOptionalShouldThrowResponseStatusException() { + var maybeResponse = Optional.empty(); + ResponseStatusException responseStatusException = assertThrows( + ResponseStatusException.class, + () -> ResponseUtil.wrapOrNotFound(maybeResponse) + ); + assertEquals(HttpStatus.NOT_FOUND, responseStatusException.getStatusCode()); + } + + @Test + void wrapOrNotFoundWithPresentOptionalAndHeadersShouldReturnResponseEntityWithOkStatusAndHeaders() { + String headerKey = "Test-Header"; + String headerValue = "HeaderValue"; + + String expectedBody = "test"; + + HttpHeaders headers = new HttpHeaders(); + headers.add(headerKey, headerValue); + + ResponseEntity response = ResponseUtil.wrapOrNotFound(Optional.of(expectedBody), headers); + assertNotNull(response); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(expectedBody, response.getBody()); + + HttpHeaders responseHeaders = response.getHeaders(); + assertTrue(responseHeaders.containsKey(headerKey)); + + List testHeaders = responseHeaders.get(headerKey); + assertNotNull(testHeaders); + assertEquals(1, testHeaders.size()); + assertEquals(headerValue, testHeaders.get(0)); + } + + @Test + void wrapOrNotFoundWithEmptyOptionalAndHeadersShouldThrowResponseStatusException() { + HttpHeaders headers = new HttpHeaders(); + headers.add("Test-Header", "HeaderValue"); + + var maybeResponse = Optional.empty(); + ResponseStatusException responseStatusException = assertThrows( + ResponseStatusException.class, + () -> ResponseUtil.wrapOrNotFound(maybeResponse, headers) + ); + assertEquals(HttpStatus.NOT_FOUND, responseStatusException.getStatusCode()); + + assertEquals(0, responseStatusException.getHeaders().size()); + } +} diff --git a/simulator-ui/.eslintrc.json b/simulator-ui/.eslintrc.json index 96996aea9..6ec524114 100644 --- a/simulator-ui/.eslintrc.json +++ b/simulator-ui/.eslintrc.json @@ -24,7 +24,7 @@ "error", { "type": "element", - "prefix": "jhi", + "prefix": ["app", "jhi"], "style": "kebab-case" } ], @@ -95,5 +95,13 @@ "object-shorthand": ["error", "always", { "avoidExplicitReturnArrows": true }], "radix": "error", "spaced-comment": ["warn", "always"] - } + }, + "overrides": [ + { + "files": ["./src/main/webapp/**/*.spec.ts"], + "rules": { + "@typescript-eslint/ban-ts-comment": "off" + } + } + ] } diff --git a/simulator-ui/.jhipster/TestParameter.json b/simulator-ui/.jhipster/TestParameter.json new file mode 100644 index 000000000..fa31d3bb4 --- /dev/null +++ b/simulator-ui/.jhipster/TestParameter.json @@ -0,0 +1,34 @@ +{ + "changelogDate": "20230926184642", + "dto": "no", + "entityTableName": "test_parameter", + "fields": [ + { + "fieldName": "key", + "fieldType": "String", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "value", + "fieldType": "String", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "createdDate", + "fieldType": "ZonedDateTime", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "lastModifiedDate", + "fieldType": "ZonedDateTime", + "fieldValidateRules": ["required"] + } + ], + "jpaMetamodelFiltering": true, + "name": "TestParameter", + "pagination": "pagination", + "readOnly": true, + "relationships": [], + "searchEngine": "no", + "service": "serviceImpl" +} diff --git a/simulator-ui/.jhipster/TestResult.json b/simulator-ui/.jhipster/TestResult.json new file mode 100644 index 000000000..52bd60d24 --- /dev/null +++ b/simulator-ui/.jhipster/TestResult.json @@ -0,0 +1,59 @@ +{ + "changelogDate": "20230926185108", + "dto": "no", + "entityTableName": "test_result", + "fields": [ + { + "fieldName": "status", + "fieldType": "Integer", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "testName", + "fieldType": "String", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "className", + "fieldType": "String", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "errorMessage", + "fieldType": "String" + }, + { + "fieldName": "failureStack", + "fieldType": "String" + }, + { + "fieldName": "failureType", + "fieldType": "String" + }, + { + "fieldName": "createdDate", + "fieldType": "ZonedDateTime", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "lastModifiedDate", + "fieldType": "ZonedDateTime", + "fieldValidateRules": ["required"] + } + ], + "jpaMetamodelFiltering": true, + "name": "TestResult", + "pagination": "pagination", + "readOnly": true, + "relationships": [ + { + "otherEntityName": "testParameter", + "otherEntityRelationshipName": "testResult", + "relationshipName": "testParameter", + "relationshipSide": "left", + "relationshipType": "one-to-many" + } + ], + "searchEngine": "no", + "service": "serviceImpl" +} diff --git a/simulator-ui/.yo-rc.json b/simulator-ui/.yo-rc.json index 0b1e33214..0cfa57807 100644 --- a/simulator-ui/.yo-rc.json +++ b/simulator-ui/.yo-rc.json @@ -8,7 +8,7 @@ "creationTimestamp": 1694794967405, "devServerPort": 4200, "enableTranslation": true, - "entities": [], + "entities": ["TestParameter", "TestResult"], "jhipsterVersion": "8.0.0-beta.3", "languages": ["en"], "microfrontend": false, diff --git a/simulator-ui/README.md b/simulator-ui/README.md index a54197486..406908214 100644 --- a/simulator-ui/README.md +++ b/simulator-ui/README.md @@ -120,7 +120,7 @@ To build the final jar and optimize the `citrus-simulator` application for produ root directory of `citrus-simulator`: ``` -./mvnw install -DfrontendSkip=false +./mvnw install -DskipFrontend=false ``` This will concatenate and minify the client CSS and JavaScript files. It will also modify `index.html` so it references diff --git a/simulator-ui/pom.xml b/simulator-ui/pom.xml index 49f898c78..87ad635d7 100644 --- a/simulator-ui/pom.xml +++ b/simulator-ui/pom.xml @@ -14,10 +14,16 @@ ${project.artifactId} - true + true + + org.citrusframework + citrus-simulator-starter + + + org.springframework.security spring-security-config @@ -27,6 +33,7 @@ spring-security-web + org.springframework.boot spring-boot-starter-actuator @@ -38,9 +45,16 @@ provided + + + org.springframework.boot + spring-boot-configuration-processor + provided + + org.citrusframework - citrus-simulator-starter + citrus-ws test @@ -55,22 +69,23 @@ maven-clean-plugin - - - node_modules - - **/* - - - + + + node_modules + + **/* + + + + com.github.eirslett frontend-maven-plugin target - ${frontendSkip} + ${skipFrontend} @@ -114,11 +129,42 @@ + + + org.apache.maven.plugins + maven-surefire-plugin + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + + + + + development @@ -138,9 +184,9 @@ ui-node-modules-skip-clean diff --git a/simulator-ui/src/main/java/org/citrusframework/simulator/ui/config/SecurityConfiguration.java b/simulator-ui/src/main/java/org/citrusframework/simulator/ui/config/SecurityConfiguration.java index 58eae8e47..53ccaafa1 100644 --- a/simulator-ui/src/main/java/org/citrusframework/simulator/ui/config/SecurityConfiguration.java +++ b/simulator-ui/src/main/java/org/citrusframework/simulator/ui/config/SecurityConfiguration.java @@ -1,45 +1,82 @@ package org.citrusframework.simulator.ui.config; -import org.citrusframework.simulator.ui.filter.CookieCsrfFilter; +import jakarta.annotation.Nullable; +import jakarta.servlet.http.HttpServlet; +import org.citrusframework.simulator.http.SimulatorRestAdapter; +import org.citrusframework.simulator.http.SimulatorRestConfigurationProperties; import org.citrusframework.simulator.ui.filter.SpaWebFilter; +import org.citrusframework.simulator.ws.SimulatorWebServiceConfigurationProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; -import org.springframework.security.web.csrf.CookieCsrfTokenRepository; -import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; +import org.springframework.security.web.util.matcher.OrRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; -import static org.springframework.security.config.Customizer.withDefaults; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; @Configuration public class SecurityConfiguration { private final SimulatorUiConfigurationProperties simulatorUiConfigurationProperties; - public SecurityConfiguration(SimulatorUiConfigurationProperties simulatorUiConfigurationProperties) { + private final @Nullable SimulatorRestConfigurationProperties simulatorRestConfigurationProperties; + private final @Nullable SimulatorRestAdapter simulatorRestAdapter; + private final @Nullable SimulatorWebServiceConfigurationProperties simulatorWebServiceConfigurationProperties; + private final @Nullable ServletRegistrationBean simulatorServletRegistrationBean; + + public SecurityConfiguration( + SimulatorUiConfigurationProperties simulatorUiConfigurationProperties, + @Autowired(required = false) @Nullable SimulatorRestConfigurationProperties simulatorRestConfigurationProperties, + @Autowired(required = false) @Nullable SimulatorRestAdapter simulatorRestAdapter, + @Autowired(required = false) @Nullable SimulatorWebServiceConfigurationProperties simulatorWebServiceConfigurationProperties, + @Autowired(required = false) @Nullable @Qualifier("simulatorServletRegistrationBean") ServletRegistrationBean simulatorServletRegistrationBean) { this.simulatorUiConfigurationProperties = simulatorUiConfigurationProperties; + + this.simulatorRestConfigurationProperties = simulatorRestConfigurationProperties; + this.simulatorRestAdapter = simulatorRestAdapter; + this.simulatorWebServiceConfigurationProperties = simulatorWebServiceConfigurationProperties; + this.simulatorServletRegistrationBean = simulatorServletRegistrationBean; + } + + private static void addPathWithinApplicationAwareServletMatchers(MvcRequestMatcher.Builder mvc, String urlMapping, List requestMatchers) { + String pathWithinApplication = urlMapping.substring(ServletUtils.extractContextPath(urlMapping).length()); + if (!pathWithinApplication.isEmpty()) { + requestMatchers.add(mvc.pattern(pathWithinApplication)); + } else { + requestMatchers.add(mvc.pattern(urlMapping)); + } } @Bean public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception { + RequestMatcher simulationEndpointsRequestMatcher = getSimulationEndpointsRequestMatcher(mvc); + http - .cors(withDefaults()) - .csrf(csrf -> - csrf - .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) - // See https://stackoverflow.com/q/74447118/65681 - .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()) - ) - .addFilterAfter(new SpaWebFilter(), BasicAuthenticationFilter.class) - .addFilterAfter(new CookieCsrfFilter(), BasicAuthenticationFilter.class) + .cors(AbstractHttpConfigurer::disable) + + // TODO: https://github.com/citrusframework/citrus-simulator/issues/21 + // .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) + // See https://stackoverflow.com/q/74447118/65681 + // .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()) + // .ignoringRequestMatchers(simulationEndpointsRequestMatcher) + .csrf(AbstractHttpConfigurer::disable) + + .addFilterAfter(new SpaWebFilter(simulationEndpointsRequestMatcher), BasicAuthenticationFilter.class) .headers(headers -> headers - .contentSecurityPolicy(csp -> csp.policyDirectives(simulatorUiConfigurationProperties.getSecurity().getContentSecurityPolicy())) + .contentSecurityPolicy(contentSecurity -> contentSecurity.policyDirectives(simulatorUiConfigurationProperties.getSecurity().getContentSecurityPolicy())) .frameOptions(FrameOptionsConfig::sameOrigin) .referrerPolicy(referrer -> referrer.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)) .permissionsPolicy(permissions -> @@ -56,6 +93,25 @@ public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Buil return http.build(); } + private RequestMatcher getSimulationEndpointsRequestMatcher(MvcRequestMatcher.Builder mvc) { + List requestMatchers = new ArrayList<>(); + + if (!Objects.isNull(simulatorRestConfigurationProperties) && !Objects.isNull(simulatorRestAdapter)) { + requestMatchers.add(mvc.pattern(simulatorRestAdapter.urlMapping(simulatorRestConfigurationProperties))); + } else if (!Objects.isNull(simulatorRestConfigurationProperties)) { + requestMatchers.add(mvc.pattern(simulatorRestConfigurationProperties.getUrlMapping())); + } + if (!Objects.isNull(simulatorServletRegistrationBean) && simulatorServletRegistrationBean.isEnabled()) { + simulatorServletRegistrationBean.getUrlMappings().forEach(urlMapping -> addPathWithinApplicationAwareServletMatchers(mvc, urlMapping, requestMatchers)); + } + + if (requestMatchers.isEmpty()) { + requestMatchers.add(mvc.pattern("*")); + } + + return new OrRequestMatcher(requestMatchers.toArray(new RequestMatcher[0])); + } + @Bean MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) { return new MvcRequestMatcher.Builder(introspector); diff --git a/simulator-ui/src/main/java/org/citrusframework/simulator/ui/config/ServletUtils.java b/simulator-ui/src/main/java/org/citrusframework/simulator/ui/config/ServletUtils.java new file mode 100644 index 000000000..0592cf7e7 --- /dev/null +++ b/simulator-ui/src/main/java/org/citrusframework/simulator/ui/config/ServletUtils.java @@ -0,0 +1,22 @@ +package org.citrusframework.simulator.ui.config; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class ServletUtils { + + private static final String CONTEXT_PATH_PATTERN_STRING = "(.*?)(/\\*\\*?(/\\*)?)?$"; + private static final Pattern CONTEXT_PATH_PATTERN = Pattern.compile(CONTEXT_PATH_PATTERN_STRING); + + private ServletUtils() { + // Static utility class + } + + static CharSequence extractContextPath(CharSequence urlMapping) { + Matcher matcher = CONTEXT_PATH_PATTERN.matcher(urlMapping); + if (matcher.matches()) { + return matcher.group(1); + } + return urlMapping; + } +} diff --git a/simulator-ui/src/main/java/org/citrusframework/simulator/ui/filter/CookieCsrfFilter.java b/simulator-ui/src/main/java/org/citrusframework/simulator/ui/filter/CookieCsrfFilter.java deleted file mode 100644 index 755275ac9..000000000 --- a/simulator-ui/src/main/java/org/citrusframework/simulator/ui/filter/CookieCsrfFilter.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.citrusframework.simulator.ui.filter; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.web.csrf.CsrfToken; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; - -/** - * Spring Security 6 doesn't set a XSRF-TOKEN cookie by default. This solution is - * recommended by Spring Security. - */ -public class CookieCsrfFilter extends OncePerRequestFilter { - - /** - * {@inheritDoc} - */ - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); - response.setHeader(csrfToken.getHeaderName(), csrfToken.getToken()); - filterChain.doFilter(request, response); - } -} diff --git a/simulator-ui/src/main/java/org/citrusframework/simulator/ui/filter/SpaWebFilter.java b/simulator-ui/src/main/java/org/citrusframework/simulator/ui/filter/SpaWebFilter.java index 73f9ee7fc..ca0b8ad04 100644 --- a/simulator-ui/src/main/java/org/citrusframework/simulator/ui/filter/SpaWebFilter.java +++ b/simulator-ui/src/main/java/org/citrusframework/simulator/ui/filter/SpaWebFilter.java @@ -4,26 +4,27 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; public class SpaWebFilter extends OncePerRequestFilter { + private final RequestMatcher simulatorRestRequestMatcher; + + public SpaWebFilter(RequestMatcher simulatorRestRequestMatcher) { + this.simulatorRestRequestMatcher = simulatorRestRequestMatcher; + } + /** * Forwards any unmapped paths (except those containing a period) to the client {@code index.html}. */ @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - // Request URI includes the contextPath if any, removed it. + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // Request URI includes the contextPath: if any, removed it. String path = request.getRequestURI().substring(request.getContextPath().length()); - if ( - !path.startsWith("/api") && - !path.startsWith("/v3/api-docs") && - !path.contains(".") && - path.matches("/(.*)") - ) { + if (!path.startsWith("/api") && !path.startsWith("/v3/api-docs") && !path.contains(".") && !simulatorRestRequestMatcher.matches(request) && path.matches("/(.*)")) { request.getRequestDispatcher("/index.html").forward(request, response); return; } diff --git a/simulator-ui/src/main/webapp/app/config/font-awesome-icons.ts b/simulator-ui/src/main/webapp/app/config/font-awesome-icons.ts index 40c449bb2..2d445905b 100644 --- a/simulator-ui/src/main/webapp/app/config/font-awesome-icons.ts +++ b/simulator-ui/src/main/webapp/app/config/font-awesome-icons.ts @@ -6,6 +6,7 @@ import { faBell, faBook, faCalendarAlt, + faChartPie, faCheck, faCloud, faCogs, @@ -50,6 +51,7 @@ export const fontAwesomeIcons = [ faBell, faBook, faCalendarAlt, + faChartPie, faCheck, faCloud, faCogs, diff --git a/simulator-ui/src/main/webapp/app/core/config/application-config.service.spec.ts b/simulator-ui/src/main/webapp/app/core/config/application-config.service.spec.ts index 4451c9bb8..0d2e00266 100644 --- a/simulator-ui/src/main/webapp/app/core/config/application-config.service.spec.ts +++ b/simulator-ui/src/main/webapp/app/core/config/application-config.service.spec.ts @@ -18,10 +18,6 @@ describe('ApplicationConfigService', () => { it('should return correctly', () => { expect(service.getEndpointFor('api')).toEqual('api'); }); - - it('should return correctly when passing microservice', () => { - expect(service.getEndpointFor('api', 'microservice')).toEqual('services/microservice/api'); - }); }); describe('with prefix', () => { @@ -32,9 +28,5 @@ describe('ApplicationConfigService', () => { it('should return correctly', () => { expect(service.getEndpointFor('api')).toEqual('prefix/api'); }); - - it('should return correctly when passing microservice', () => { - expect(service.getEndpointFor('api', 'microservice')).toEqual('prefix/services/microservice/api'); - }); }); }); diff --git a/simulator-ui/src/main/webapp/app/core/config/application-config.service.ts b/simulator-ui/src/main/webapp/app/core/config/application-config.service.ts index 0102e5f03..7c1b86d70 100644 --- a/simulator-ui/src/main/webapp/app/core/config/application-config.service.ts +++ b/simulator-ui/src/main/webapp/app/core/config/application-config.service.ts @@ -5,24 +5,12 @@ import { Injectable } from '@angular/core'; }) export class ApplicationConfigService { private endpointPrefix = ''; - private microfrontend = false; setEndpointPrefix(endpointPrefix: string): void { this.endpointPrefix = endpointPrefix; } - setMicrofrontend(microfrontend = true): void { - this.microfrontend = microfrontend; - } - - isMicrofrontend(): boolean { - return this.microfrontend; - } - - getEndpointFor(api: string, microservice?: string): string { - if (microservice) { - return `${this.endpointPrefix}services/${microservice}/${api}`; - } + getEndpointFor(api: string): string { return `${this.endpointPrefix}${api}`; } } diff --git a/simulator-ui/src/main/webapp/app/core/util/alert.service.spec.ts b/simulator-ui/src/main/webapp/app/core/util/alert.service.spec.ts index 3a7980e15..1a52a0906 100644 --- a/simulator-ui/src/main/webapp/app/core/util/alert.service.spec.ts +++ b/simulator-ui/src/main/webapp/app/core/util/alert.service.spec.ts @@ -101,10 +101,10 @@ describe('Alert service test', () => { it('should produce an alert object with correct id', inject([AlertService], (service: AlertService) => { service.addAlert({ type: 'info', message: 'Hello Citrus info' }); - expect(service.addAlert({ type: 'success', message: 'Hello Citrus success' })).toEqual( + expect(service.addAlert({ type: 'success', message: 'Hello Citrus succeed' })).toEqual( expect.objectContaining({ type: 'success', - message: 'Hello Citrus success', + message: 'Hello Citrus succeed', id: 1, } as Alert), ); @@ -113,7 +113,7 @@ describe('Alert service test', () => { expect(service.get()[1]).toEqual( expect.objectContaining({ type: 'success', - message: 'Hello Citrus success', + message: 'Hello Citrus succeed', id: 1, } as Alert), ); @@ -122,11 +122,11 @@ describe('Alert service test', () => { it('should close an alert correctly', inject([AlertService], (service: AlertService) => { const alert0 = service.addAlert({ type: 'info', message: 'Hello Citrus info' }); const alert1 = service.addAlert({ type: 'info', message: 'Hello Citrus info 2' }); - const alert2 = service.addAlert({ type: 'success', message: 'Hello Citrus success' }); + const alert2 = service.addAlert({ type: 'success', message: 'Hello Citrus succeed' }); expect(alert2).toEqual( expect.objectContaining({ type: 'success', - message: 'Hello Citrus success', + message: 'Hello Citrus succeed', id: 2, } as Alert), ); @@ -146,7 +146,7 @@ describe('Alert service test', () => { expect(service.get()[0]).not.toEqual( expect.objectContaining({ type: 'success', - message: 'Hello Citrus success', + message: 'Hello Citrus succeed', id: 2, } as Alert), ); @@ -199,7 +199,7 @@ describe('Alert service test', () => { expect(service.get().length).toBe(0); })); - it('should produce a success message', inject([AlertService], (service: AlertService) => { + it('should produce a succeed message', inject([AlertService], (service: AlertService) => { expect(service.addAlert({ type: 'success', message: 'Hello Citrus' })).toEqual( expect.objectContaining({ type: 'success', @@ -208,7 +208,7 @@ describe('Alert service test', () => { ); })); - it('should produce a success message with custom position', inject([AlertService], (service: AlertService) => { + it('should produce a succeed message with custom position', inject([AlertService], (service: AlertService) => { expect(service.addAlert({ type: 'success', message: 'Hello Citrus', position: 'bottom left' })).toEqual( expect.objectContaining({ type: 'success', diff --git a/simulator-ui/src/main/webapp/app/entities/entity-routing.module.ts b/simulator-ui/src/main/webapp/app/entities/entity-routing.module.ts index fe1354ddd..ba8bd679b 100644 --- a/simulator-ui/src/main/webapp/app/entities/entity-routing.module.ts +++ b/simulator-ui/src/main/webapp/app/entities/entity-routing.module.ts @@ -4,6 +4,16 @@ import { RouterModule } from '@angular/router'; @NgModule({ imports: [ RouterModule.forChild([ + { + path: 'test-parameter', + data: { pageTitle: 'citrusSimulatorApp.testParameter.home.title' }, + loadChildren: () => import('./test-parameter/test-parameter.routes'), + }, + { + path: 'test-result', + data: { pageTitle: 'citrusSimulatorApp.testResult.home.title' }, + loadChildren: () => import('./test-result/test-result.routes'), + }, /* jhipster-needle-add-entity-route - JHipster will add entity modules routes here */ ]), ], diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/detail/test-parameter-detail.component.html b/simulator-ui/src/main/webapp/app/entities/test-parameter/detail/test-parameter-detail.component.html new file mode 100644 index 000000000..a91a38d47 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/detail/test-parameter-detail.component.html @@ -0,0 +1,42 @@ +

+
+
+

+ Test Parameter +

+ +
+ + + + + +
+
Key
+
+ {{ testParameter.key }} +
+
Value
+
+ {{ testParameter.value }} +
+
Created Date
+
+ {{ testParameter.createdDate | formatMediumDatetime }} +
+
Last Modified Date
+
+ {{ testParameter.lastModifiedDate | formatMediumDatetime }} +
+
Test Result
+
+ {{ testParameter.testResult.id }} +
+
+ + +
+
+
diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/detail/test-parameter-detail.component.spec.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/detail/test-parameter-detail.component.spec.ts new file mode 100644 index 000000000..3e4057e8b --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/detail/test-parameter-detail.component.spec.ts @@ -0,0 +1,38 @@ +import { TestBed } from '@angular/core/testing'; +import { provideRouter, withComponentInputBinding } from '@angular/router'; +import { RouterTestingHarness, RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { TestParameterDetailComponent } from './test-parameter-detail.component'; + +describe('TestParameter Management Detail Component', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TestParameterDetailComponent, RouterTestingModule.withRoutes([], { bindToComponentInputs: true })], + providers: [ + provideRouter( + [ + { + path: '**', + component: TestParameterDetailComponent, + resolve: { testParameter: () => of({ id: 123 }) }, + }, + ], + withComponentInputBinding(), + ), + ], + }) + .overrideTemplate(TestParameterDetailComponent, '') + .compileComponents(); + }); + + describe('OnInit', () => { + it('Should load testParameter on init', async () => { + const harness = await RouterTestingHarness.create(); + const instance = await harness.navigateByUrl('/', TestParameterDetailComponent); + + // THEN + expect(instance.testParameter).toEqual(expect.objectContaining({ id: 123 })); + }); + }); +}); diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/detail/test-parameter-detail.component.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/detail/test-parameter-detail.component.ts new file mode 100644 index 000000000..20f86d5bb --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/detail/test-parameter-detail.component.ts @@ -0,0 +1,22 @@ +import { Component, Input } from '@angular/core'; +import { ActivatedRoute, RouterModule } from '@angular/router'; + +import SharedModule from 'app/shared/shared.module'; +import { DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe } from 'app/shared/date'; +import { ITestParameter } from '../test-parameter.model'; + +@Component({ + standalone: true, + selector: 'jhi-test-parameter-detail', + templateUrl: './test-parameter-detail.component.html', + imports: [SharedModule, RouterModule, DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe], +}) +export class TestParameterDetailComponent { + @Input() testParameter: ITestParameter | null = null; + + constructor(protected activatedRoute: ActivatedRoute) {} + + previousState(): void { + window.history.back(); + } +} diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/list/test-parameter.component.html b/simulator-ui/src/main/webapp/app/entities/test-parameter/list/test-parameter.component.html new file mode 100644 index 000000000..96648b2e1 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/list/test-parameter.component.html @@ -0,0 +1,104 @@ +
+

+ Test Parameters + +
+ +
+

+ + + + + + + +
+ No Test Parameters found +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+
+ Key + +
+
+
+ Value + +
+
+
+ Created Date + +
+
+
+ Last Modified Date + +
+
+
+ Test Result + +
+
{{ testParameter.key }}{{ testParameter.value }}{{ testParameter.createdDate | formatMediumDatetime }}{{ testParameter.lastModifiedDate | formatMediumDatetime }} + {{ testParameter.testResult.id }} + +
+ +
+
+
+ +
+
+ +
+ +
+ +
+
+
diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/list/test-parameter.component.spec.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/list/test-parameter.component.spec.ts new file mode 100644 index 000000000..b0fb437ef --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/list/test-parameter.component.spec.ts @@ -0,0 +1,125 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpHeaders, HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { TestParameterService } from '../service/test-parameter.service'; + +import { TestParameterComponent } from './test-parameter.component'; +import SpyInstance = jest.SpyInstance; + +describe('TestParameter Management Component', () => { + let comp: TestParameterComponent; + let fixture: ComponentFixture; + let service: TestParameterService; + let routerNavigateSpy: SpyInstance>; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + RouterTestingModule.withRoutes([{ path: 'test-parameter', component: TestParameterComponent }]), + HttpClientTestingModule, + TestParameterComponent, + ], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: of({ + defaultSort: 'createdDate,desc', + }), + queryParamMap: of( + jest.requireActual('@angular/router').convertToParamMap({ + page: '1', + size: '1', + sort: 'createdDate,desc', + 'filter[someKey.in]': 'dc4279ea-cfb9-11ec-9d64-0242ac120002', + }), + ), + snapshot: { queryParams: {} }, + }, + }, + ], + }) + .overrideTemplate(TestParameterComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(TestParameterComponent); + comp = fixture.componentInstance; + service = TestBed.inject(TestParameterService); + routerNavigateSpy = jest.spyOn(comp.router, 'navigate'); + + const headers = new HttpHeaders(); + jest.spyOn(service, 'query').mockReturnValue( + of( + new HttpResponse({ + body: [{ key: 'key', testResult: { id: 123 } }], + headers, + }), + ), + ); + }); + + it('Should call load all on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenCalled(); + expect(comp.testParameters?.[0]).toEqual(expect.objectContaining({ key: 'key', testResult: { id: 123 } })); + }); + + describe('trackId', () => { + it('Should forward to testParameterService', () => { + const entity = { key: 'key', testResult: { id: 123 } }; + jest.spyOn(service, 'getTestParameterIdentifier'); + const id = comp.trackId(0, entity); + expect(service.getTestParameterIdentifier).toHaveBeenCalledWith(entity); + expect(id).toBe(2018011204); // This is a hash of the composite primary key + }); + }); + + it('should load a page', () => { + // WHEN + comp.navigateToPage(1); + + // THEN + expect(routerNavigateSpy).toHaveBeenCalled(); + }); + + it('should calculate the sort attribute for an id', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenLastCalledWith(expect.objectContaining({ sort: ['createdDate,desc'] })); + }); + + it('should calculate the sort attribute for a non-id attribute', () => { + // GIVEN + comp.predicate = 'name'; + + // WHEN + comp.navigateToWithComponentValues(); + + // THEN + expect(routerNavigateSpy).toHaveBeenLastCalledWith( + expect.anything(), + expect.objectContaining({ + queryParams: expect.objectContaining({ + sort: ['name,asc'], + }), + }), + ); + }); + + it('should calculate the filter attribute', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenLastCalledWith(expect.objectContaining({ 'someKey.in': ['dc4279ea-cfb9-11ec-9d64-0242ac120002'] })); + }); +}); diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/list/test-parameter.component.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/list/test-parameter.component.ts new file mode 100644 index 000000000..06dc18098 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/list/test-parameter.component.ts @@ -0,0 +1,154 @@ +import { Component, NgZone, OnInit } from '@angular/core'; +import { HttpHeaders } from '@angular/common/http'; +import { ActivatedRoute, Data, ParamMap, Router, RouterModule } from '@angular/router'; +import { combineLatest, Observable, switchMap, tap } from 'rxjs'; + +import SharedModule from 'app/shared/shared.module'; +import { SortDirective, SortByDirective } from 'app/shared/sort'; +import { DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe } from 'app/shared/date'; +import { ItemCountComponent } from 'app/shared/pagination'; +import { FormsModule } from '@angular/forms'; + +import { ITEMS_PER_PAGE, PAGE_HEADER, TOTAL_COUNT_RESPONSE_HEADER } from 'app/config/pagination.constants'; +import { ASC, DESC, SORT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants'; +import { FilterComponent, FilterOptions, IFilterOptions, IFilterOption } from 'app/shared/filter'; +import { EntityArrayResponseType, TestParameterService } from '../service/test-parameter.service'; +import { ITestParameter } from '../test-parameter.model'; + +@Component({ + standalone: true, + selector: 'jhi-test-parameter', + templateUrl: './test-parameter.component.html', + imports: [ + RouterModule, + FormsModule, + SharedModule, + SortDirective, + SortByDirective, + DurationPipe, + FormatMediumDatetimePipe, + FormatMediumDatePipe, + FilterComponent, + ItemCountComponent, + ], +}) +export class TestParameterComponent implements OnInit { + testParameters?: ITestParameter[]; + isLoading = false; + + predicate = 'createdDate'; + ascending = true; + filters: IFilterOptions = new FilterOptions(); + + itemsPerPage = ITEMS_PER_PAGE; + totalItems = 0; + page = 1; + + constructor( + protected testParameterService: TestParameterService, + protected activatedRoute: ActivatedRoute, + private ngZone: NgZone, + public router: Router, + ) {} + + trackId = (_index: number, item: ITestParameter): number => this.testParameterService.getTestParameterIdentifier(item); + + ngOnInit(): void { + this.load(); + + this.filters.filterChanges.subscribe(filterOptions => this.handleNavigation(1, this.predicate, this.ascending, filterOptions)); + } + + load(): void { + this.loadFromBackendWithRouteInformations().subscribe({ + next: (res: EntityArrayResponseType) => { + this.onResponseSuccess(res); + }, + }); + } + + navigateToWithComponentValues(): void { + this.handleNavigation(this.page, this.predicate, this.ascending, this.filters.filterOptions); + } + + navigateToPage(page = this.page): void { + this.handleNavigation(page, this.predicate, this.ascending, this.filters.filterOptions); + } + + protected loadFromBackendWithRouteInformations(): Observable { + return combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data]).pipe( + tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)), + switchMap(() => this.queryBackend(this.page, this.predicate, this.ascending, this.filters.filterOptions)), + ); + } + + protected fillComponentAttributeFromRoute(params: ParamMap, data: Data): void { + const page = params.get(PAGE_HEADER); + this.page = +(page ?? 1); + const sort = (params.get(SORT) ?? data[DEFAULT_SORT_DATA]).split(','); + this.predicate = sort[0]; + this.ascending = sort[1] === ASC; + this.filters.initializeFromParams(params); + } + + protected onResponseSuccess(response: EntityArrayResponseType): void { + this.fillComponentAttributesFromResponseHeader(response.headers); + const dataFromBody = this.fillComponentAttributesFromResponseBody(response.body); + this.testParameters = dataFromBody; + } + + protected fillComponentAttributesFromResponseBody(data: ITestParameter[] | null): ITestParameter[] { + return data ?? []; + } + + protected fillComponentAttributesFromResponseHeader(headers: HttpHeaders): void { + this.totalItems = Number(headers.get(TOTAL_COUNT_RESPONSE_HEADER)); + } + + protected queryBackend( + page?: number, + predicate?: string, + ascending?: boolean, + filterOptions?: IFilterOption[], + ): Observable { + this.isLoading = true; + const pageToLoad: number = page ?? 1; + const queryObject: any = { + page: pageToLoad - 1, + size: this.itemsPerPage, + sort: this.getSortQueryParam(predicate, ascending), + }; + filterOptions?.forEach(filterOption => { + queryObject[filterOption.name] = filterOption.values; + }); + return this.testParameterService.query(queryObject).pipe(tap(() => (this.isLoading = false))); + } + + protected handleNavigation(page = this.page, predicate?: string, ascending?: boolean, filterOptions?: IFilterOption[]): void { + const queryParamsObj: any = { + page, + size: this.itemsPerPage, + sort: this.getSortQueryParam(predicate, ascending), + }; + + filterOptions?.forEach(filterOption => { + queryParamsObj[filterOption.nameAsQueryParam()] = filterOption.values; + }); + + this.ngZone.run(() => + this.router.navigate(['./'], { + relativeTo: this.activatedRoute, + queryParams: queryParamsObj, + }), + ); + } + + protected getSortQueryParam(predicate = this.predicate, ascending = this.ascending): string[] { + const ascendingQueryParam = ascending ? ASC : DESC; + if (predicate === '') { + return []; + } else { + return [predicate + ',' + ascendingQueryParam]; + } + } +} diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/route/test-parameter-routing-resolve.service.spec.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/route/test-parameter-routing-resolve.service.spec.ts new file mode 100644 index 000000000..64e6160bb --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/route/test-parameter-routing-resolve.service.spec.ts @@ -0,0 +1,101 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRouteSnapshot, ActivatedRoute, Router, convertToParamMap } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { ITestParameter } from '../test-parameter.model'; +import { TestParameterService } from '../service/test-parameter.service'; + +import testParameterResolve from './test-parameter-routing-resolve.service'; + +describe('TestParameter routing resolve service', () => { + let mockRouter: Router; + let mockActivatedRouteSnapshot: ActivatedRouteSnapshot; + let service: TestParameterService; + let resultTestParameter: ITestParameter | null | undefined; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + paramMap: convertToParamMap({}), + }, + }, + }, + ], + }); + mockRouter = TestBed.inject(Router); + jest.spyOn(mockRouter, 'navigate').mockImplementation(() => Promise.resolve(true)); + mockActivatedRouteSnapshot = TestBed.inject(ActivatedRoute).snapshot; + service = TestBed.inject(TestParameterService); + resultTestParameter = undefined; + }); + + describe('resolve', () => { + it('should return ITestParameter returned by find', () => { + const expectedResult: ITestParameter = { key: 'key', testResult: { id: 123 } }; + + // GIVEN + service.find = jest.fn((testResultId, key) => of(new HttpResponse({ body: { key, testResult: { id: testResultId } } }))); + mockActivatedRouteSnapshot.params = { testResultId: expectedResult.testResult.id, key: expectedResult.key }; + + // WHEN + TestBed.runInInjectionContext(() => { + testParameterResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultTestParameter = result; + }, + }); + }); + + // THEN + expect(service.find).toBeCalledWith(expectedResult.testResult.id, expectedResult.key); + expect(resultTestParameter).toEqual(expectedResult); + }); + + it('should return null if id is not provided', () => { + // GIVEN + service.find = jest.fn(); + mockActivatedRouteSnapshot.params = {}; + + // WHEN + TestBed.runInInjectionContext(() => { + testParameterResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultTestParameter = result; + }, + }); + }); + + // THEN + expect(service.find).not.toBeCalled(); + expect(resultTestParameter).toEqual(null); + }); + + it('should route to 404 page if data not found in server', () => { + // GIVEN + jest.spyOn(service, 'find').mockReturnValue(of(new HttpResponse({ body: null }))); + mockActivatedRouteSnapshot.params = { testResultId: 123, key: 'key' }; + + // WHEN + TestBed.runInInjectionContext(() => { + testParameterResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultTestParameter = result; + }, + }); + }); + + // THEN + expect(service.find).toBeCalledWith(123, 'key'); + expect(resultTestParameter).toEqual(undefined); + expect(mockRouter.navigate).toHaveBeenCalledWith(['404']); + }); + }); +}); diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/route/test-parameter-routing-resolve.service.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/route/test-parameter-routing-resolve.service.ts new file mode 100644 index 000000000..e270ca90b --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/route/test-parameter-routing-resolve.service.ts @@ -0,0 +1,31 @@ +import { inject } from '@angular/core'; +import { HttpResponse } from '@angular/common/http'; +import { ActivatedRouteSnapshot, Router } from '@angular/router'; +import { of, EMPTY, Observable } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; + +import { ITestParameter } from '../test-parameter.model'; +import { TestParameterService } from '../service/test-parameter.service'; + +export const testParameterResolve = (route: ActivatedRouteSnapshot): Observable => { + const testResultId = route.params['testResultId']; + const key = route.params['key']; + + if (key && testResultId) { + return inject(TestParameterService) + .find(testResultId, key) + .pipe( + mergeMap((testParameter: HttpResponse) => { + if (testParameter.body) { + return of(testParameter.body); + } else { + inject(Router).navigate(['404']); + return EMPTY; + } + }), + ); + } + return of(null); +}; + +export default testParameterResolve; diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/service/test-parameter.service.spec.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/service/test-parameter.service.spec.ts new file mode 100644 index 000000000..200f748e9 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/service/test-parameter.service.spec.ts @@ -0,0 +1,165 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { ITestParameter } from '../test-parameter.model'; +import { sampleWithRequiredData, sampleWithPartialData, sampleWithFullData } from '../test-parameter.test-samples'; + +import { TestParameterService, RestTestParameter } from './test-parameter.service'; + +const requireRestSample: RestTestParameter = { + ...sampleWithRequiredData, + createdDate: sampleWithRequiredData.createdDate?.toJSON(), + lastModifiedDate: sampleWithRequiredData.lastModifiedDate?.toJSON(), +}; + +describe('TestParameter Service', () => { + let service: TestParameterService; + let httpMock: HttpTestingController; + let expectedResult: ITestParameter | ITestParameter[] | boolean | null; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + expectedResult = null; + service = TestBed.inject(TestParameterService); + httpMock = TestBed.inject(HttpTestingController); + }); + + describe('Service methods', () => { + it('should find an element', () => { + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.find(123, 'key').subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should return a list of TestParameter', () => { + const returnedFromService = { ...requireRestSample }; + + const expected = { ...sampleWithRequiredData }; + + service.query().subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush([returnedFromService]); + httpMock.verify(); + expect(expectedResult).toMatchObject([expected]); + }); + + describe('addTestParameterToCollectionIfMissing', () => { + it('should add a TestParameter to an empty array', () => { + const testParameter: ITestParameter = sampleWithRequiredData; + expectedResult = service.addTestParameterToCollectionIfMissing([], testParameter); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(testParameter); + }); + + it('should not add a TestParameter to an array that contains it', () => { + const testParameter: ITestParameter = sampleWithRequiredData; + const testParameterCollection: ITestParameter[] = [ + { + ...testParameter, + }, + sampleWithPartialData, + ]; + expectedResult = service.addTestParameterToCollectionIfMissing(testParameterCollection, testParameter); + expect(expectedResult).toHaveLength(2); + }); + + it("should add a TestParameter to an array that doesn't contain it", () => { + const testParameter: ITestParameter = sampleWithRequiredData; + const testParameterCollection: ITestParameter[] = [sampleWithPartialData]; + expectedResult = service.addTestParameterToCollectionIfMissing(testParameterCollection, testParameter); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(testParameter); + }); + + it('should add only unique TestParameter to an array', () => { + const testParameterArray: ITestParameter[] = [sampleWithRequiredData, sampleWithPartialData, sampleWithFullData]; + const testParameterCollection: ITestParameter[] = [sampleWithRequiredData]; + expectedResult = service.addTestParameterToCollectionIfMissing(testParameterCollection, ...testParameterArray); + expect(expectedResult).toHaveLength(3); + }); + + it('should accept varargs', () => { + const testParameter: ITestParameter = sampleWithRequiredData; + const testParameter2: ITestParameter = sampleWithPartialData; + expectedResult = service.addTestParameterToCollectionIfMissing([], testParameter, testParameter2); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(testParameter); + expect(expectedResult).toContain(testParameter2); + }); + + it('should accept null and undefined values', () => { + const testParameter: ITestParameter = sampleWithRequiredData; + expectedResult = service.addTestParameterToCollectionIfMissing([], null, testParameter, undefined); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(testParameter); + }); + + it('should return initial array if no TestParameter is added', () => { + const testParameterCollection: ITestParameter[] = [sampleWithRequiredData]; + expectedResult = service.addTestParameterToCollectionIfMissing(testParameterCollection, undefined, null); + expect(expectedResult).toEqual(testParameterCollection); + }); + }); + + describe('compareTestParameter', () => { + it('Should return true if both entities are null', () => { + const entity1 = null; + const entity2 = null; + + const compareResult = service.compareTestParameter(entity1, entity2); + + expect(compareResult).toEqual(true); + }); + + it('Should return false if one entity is null', () => { + const entity1 = { key: 'key', testResult: { id: 123 } }; + const entity2 = null; + + const compareResult1 = service.compareTestParameter(entity1, entity2); + const compareResult2 = service.compareTestParameter(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey differs', () => { + const entity1 = { key: 'key', testResult: { id: 123 } }; + const entity2 = { key: 'another key', testResult: { id: 123 } }; + const entity3 = { key: 'key', testResult: { id: 234 } }; + + const compareResult1 = service.compareTestParameter(entity1, entity2); + const compareResult2 = service.compareTestParameter(entity1, entity3); + const compareResult3 = service.compareTestParameter(entity2, entity1); + const compareResult4 = service.compareTestParameter(entity2, entity3); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + expect(compareResult3).toEqual(false); + expect(compareResult4).toEqual(false); + }); + + it('Should return true if primaryKey matches', () => { + const entity1 = { key: 'key', testResult: { id: 123 } }; + const entity2 = { key: 'key', testResult: { id: 123 } }; + + const compareResult1 = service.compareTestParameter(entity1, entity2); + const compareResult2 = service.compareTestParameter(entity2, entity1); + + expect(compareResult1).toEqual(true); + expect(compareResult2).toEqual(true); + }); + }); + }); + + afterEach(() => { + httpMock.verify(); + }); +}); diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/service/test-parameter.service.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/service/test-parameter.service.ts new file mode 100644 index 000000000..23981a213 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/service/test-parameter.service.ts @@ -0,0 +1,128 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { map } from 'rxjs/operators'; + +import dayjs from 'dayjs/esm'; + +import { isPresent } from 'app/core/util/operators'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { createRequestOption } from 'app/core/request/request-util'; +import { ITestParameter, NewTestParameter } from '../test-parameter.model'; + +type RestOf = Omit & { + createdDate?: string | null; + lastModifiedDate?: string | null; +}; + +export type RestTestParameter = RestOf; + +export type EntityResponseType = HttpResponse; +export type EntityArrayResponseType = HttpResponse; + +@Injectable({ providedIn: 'root' }) +export class TestParameterService { + protected resourceUrl = this.applicationConfigService.getEndpointFor('api/test-parameters'); + + constructor( + protected http: HttpClient, + protected applicationConfigService: ApplicationConfigService, + ) {} + + find(testResultId: number, key: string): Observable { + return this.http + .get(`${this.resourceUrl}/${testResultId}/${key}`, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + query(req?: any): Observable { + const options = createRequestOption(req); + return this.http + .get(this.resourceUrl, { params: options, observe: 'response' }) + .pipe(map(res => this.convertResponseArrayFromServer(res))); + } + + getTestParameterIdentifier(testParameter: Pick): number { + return this.hash((testParameter.testResult.id ? testParameter.testResult.id : 0) + '-' + testParameter.key); + } + + compareTestParameter( + o1: Pick | null, + o2: Pick | null, + ): boolean { + return o1 && o2 ? this.getTestParameterIdentifier(o1) === this.getTestParameterIdentifier(o2) : o1 === o2; + } + + addTestParameterToCollectionIfMissing>( + testParameterCollection: Type[], + ...testParametersToCheck: (Type | null | undefined)[] + ): Type[] { + const testParameters: Type[] = testParametersToCheck.filter(isPresent); + if (testParameters.length > 0) { + const testParameterCollectionIdentifiers = testParameterCollection.map( + testParameterItem => this.getTestParameterIdentifier(testParameterItem)!, + ); + const testParametersToAdd = testParameters.filter(testParameterItem => { + const testParameterIdentifier = this.getTestParameterIdentifier(testParameterItem); + if (testParameterCollectionIdentifiers.includes(testParameterIdentifier)) { + return false; + } + testParameterCollectionIdentifiers.push(testParameterIdentifier); + return true; + }); + return [...testParametersToAdd, ...testParameterCollection]; + } + return testParameterCollection; + } + + protected convertDateFromClient(testParameter: T): RestOf { + return { + ...testParameter, + createdDate: testParameter.createdDate?.toJSON() ?? null, + lastModifiedDate: testParameter.lastModifiedDate?.toJSON() ?? null, + }; + } + + protected convertDateFromServer(restTestParameter: RestTestParameter): ITestParameter { + return { + ...restTestParameter, + createdDate: restTestParameter.createdDate ? dayjs(restTestParameter.createdDate) : undefined, + lastModifiedDate: restTestParameter.lastModifiedDate ? dayjs(restTestParameter.lastModifiedDate) : undefined, + }; + } + + protected convertResponseFromServer(res: HttpResponse): HttpResponse { + return res.clone({ + body: res.body ? this.convertDateFromServer(res.body) : null, + }); + } + + protected convertResponseArrayFromServer(res: HttpResponse): HttpResponse { + return res.clone({ + body: res.body ? res.body.map(item => this.convertDateFromServer(item)) : null, + }); + } + + /** + * Compute a "java-like" hash from the given string. Required because we cannot simply return a numerical identifier + * for the {@link ITestParameter} type: It uses a composed primary key. + * + * @param composedKey The composed key: `{@link ITestParameter#testResult#id} + '-' + {@link ITestResult#key}` + * @private + */ + private hash(composedKey: string): number { + let hash = 0; + if (composedKey.length === 0) { + return hash; + } + for (let i = 0; i < composedKey.length; i++) { + const char = composedKey.charCodeAt(i); + // eslint-disable-next-line no-bitwise + hash = (hash << 5) - hash + char; + // eslint-disable-next-line no-bitwise + hash |= 0; // Convert to 32bit integer + } + return hash; + } +} diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/test-parameter.model.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/test-parameter.model.ts new file mode 100644 index 000000000..7687c730f --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/test-parameter.model.ts @@ -0,0 +1,12 @@ +import dayjs from 'dayjs/esm'; +import { ITestResult } from 'app/entities/test-result/test-result.model'; + +export interface ITestParameter { + key?: string | null; + value?: string | null; + createdDate?: dayjs.Dayjs | null; + lastModifiedDate?: dayjs.Dayjs | null; + testResult: Pick; +} + +export type NewTestParameter = ITestParameter; diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/test-parameter.routes.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/test-parameter.routes.ts new file mode 100644 index 000000000..4761c0c82 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/test-parameter.routes.ts @@ -0,0 +1,25 @@ +import { Routes } from '@angular/router'; + +import { ASC } from 'app/config/navigation.constants'; +import { TestParameterComponent } from './list/test-parameter.component'; +import { TestParameterDetailComponent } from './detail/test-parameter-detail.component'; +import TestParameterResolve from './route/test-parameter-routing-resolve.service'; + +const testParameterRoute: Routes = [ + { + path: '', + component: TestParameterComponent, + data: { + defaultSort: 'createdDate,' + ASC, + }, + }, + { + path: ':testResultId/:key/view', + component: TestParameterDetailComponent, + resolve: { + testParameter: TestParameterResolve, + }, + }, +]; + +export default testParameterRoute; diff --git a/simulator-ui/src/main/webapp/app/entities/test-parameter/test-parameter.test-samples.ts b/simulator-ui/src/main/webapp/app/entities/test-parameter/test-parameter.test-samples.ts new file mode 100644 index 000000000..b0a4d6808 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-parameter/test-parameter.test-samples.ts @@ -0,0 +1,48 @@ +import dayjs from 'dayjs/esm'; + +import { ITestParameter, NewTestParameter } from './test-parameter.model'; + +export const sampleWithRequiredData: ITestParameter = { + key: 'indeed on chance', + value: 'too junior', + createdDate: dayjs('2023-09-26T08:48'), + lastModifiedDate: dayjs('2023-09-26T12:17'), + testResult: { + id: 79374, + }, +}; + +export const sampleWithPartialData: ITestParameter = { + key: 'gel supposing for', + value: 'yuck whereas as', + createdDate: dayjs('2023-09-26T16:06'), + lastModifiedDate: dayjs('2023-09-26T06:13'), + testResult: { + id: 84283, + }, +}; + +export const sampleWithFullData: ITestParameter = { + key: 'inter hm ew', + value: 'gently', + createdDate: dayjs('2023-09-26T13:02'), + lastModifiedDate: dayjs('2023-09-26T17:35'), + testResult: { + id: 15826, + }, +}; + +export const sampleWithNewData: NewTestParameter = { + key: 'righteously yet likewise', + value: 'memorable', + createdDate: dayjs('2023-09-26T11:57'), + lastModifiedDate: dayjs('2023-09-26T17:27'), + testResult: { + id: 84692, + }, +}; + +Object.freeze(sampleWithNewData); +Object.freeze(sampleWithRequiredData); +Object.freeze(sampleWithPartialData); +Object.freeze(sampleWithFullData); diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/detail/test-result-detail.component.html b/simulator-ui/src/main/webapp/app/entities/test-result/detail/test-result-detail.component.html new file mode 100644 index 000000000..d8218a1fe --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/detail/test-result-detail.component.html @@ -0,0 +1,56 @@ +
+
+
+

Test Result

+ +
+ + + + + +
+
ID
+
+ {{ testResult.id }} +
+
Status
+
+ {{ testResult.status }} +
+
Test Name
+
+ {{ testResult.testName }} +
+
Class Name
+
+ {{ testResult.className }} +
+
Error Message
+
+ {{ testResult.errorMessage }} +
+
Failure Stack
+
+ {{ testResult.failureStack }} +
+
Failure Type
+
+ {{ testResult.failureType }} +
+
Created Date
+
+ {{ testResult.createdDate | formatMediumDatetime }} +
+
Last Modified Date
+
+ {{ testResult.lastModifiedDate | formatMediumDatetime }} +
+
+ + +
+
+
diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/detail/test-result-detail.component.spec.ts b/simulator-ui/src/main/webapp/app/entities/test-result/detail/test-result-detail.component.spec.ts new file mode 100644 index 000000000..94364f117 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/detail/test-result-detail.component.spec.ts @@ -0,0 +1,38 @@ +import { TestBed } from '@angular/core/testing'; +import { provideRouter, withComponentInputBinding } from '@angular/router'; +import { RouterTestingHarness, RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { TestResultDetailComponent } from './test-result-detail.component'; + +describe('TestResult Management Detail Component', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TestResultDetailComponent, RouterTestingModule.withRoutes([], { bindToComponentInputs: true })], + providers: [ + provideRouter( + [ + { + path: '**', + component: TestResultDetailComponent, + resolve: { testResult: () => of({ id: 123 }) }, + }, + ], + withComponentInputBinding(), + ), + ], + }) + .overrideTemplate(TestResultDetailComponent, '') + .compileComponents(); + }); + + describe('OnInit', () => { + it('Should load testResult on init', async () => { + const harness = await RouterTestingHarness.create(); + const instance = await harness.navigateByUrl('/', TestResultDetailComponent); + + // THEN + expect(instance.testResult).toEqual(expect.objectContaining({ id: 123 })); + }); + }); +}); diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/detail/test-result-detail.component.ts b/simulator-ui/src/main/webapp/app/entities/test-result/detail/test-result-detail.component.ts new file mode 100644 index 000000000..34019db86 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/detail/test-result-detail.component.ts @@ -0,0 +1,22 @@ +import { Component, Input } from '@angular/core'; +import { ActivatedRoute, RouterModule } from '@angular/router'; + +import SharedModule from 'app/shared/shared.module'; +import { DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe } from 'app/shared/date'; +import { ITestResult } from '../test-result.model'; + +@Component({ + standalone: true, + selector: 'jhi-test-result-detail', + templateUrl: './test-result-detail.component.html', + imports: [SharedModule, RouterModule, DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe], +}) +export class TestResultDetailComponent { + @Input() testResult: ITestResult | null = null; + + constructor(protected activatedRoute: ActivatedRoute) {} + + previousState(): void { + window.history.back(); + } +} diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/list/test-result.component.html b/simulator-ui/src/main/webapp/app/entities/test-result/list/test-result.component.html new file mode 100644 index 000000000..9741b3ee4 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/list/test-result.component.html @@ -0,0 +1,147 @@ +
+

+ Test Results + +
+ +
+

+ + + + + + + +
+ No Test Results found +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ ID + +
+
+
+ Status + +
+
+
+ Test Name + +
+
+
+ Class Name + +
+
+
+ Error Message + +
+
+
+ Failure Stack + +
+
+
+ Failure Type + +
+
+
+ Created Date + +
+
+
+ Last Modified Date + +
+
+ {{ testResult.id }} + {{ testResult.status }}{{ testResult.testName }}{{ testResult.className }}{{ testResult.errorMessage }}{{ testResult.failureStack }}{{ testResult.failureType }}{{ testResult.createdDate | formatMediumDatetime }}{{ testResult.lastModifiedDate | formatMediumDatetime }} +
+ + +
+
+
+ +
+
+ +
+ +
+ +
+
+
diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/list/test-result.component.spec.ts b/simulator-ui/src/main/webapp/app/entities/test-result/list/test-result.component.spec.ts new file mode 100644 index 000000000..87b2a6172 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/list/test-result.component.spec.ts @@ -0,0 +1,125 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpHeaders, HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { TestResultService } from '../service/test-result.service'; + +import { TestResultComponent } from './test-result.component'; +import SpyInstance = jest.SpyInstance; + +describe('TestResult Management Component', () => { + let comp: TestResultComponent; + let fixture: ComponentFixture; + let service: TestResultService; + let routerNavigateSpy: SpyInstance>; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + RouterTestingModule.withRoutes([{ path: 'test-result', component: TestResultComponent }]), + HttpClientTestingModule, + TestResultComponent, + ], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: of({ + defaultSort: 'id,asc', + }), + queryParamMap: of( + jest.requireActual('@angular/router').convertToParamMap({ + page: '1', + size: '1', + sort: 'id,desc', + 'filter[someId.in]': 'dc4279ea-cfb9-11ec-9d64-0242ac120002', + }), + ), + snapshot: { queryParams: {} }, + }, + }, + ], + }) + .overrideTemplate(TestResultComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(TestResultComponent); + comp = fixture.componentInstance; + service = TestBed.inject(TestResultService); + routerNavigateSpy = jest.spyOn(comp.router, 'navigate'); + + const headers = new HttpHeaders(); + jest.spyOn(service, 'query').mockReturnValue( + of( + new HttpResponse({ + body: [{ id: 123 }], + headers, + }), + ), + ); + }); + + it('Should call load all on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenCalled(); + expect(comp.testResults?.[0]).toEqual(expect.objectContaining({ id: 123 })); + }); + + describe('trackId', () => { + it('Should forward to testResultService', () => { + const entity = { id: 123 }; + jest.spyOn(service, 'getTestResultIdentifier'); + const id = comp.trackId(0, entity); + expect(service.getTestResultIdentifier).toHaveBeenCalledWith(entity); + expect(id).toBe(entity.id); + }); + }); + + it('should load a page', () => { + // WHEN + comp.navigateToPage(1); + + // THEN + expect(routerNavigateSpy).toHaveBeenCalled(); + }); + + it('should calculate the sort attribute for an id', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenLastCalledWith(expect.objectContaining({ sort: ['id,desc'] })); + }); + + it('should calculate the sort attribute for a non-id attribute', () => { + // GIVEN + comp.predicate = 'name'; + + // WHEN + comp.navigateToWithComponentValues(); + + // THEN + expect(routerNavigateSpy).toHaveBeenLastCalledWith( + expect.anything(), + expect.objectContaining({ + queryParams: expect.objectContaining({ + sort: ['name,asc'], + }), + }), + ); + }); + + it('should calculate the filter attribute', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenLastCalledWith(expect.objectContaining({ 'someId.in': ['dc4279ea-cfb9-11ec-9d64-0242ac120002'] })); + }); +}); diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/list/test-result.component.ts b/simulator-ui/src/main/webapp/app/entities/test-result/list/test-result.component.ts new file mode 100644 index 000000000..f458307c9 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/list/test-result.component.ts @@ -0,0 +1,154 @@ +import { Component, NgZone, OnInit } from '@angular/core'; +import { HttpHeaders } from '@angular/common/http'; +import { ActivatedRoute, Data, ParamMap, Router, RouterModule } from '@angular/router'; +import { combineLatest, Observable, switchMap, tap } from 'rxjs'; + +import SharedModule from 'app/shared/shared.module'; +import { SortDirective, SortByDirective } from 'app/shared/sort'; +import { DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe } from 'app/shared/date'; +import { ItemCountComponent } from 'app/shared/pagination'; +import { FormsModule } from '@angular/forms'; + +import { ITEMS_PER_PAGE, PAGE_HEADER, TOTAL_COUNT_RESPONSE_HEADER } from 'app/config/pagination.constants'; +import { ASC, DESC, SORT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants'; +import { FilterComponent, FilterOptions, IFilterOptions, IFilterOption } from 'app/shared/filter'; +import { EntityArrayResponseType, TestResultService } from '../service/test-result.service'; +import { ITestResult } from '../test-result.model'; + +@Component({ + standalone: true, + selector: 'jhi-test-result', + templateUrl: './test-result.component.html', + imports: [ + RouterModule, + FormsModule, + SharedModule, + SortDirective, + SortByDirective, + DurationPipe, + FormatMediumDatetimePipe, + FormatMediumDatePipe, + FilterComponent, + ItemCountComponent, + ], +}) +export class TestResultComponent implements OnInit { + testResults?: ITestResult[]; + isLoading = false; + + predicate = 'id'; + ascending = true; + filters: IFilterOptions = new FilterOptions(); + + itemsPerPage = ITEMS_PER_PAGE; + totalItems = 0; + page = 1; + + constructor( + protected testResultService: TestResultService, + protected activatedRoute: ActivatedRoute, + private ngZone: NgZone, + public router: Router, + ) {} + + trackId = (_index: number, item: ITestResult): number => this.testResultService.getTestResultIdentifier(item); + + ngOnInit(): void { + this.load(); + + this.filters.filterChanges.subscribe(filterOptions => this.handleNavigation(1, this.predicate, this.ascending, filterOptions)); + } + + load(): void { + this.loadFromBackendWithRouteInformations().subscribe({ + next: (res: EntityArrayResponseType) => { + this.onResponseSuccess(res); + }, + }); + } + + navigateToWithComponentValues(): void { + this.handleNavigation(this.page, this.predicate, this.ascending, this.filters.filterOptions); + } + + navigateToPage(page = this.page): void { + this.handleNavigation(page, this.predicate, this.ascending, this.filters.filterOptions); + } + + protected loadFromBackendWithRouteInformations(): Observable { + return combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data]).pipe( + tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)), + switchMap(() => this.queryBackend(this.page, this.predicate, this.ascending, this.filters.filterOptions)), + ); + } + + protected fillComponentAttributeFromRoute(params: ParamMap, data: Data): void { + const page = params.get(PAGE_HEADER); + this.page = +(page ?? 1); + const sort = (params.get(SORT) ?? data[DEFAULT_SORT_DATA]).split(','); + this.predicate = sort[0]; + this.ascending = sort[1] === ASC; + this.filters.initializeFromParams(params); + } + + protected onResponseSuccess(response: EntityArrayResponseType): void { + this.fillComponentAttributesFromResponseHeader(response.headers); + const dataFromBody = this.fillComponentAttributesFromResponseBody(response.body); + this.testResults = dataFromBody; + } + + protected fillComponentAttributesFromResponseBody(data: ITestResult[] | null): ITestResult[] { + return data ?? []; + } + + protected fillComponentAttributesFromResponseHeader(headers: HttpHeaders): void { + this.totalItems = Number(headers.get(TOTAL_COUNT_RESPONSE_HEADER)); + } + + protected queryBackend( + page?: number, + predicate?: string, + ascending?: boolean, + filterOptions?: IFilterOption[], + ): Observable { + this.isLoading = true; + const pageToLoad: number = page ?? 1; + const queryObject: any = { + page: pageToLoad - 1, + size: this.itemsPerPage, + sort: this.getSortQueryParam(predicate, ascending), + }; + filterOptions?.forEach(filterOption => { + queryObject[filterOption.name] = filterOption.values; + }); + return this.testResultService.query(queryObject).pipe(tap(() => (this.isLoading = false))); + } + + protected handleNavigation(page = this.page, predicate?: string, ascending?: boolean, filterOptions?: IFilterOption[]): void { + const queryParamsObj: any = { + page, + size: this.itemsPerPage, + sort: this.getSortQueryParam(predicate, ascending), + }; + + filterOptions?.forEach(filterOption => { + queryParamsObj[filterOption.nameAsQueryParam()] = filterOption.values; + }); + + this.ngZone.run(() => + this.router.navigate(['./'], { + relativeTo: this.activatedRoute, + queryParams: queryParamsObj, + }), + ); + } + + protected getSortQueryParam(predicate = this.predicate, ascending = this.ascending): string[] { + const ascendingQueryParam = ascending ? ASC : DESC; + if (predicate === '') { + return []; + } else { + return [predicate + ',' + ascendingQueryParam]; + } + } +} diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/route/test-result-routing-resolve.service.spec.ts b/simulator-ui/src/main/webapp/app/entities/test-result/route/test-result-routing-resolve.service.spec.ts new file mode 100644 index 000000000..0e5d2f5fe --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/route/test-result-routing-resolve.service.spec.ts @@ -0,0 +1,99 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRouteSnapshot, ActivatedRoute, Router, convertToParamMap } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { ITestResult } from '../test-result.model'; +import { TestResultService } from '../service/test-result.service'; + +import testResultResolve from './test-result-routing-resolve.service'; + +describe('TestResult routing resolve service', () => { + let mockRouter: Router; + let mockActivatedRouteSnapshot: ActivatedRouteSnapshot; + let service: TestResultService; + let resultTestResult: ITestResult | null | undefined; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + paramMap: convertToParamMap({}), + }, + }, + }, + ], + }); + mockRouter = TestBed.inject(Router); + jest.spyOn(mockRouter, 'navigate').mockImplementation(() => Promise.resolve(true)); + mockActivatedRouteSnapshot = TestBed.inject(ActivatedRoute).snapshot; + service = TestBed.inject(TestResultService); + resultTestResult = undefined; + }); + + describe('resolve', () => { + it('should return ITestResult returned by find', () => { + // GIVEN + service.find = jest.fn(id => of(new HttpResponse({ body: { id } }))); + mockActivatedRouteSnapshot.params = { id: 123 }; + + // WHEN + TestBed.runInInjectionContext(() => { + testResultResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultTestResult = result; + }, + }); + }); + + // THEN + expect(service.find).toBeCalledWith(123); + expect(resultTestResult).toEqual({ id: 123 }); + }); + + it('should return null if id is not provided', () => { + // GIVEN + service.find = jest.fn(); + mockActivatedRouteSnapshot.params = {}; + + // WHEN + TestBed.runInInjectionContext(() => { + testResultResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultTestResult = result; + }, + }); + }); + + // THEN + expect(service.find).not.toBeCalled(); + expect(resultTestResult).toEqual(null); + }); + + it('should route to 404 page if data not found in server', () => { + // GIVEN + jest.spyOn(service, 'find').mockReturnValue(of(new HttpResponse({ body: null }))); + mockActivatedRouteSnapshot.params = { id: 123 }; + + // WHEN + TestBed.runInInjectionContext(() => { + testResultResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultTestResult = result; + }, + }); + }); + + // THEN + expect(service.find).toBeCalledWith(123); + expect(resultTestResult).toEqual(undefined); + expect(mockRouter.navigate).toHaveBeenCalledWith(['404']); + }); + }); +}); diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/route/test-result-routing-resolve.service.ts b/simulator-ui/src/main/webapp/app/entities/test-result/route/test-result-routing-resolve.service.ts new file mode 100644 index 000000000..b05a4a064 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/route/test-result-routing-resolve.service.ts @@ -0,0 +1,29 @@ +import { inject } from '@angular/core'; +import { HttpResponse } from '@angular/common/http'; +import { ActivatedRouteSnapshot, Router } from '@angular/router'; +import { of, EMPTY, Observable } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; + +import { ITestResult } from '../test-result.model'; +import { TestResultService } from '../service/test-result.service'; + +export const testResultResolve = (route: ActivatedRouteSnapshot): Observable => { + const id = route.params['id']; + if (id) { + return inject(TestResultService) + .find(id) + .pipe( + mergeMap((testResult: HttpResponse) => { + if (testResult.body) { + return of(testResult.body); + } else { + inject(Router).navigate(['404']); + return EMPTY; + } + }), + ); + } + return of(null); +}; + +export default testResultResolve; diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/service/test-result.service.spec.ts b/simulator-ui/src/main/webapp/app/entities/test-result/service/test-result.service.spec.ts new file mode 100644 index 000000000..15336203e --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/service/test-result.service.spec.ts @@ -0,0 +1,174 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { ITestResult } from '../test-result.model'; +import { sampleWithRequiredData, sampleWithPartialData, sampleWithFullData } from '../test-result.test-samples'; + +import { TestResultService, RestTestResult, TestResultsByStatus } from './test-result.service'; + +const requireRestSample: RestTestResult = { + ...sampleWithRequiredData, + createdDate: sampleWithRequiredData.createdDate?.toJSON(), + lastModifiedDate: sampleWithRequiredData.lastModifiedDate?.toJSON(), +}; + +describe('TestResult Service', () => { + let service: TestResultService; + let httpMock: HttpTestingController; + let expectedResult: ITestResult | ITestResult[] | boolean | null; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + expectedResult = null; + service = TestBed.inject(TestResultService); + httpMock = TestBed.inject(HttpTestingController); + }); + + describe('Service methods', () => { + it('should find an element', () => { + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.find(123).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should return a list of TestResult', () => { + const returnedFromService = { ...requireRestSample }; + + const expected = { ...sampleWithRequiredData }; + + service.query().subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush([returnedFromService]); + httpMock.verify(); + expect(expectedResult).toMatchObject([expected]); + }); + + it('should return results count by status', () => { + const returnedFromService: TestResultsByStatus = { total: 3, successful: 2, failed: 1 }; + + let actualResult: TestResultsByStatus | null; + service.countByStatus().subscribe(resp => (actualResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush([returnedFromService]); + httpMock.verify(); + + // @ts-ignore: Usage before assignment is ok + expect(actualResult).toMatchObject(actualResult); + }); + + describe('addTestResultToCollectionIfMissing', () => { + it('should add a TestResult to an empty array', () => { + const testResult: ITestResult = sampleWithRequiredData; + expectedResult = service.addTestResultToCollectionIfMissing([], testResult); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(testResult); + }); + + it('should not add a TestResult to an array that contains it', () => { + const testResult: ITestResult = sampleWithRequiredData; + const testResultCollection: ITestResult[] = [ + { + ...testResult, + }, + sampleWithPartialData, + ]; + expectedResult = service.addTestResultToCollectionIfMissing(testResultCollection, testResult); + expect(expectedResult).toHaveLength(2); + }); + + it("should add a TestResult to an array that doesn't contain it", () => { + const testResult: ITestResult = sampleWithRequiredData; + const testResultCollection: ITestResult[] = [sampleWithPartialData]; + expectedResult = service.addTestResultToCollectionIfMissing(testResultCollection, testResult); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(testResult); + }); + + it('should add only unique TestResult to an array', () => { + const testResultArray: ITestResult[] = [sampleWithRequiredData, sampleWithPartialData, sampleWithFullData]; + const testResultCollection: ITestResult[] = [sampleWithRequiredData]; + expectedResult = service.addTestResultToCollectionIfMissing(testResultCollection, ...testResultArray); + expect(expectedResult).toHaveLength(3); + }); + + it('should accept varargs', () => { + const testResult: ITestResult = sampleWithRequiredData; + const testResult2: ITestResult = sampleWithPartialData; + expectedResult = service.addTestResultToCollectionIfMissing([], testResult, testResult2); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(testResult); + expect(expectedResult).toContain(testResult2); + }); + + it('should accept null and undefined values', () => { + const testResult: ITestResult = sampleWithRequiredData; + expectedResult = service.addTestResultToCollectionIfMissing([], null, testResult, undefined); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(testResult); + }); + + it('should return initial array if no TestResult is added', () => { + const testResultCollection: ITestResult[] = [sampleWithRequiredData]; + expectedResult = service.addTestResultToCollectionIfMissing(testResultCollection, undefined, null); + expect(expectedResult).toEqual(testResultCollection); + }); + }); + + describe('compareTestResult', () => { + it('Should return true if both entities are null', () => { + const entity1 = null; + const entity2 = null; + + const compareResult = service.compareTestResult(entity1, entity2); + + expect(compareResult).toEqual(true); + }); + + it('Should return false if one entity is null', () => { + const entity1 = { id: 123 }; + const entity2 = null; + + const compareResult1 = service.compareTestResult(entity1, entity2); + const compareResult2 = service.compareTestResult(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey differs', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 456 }; + + const compareResult1 = service.compareTestResult(entity1, entity2); + const compareResult2 = service.compareTestResult(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return true if primaryKey matches', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 123 }; + + const compareResult1 = service.compareTestResult(entity1, entity2); + const compareResult2 = service.compareTestResult(entity2, entity1); + + expect(compareResult1).toEqual(true); + expect(compareResult2).toEqual(true); + }); + }); + }); + + afterEach(() => { + httpMock.verify(); + }); +}); diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/service/test-result.service.ts b/simulator-ui/src/main/webapp/app/entities/test-result/service/test-result.service.ts new file mode 100644 index 000000000..319024991 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/service/test-result.service.ts @@ -0,0 +1,111 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { map } from 'rxjs/operators'; + +import dayjs from 'dayjs/esm'; + +import { isPresent } from 'app/core/util/operators'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { createRequestOption } from 'app/core/request/request-util'; +import { ITestResult, NewTestResult } from '../test-result.model'; + +type RestOf = Omit & { + createdDate?: string | null; + lastModifiedDate?: string | null; +}; + +export type RestTestResult = RestOf; + +export type EntityResponseType = HttpResponse; +export type EntityArrayResponseType = HttpResponse; + +export type TestResultsByStatus = { + total: number; + successful: number; + failed: number; +}; + +@Injectable({ providedIn: 'root' }) +export class TestResultService { + protected resourceUrl = this.applicationConfigService.getEndpointFor('api/test-results'); + + constructor( + protected http: HttpClient, + protected applicationConfigService: ApplicationConfigService, + ) {} + + find(id: number): Observable { + return this.http + .get(`${this.resourceUrl}/${id}`, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + query(req?: any): Observable { + const options = createRequestOption(req); + return this.http + .get(this.resourceUrl, { params: options, observe: 'response' }) + .pipe(map(res => this.convertResponseArrayFromServer(res))); + } + + countByStatus(): Observable> { + return this.http.get(`${this.resourceUrl}/count-by-status`, { observe: 'response' }); + } + + getTestResultIdentifier(testResult: Pick): number { + return testResult.id; + } + + compareTestResult(o1: Pick | null, o2: Pick | null): boolean { + return o1 && o2 ? this.getTestResultIdentifier(o1) === this.getTestResultIdentifier(o2) : o1 === o2; + } + + addTestResultToCollectionIfMissing>( + testResultCollection: Type[], + ...testResultsToCheck: (Type | null | undefined)[] + ): Type[] { + const testResults: Type[] = testResultsToCheck.filter(isPresent); + if (testResults.length > 0) { + const testResultCollectionIdentifiers = testResultCollection.map(testResultItem => this.getTestResultIdentifier(testResultItem)!); + const testResultsToAdd = testResults.filter(testResultItem => { + const testResultIdentifier = this.getTestResultIdentifier(testResultItem); + if (testResultCollectionIdentifiers.includes(testResultIdentifier)) { + return false; + } + testResultCollectionIdentifiers.push(testResultIdentifier); + return true; + }); + return [...testResultsToAdd, ...testResultCollection]; + } + return testResultCollection; + } + + protected convertDateFromClient(testResult: T): RestOf { + return { + ...testResult, + createdDate: testResult.createdDate?.toJSON() ?? null, + lastModifiedDate: testResult.lastModifiedDate?.toJSON() ?? null, + }; + } + + protected convertDateFromServer(restTestResult: RestTestResult): ITestResult { + return { + ...restTestResult, + createdDate: restTestResult.createdDate ? dayjs(restTestResult.createdDate) : undefined, + lastModifiedDate: restTestResult.lastModifiedDate ? dayjs(restTestResult.lastModifiedDate) : undefined, + }; + } + + protected convertResponseFromServer(res: HttpResponse): HttpResponse { + return res.clone({ + body: res.body ? this.convertDateFromServer(res.body) : null, + }); + } + + protected convertResponseArrayFromServer(res: HttpResponse): HttpResponse { + return res.clone({ + body: res.body ? res.body.map(item => this.convertDateFromServer(item)) : null, + }); + } +} diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/test-result.model.ts b/simulator-ui/src/main/webapp/app/entities/test-result/test-result.model.ts new file mode 100644 index 000000000..8c353d7a8 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/test-result.model.ts @@ -0,0 +1,15 @@ +import dayjs from 'dayjs/esm'; + +export interface ITestResult { + id: number; + status?: number | null; + testName?: string | null; + className?: string | null; + errorMessage?: string | null; + failureStack?: string | null; + failureType?: string | null; + createdDate?: dayjs.Dayjs | null; + lastModifiedDate?: dayjs.Dayjs | null; +} + +export type NewTestResult = Omit & { id: null }; diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/test-result.routes.ts b/simulator-ui/src/main/webapp/app/entities/test-result/test-result.routes.ts new file mode 100644 index 000000000..add6b6a07 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/test-result.routes.ts @@ -0,0 +1,25 @@ +import { Routes } from '@angular/router'; + +import { ASC } from 'app/config/navigation.constants'; +import { TestResultComponent } from './list/test-result.component'; +import { TestResultDetailComponent } from './detail/test-result-detail.component'; +import TestResultResolve from './route/test-result-routing-resolve.service'; + +const testResultRoute: Routes = [ + { + path: '', + component: TestResultComponent, + data: { + defaultSort: 'id,' + ASC, + }, + }, + { + path: ':id/view', + component: TestResultDetailComponent, + resolve: { + testResult: TestResultResolve, + }, + }, +]; + +export default testResultRoute; diff --git a/simulator-ui/src/main/webapp/app/entities/test-result/test-result.test-samples.ts b/simulator-ui/src/main/webapp/app/entities/test-result/test-result.test-samples.ts new file mode 100644 index 000000000..6e3115064 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/entities/test-result/test-result.test-samples.ts @@ -0,0 +1,49 @@ +import dayjs from 'dayjs/esm'; + +import { ITestResult, NewTestResult } from './test-result.model'; + +export const sampleWithRequiredData: ITestResult = { + id: 22758, + status: 18375, + testName: 'breadcrumb', + className: 'newspaper', + createdDate: dayjs('2023-09-26T09:11'), + lastModifiedDate: dayjs('2023-09-26T14:25'), +}; + +export const sampleWithPartialData: ITestResult = { + id: 1008, + status: 25893, + testName: 'zowie', + className: 'regarding openly', + errorMessage: 'toward', + failureStack: 'whose', + createdDate: dayjs('2023-09-26T13:39'), + lastModifiedDate: dayjs('2023-09-26T15:03'), +}; + +export const sampleWithFullData: ITestResult = { + id: 11970, + status: 5871, + testName: 'peruse probable display', + className: 'dining', + errorMessage: 'reproachfully better what', + failureStack: 'flugelhorn over', + failureType: 'aha', + createdDate: dayjs('2023-09-26T07:09'), + lastModifiedDate: dayjs('2023-09-26T09:18'), +}; + +export const sampleWithNewData: NewTestResult = { + status: 22262, + testName: 'um finally', + className: 'supporter vastly', + createdDate: dayjs('2023-09-25T22:19'), + lastModifiedDate: dayjs('2023-09-26T03:16'), + id: null, +}; + +Object.freeze(sampleWithNewData); +Object.freeze(sampleWithRequiredData); +Object.freeze(sampleWithPartialData); +Object.freeze(sampleWithFullData); diff --git a/simulator-ui/src/main/webapp/app/home/home.component.html b/simulator-ui/src/main/webapp/app/home/home.component.html index 6582f7123..fc6758998 100644 --- a/simulator-ui/src/main/webapp/app/home/home.component.html +++ b/simulator-ui/src/main/webapp/app/home/home.component.html @@ -4,13 +4,32 @@
-

Citrus Simulator

+
+

+ {{ simulatorInfo!.name }} -

- If you like Citrus and the Simulator, don't forget to give us a star on - GitHub! -

+ + Citrus Simulator + +

+ +

+ +

+ +

+ If you like Citrus and the Simulator, don't forget to give us a star on + + GitHub + + ! +

+
+ +
+
+ +
+
diff --git a/simulator-ui/src/main/webapp/app/home/home.component.spec.ts b/simulator-ui/src/main/webapp/app/home/home.component.spec.ts new file mode 100644 index 000000000..6408d14f9 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/home/home.component.spec.ts @@ -0,0 +1,56 @@ +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { InfoResponse } from 'app/layouts/profiles/profile-info.model'; + +import HomeComponent from './home.component'; + +describe('Home Component', () => { + let applicationConfigService: ApplicationConfigService; + let httpMock: HttpTestingController; + + let fixture: ComponentFixture; + let component: HomeComponent; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HomeComponent, HttpClientTestingModule], + providers: [ApplicationConfigService], + }) + .overrideTemplate(HomeComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + httpMock = TestBed.inject(HttpTestingController); + applicationConfigService = TestBed.inject(ApplicationConfigService); + }); + + afterEach(() => { + httpMock.verify(); // Ensure that there are no outstanding HTTP requests. + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should fetch simulatorInfo on initialization', fakeAsync(() => { + const mockInfoResponse: InfoResponse = { + simulator: { + name: 'Citrus Simulator', + version: '1.2.3', + }, + }; + + component.ngOnInit(); + + const req = httpMock.expectOne(applicationConfigService.getEndpointFor('api/manage/info')); + expect(req.request.method).toBe('GET'); + req.flush(mockInfoResponse); + + tick(); + + expect(component.simulatorInfo).toEqual(mockInfoResponse.simulator); + })); +}); diff --git a/simulator-ui/src/main/webapp/app/home/home.component.ts b/simulator-ui/src/main/webapp/app/home/home.component.ts index 8a6ac0cf4..946d1a4a0 100644 --- a/simulator-ui/src/main/webapp/app/home/home.component.ts +++ b/simulator-ui/src/main/webapp/app/home/home.component.ts @@ -1,13 +1,33 @@ -import { Component } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Component, OnInit } from '@angular/core'; import { RouterModule } from '@angular/router'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { InfoResponse, SimulatorInfo } from 'app/layouts/profiles/profile-info.model'; import SharedModule from 'app/shared/shared.module'; +import TestResultSummaryComponent from './test-result-summary.component'; + @Component({ standalone: true, selector: 'jhi-home', templateUrl: './home.component.html', styleUrls: ['./home.component.scss'], - imports: [SharedModule, RouterModule], + imports: [RouterModule, SharedModule, TestResultSummaryComponent], }) -export default class HomeComponent {} +export default class HomeComponent implements OnInit { + simulatorInfo: SimulatorInfo | null = null; + + private infoUrl = this.applicationConfigService.getEndpointFor('api/manage/info'); + + constructor( + private applicationConfigService: ApplicationConfigService, + private http: HttpClient, + ) {} + + ngOnInit(): void { + this.http.get(this.infoUrl).subscribe((response: InfoResponse) => { + this.simulatorInfo = response.simulator ?? null; + }); + } +} diff --git a/simulator-ui/src/main/webapp/app/home/test-result-summary.component.html b/simulator-ui/src/main/webapp/app/home/test-result-summary.component.html new file mode 100644 index 000000000..e9987901b --- /dev/null +++ b/simulator-ui/src/main/webapp/app/home/test-result-summary.component.html @@ -0,0 +1,45 @@ +
+

+ + Test Results +

+ +
+
+ +

Total:

+
{{ testResults?.total ?? 0 }} (100 %)
+
+
+ +
+ +

Successful:

+
+ {{ testResults?.successful ?? 0 }} ({{ successfulPercentage }} %) +
+
+
+ +
+ +

Failed:

+
+ {{ testResults?.failed ?? 0 }} ({{ failedPercentage }} %) +
+
+
+
+ + + + No simulations ran yet! Try starting one: + Documentation + + +
diff --git a/simulator-ui/src/main/webapp/app/home/test-result-summary.component.spec.ts b/simulator-ui/src/main/webapp/app/home/test-result-summary.component.spec.ts new file mode 100644 index 000000000..e0f7de1dc --- /dev/null +++ b/simulator-ui/src/main/webapp/app/home/test-result-summary.component.spec.ts @@ -0,0 +1,77 @@ +import { HttpResponse } from '@angular/common/http'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; + +import { TranslateService } from '@ngx-translate/core'; + +import { of } from 'rxjs'; + +import { TestResultsByStatus, TestResultService } from 'app/entities/test-result/service/test-result.service'; + +import TestResultSummaryComponent from './test-result-summary.component'; + +import Mocked = jest.Mocked; + +describe('TestResultSummaryComponent', () => { + let testResultService: Mocked; + + let fixture: ComponentFixture; + let component: TestResultSummaryComponent; + + beforeEach(async () => { + testResultService = { + countByStatus: jest.fn(), + } as unknown as Mocked; + + await TestBed.configureTestingModule({ + imports: [TestResultSummaryComponent], + providers: [TranslateService, { provide: TestResultService, useValue: testResultService }], + }) + .overrideTemplate(TestResultSummaryComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(TestResultSummaryComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('ngOnInit', () => { + it('should correctly calculate percentages', fakeAsync(() => { + const mockData = new HttpResponse({ + body: { + total: 2, + successful: 1, + failed: 1, + }, + }); + + testResultService.countByStatus.mockReturnValue(of(mockData)); + + component.ngOnInit(); + tick(); + + expect(component.testResults).toEqual(mockData.body); + expect(component.successfulPercentage).toEqual(50); + expect(component.failedPercentage).toEqual(50); + })); + + it('default to a zero-result', fakeAsync(() => { + const mockData = new HttpResponse({ body: null }); + + testResultService.countByStatus.mockReturnValue(of(mockData)); + + component.ngOnInit(); + tick(); + + expect(component.testResults).toEqual({ + total: 0, + successful: 0, + failed: 0, + }); + expect(component.successfulPercentage).toEqual(0); + expect(component.failedPercentage).toEqual(0); + })); + }); +}); diff --git a/simulator-ui/src/main/webapp/app/home/test-result-summary.component.ts b/simulator-ui/src/main/webapp/app/home/test-result-summary.component.ts new file mode 100644 index 000000000..a9cf52556 --- /dev/null +++ b/simulator-ui/src/main/webapp/app/home/test-result-summary.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit } from '@angular/core'; + +import { map } from 'rxjs/operators'; + +import { TestResultsByStatus, TestResultService } from 'app/entities/test-result/service/test-result.service'; +import SharedModule from 'app/shared/shared.module'; + +@Component({ + standalone: true, + selector: 'app-test-result-summary', + templateUrl: './test-result-summary.component.html', + imports: [SharedModule], +}) +export default class TestResultSummaryComponent implements OnInit { + testResults: TestResultsByStatus | null = null; + + successfulPercentage = 0; + failedPercentage = 0; + + constructor(private testResultService: TestResultService) {} + + ngOnInit(): void { + this.load(); + } + + private load(): void { + this.testResultService + .countByStatus() + .pipe(map(response => response.body ?? { total: 0, successful: 0, failed: 0 })) + .subscribe((testResults: TestResultsByStatus) => { + this.testResults = testResults; + + if (testResults.total > 0) { + this.successfulPercentage = (testResults.successful / testResults.total) * 100; + this.failedPercentage = (testResults.failed / testResults.total) * 100; + } + }); + } +} diff --git a/simulator-ui/src/main/webapp/app/layouts/navbar/navbar.component.html b/simulator-ui/src/main/webapp/app/layouts/navbar/navbar.component.html index 5b2d17f91..dd88daa03 100644 --- a/simulator-ui/src/main/webapp/app/layouts/navbar/navbar.component.html +++ b/simulator-ui/src/main/webapp/app/layouts/navbar/navbar.component.html @@ -42,6 +42,30 @@ diff --git a/simulator-ui/src/main/webapp/app/layouts/profiles/profile-info.model.ts b/simulator-ui/src/main/webapp/app/layouts/profiles/profile-info.model.ts index 14e920f1a..0daee0bcf 100644 --- a/simulator-ui/src/main/webapp/app/layouts/profiles/profile-info.model.ts +++ b/simulator-ui/src/main/webapp/app/layouts/profiles/profile-info.model.ts @@ -3,6 +3,7 @@ export interface InfoResponse { git?: any; build?: any; activeProfiles?: string[]; + simulator?: SimulatorInfo; } export class ProfileInfo { @@ -13,3 +14,10 @@ export class ProfileInfo { public openAPIEnabled?: boolean, ) {} } + +export class SimulatorInfo { + constructor( + public name?: string, + public version?: string, + ) {} +} diff --git a/simulator-ui/src/main/webapp/i18n/en/global.json b/simulator-ui/src/main/webapp/i18n/en/global.json index f37e1a7cd..9b2490675 100644 --- a/simulator-ui/src/main/webapp/i18n/en/global.json +++ b/simulator-ui/src/main/webapp/i18n/en/global.json @@ -7,6 +7,8 @@ "jhipster-needle-menu-add-element": "JHipster will add additional menu entries here (do not translate!)", "entities": { "main": "Entities", + "testParameter": "Test Parameter", + "testResult": "Test Result", "jhipster-needle-menu-add-entry": "JHipster will add additional entities here (do not translate!)" }, "language": "Language" diff --git a/simulator-ui/src/main/webapp/i18n/en/home.json b/simulator-ui/src/main/webapp/i18n/en/home.json index 635433e95..3549ae811 100755 --- a/simulator-ui/src/main/webapp/i18n/en/home.json +++ b/simulator-ui/src/main/webapp/i18n/en/home.json @@ -1,7 +1,18 @@ { "home": { "title": "Citrus Simulator", + "version": "Version {{version}}", "like": "If you like Citrus and the Simulator, don't forget to give us a star on", - "github": "GitHub" + "github": "GitHub", + "simulations": { + "title": "Simulations", + "documentation": "Documentation", + "noSimulationsRanYet": "No simulations ran yet! Try starting one:", + "results": { + "total": "Total:", + "successful": "Successful:", + "failed": "Failed:" + } + } } } diff --git a/simulator-ui/src/main/webapp/i18n/en/testParameter.json b/simulator-ui/src/main/webapp/i18n/en/testParameter.json new file mode 100644 index 000000000..0558ca11c --- /dev/null +++ b/simulator-ui/src/main/webapp/i18n/en/testParameter.json @@ -0,0 +1,20 @@ +{ + "citrusSimulatorApp": { + "testParameter": { + "home": { + "title": "Test Parameters", + "refreshListLabel": "Refresh list", + "notFound": "No Test Parameters found" + }, + "detail": { + "title": "Test Parameter" + }, + "id": "ID", + "key": "Key", + "value": "Value", + "createdDate": "Created Date", + "lastModifiedDate": "Last Modified Date", + "testResult": "Test Result" + } + } +} diff --git a/simulator-ui/src/main/webapp/i18n/en/testResult.json b/simulator-ui/src/main/webapp/i18n/en/testResult.json new file mode 100644 index 000000000..3949c3e28 --- /dev/null +++ b/simulator-ui/src/main/webapp/i18n/en/testResult.json @@ -0,0 +1,24 @@ +{ + "citrusSimulatorApp": { + "testResult": { + "home": { + "title": "Test Results", + "refreshListLabel": "Refresh list", + "notFound": "No Test Results found" + }, + "detail": { + "title": "Test Result" + }, + "id": "ID", + "status": "Status", + "testName": "Test Name", + "className": "Class Name", + "errorMessage": "Error Message", + "failureStack": "Failure Stack", + "failureType": "Failure Type", + "createdDate": "Created Date", + "lastModifiedDate": "Last Modified Date", + "testParameter": "Test Parameter" + } + } +} diff --git a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/IntegrationTest.java b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/IntegrationTest.java index 8408aef57..b44843605 100644 --- a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/IntegrationTest.java +++ b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/IntegrationTest.java @@ -1,6 +1,6 @@ package org.citrusframework.simulator.ui; -import org.citrusframework.simulator.ui.config.SimulatorUiAutoconfiguration; +import org.citrusframework.simulator.ui.test.TestApplication; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; @@ -14,7 +14,7 @@ */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -@SpringBootTest(classes = {SimulatorUiAutoconfiguration.class}) +@SpringBootTest(classes = {TestApplication.class}) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) public @interface IntegrationTest { } diff --git a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/SimulatorStarter.java b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/SimulatorStarter.java deleted file mode 100644 index 22b778428..000000000 --- a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/SimulatorStarter.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.citrusframework.simulator.ui; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class SimulatorStarter { - - public static void main(String[] args) { - SpringApplication.run(SimulatorStarter.class, args); - } -} diff --git a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/config/InfoEndpointConfigurationTest.java b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/config/InfoEndpointConfigurationTest.java new file mode 100644 index 000000000..2b18fb764 --- /dev/null +++ b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/config/InfoEndpointConfigurationTest.java @@ -0,0 +1,34 @@ +package org.citrusframework.simulator.ui.config; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.actuate.info.Info; +import org.springframework.core.env.Environment; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class InfoEndpointConfigurationTest { + + @InjectMocks + private InfoEndpointConfiguration infoEndpointConfiguration; + + @Mock + private Environment environmentMock; + + @Test + void shouldContributeActiveProfilesToInfoBuilder() { + String[] activeProfiles = {"dev", "local"}; + when(environmentMock.getActiveProfiles()).thenReturn(activeProfiles); + + Info.Builder builder = new Info.Builder(); + infoEndpointConfiguration.contribute(builder); + + Info info = builder.build(); + assertEquals(activeProfiles, info.getDetails().get("activeProfiles")); + } +} diff --git a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/config/ServletUtilsTest.java b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/config/ServletUtilsTest.java new file mode 100644 index 000000000..97aa0a5fa --- /dev/null +++ b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/config/ServletUtilsTest.java @@ -0,0 +1,27 @@ +package org.citrusframework.simulator.ui.config; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ServletUtilsTest { + + static Stream extractContextPath() { + return Stream.of( + Arguments.of("/a/b", "/a/b"), + Arguments.of("/a/b/*", "/a/b"), + Arguments.of("/a/b/**", "/a/b"), + Arguments.of("/a/b/**/*", "/a/b") + ); + } + + @MethodSource + @ParameterizedTest + void extractContextPath(String urlMapping, String contextPath) { + assertEquals(contextPath, ServletUtils.extractContextPath(urlMapping)); + } +} diff --git a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/config/SimulatorUiAutoconfigurationIT.java b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/config/SimulatorUiAutoconfigurationIT.java new file mode 100644 index 000000000..f16d540b6 --- /dev/null +++ b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/config/SimulatorUiAutoconfigurationIT.java @@ -0,0 +1,19 @@ +package org.citrusframework.simulator.ui.config; + +import org.citrusframework.simulator.ui.IntegrationTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@IntegrationTest +public class SimulatorUiAutoconfigurationIT { + + @Autowired + private SimulatorUiAutoconfiguration simulatorUiAutoconfiguration; + + @Test + void isEnabledByDefault() { + assertNotNull(simulatorUiAutoconfiguration, "Simulator UI autoconfiguration is enabled by default, whenever simulator-ui is on the classpath."); + } +} diff --git a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/filter/SpaWebFilterIT.java b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/filter/SpaWebFilterIT.java index a36501075..cf4c8aa2a 100644 --- a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/filter/SpaWebFilterIT.java +++ b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/filter/SpaWebFilterIT.java @@ -7,6 +7,7 @@ import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -14,6 +15,9 @@ @AutoConfigureMockMvc class SpaWebFilterIT { + private static final String REST_URL_MAPPING = "/simulator/rest"; + private static final String WS_SERVLET_PATH = "/simulator/ws"; + @Autowired private MockMvc mockMvc; @@ -22,26 +26,14 @@ void testFilterForwardsToIndex() throws Exception { mockMvc.perform(get("/")).andExpect(status().isOk()).andExpect(forwardedUrl("/index.html")); } - // TODO: Use valid endpoint - // @Test - // void testFilterDoesNotForwardToIndexForApi() throws Exception { - // mockMvc.perform(get("/api/authenticate")).andExpect(status().isOk()).andExpect(forwardedUrl(null)); - // } - - // TODO: Maybe add swagger UI? - // @Test - // void testFilterDoesNotForwardToIndexForV3ApiDocs() throws Exception { - // mockMvc.perform(get("/v3/api-docs")).andExpect(status().isOk()).andExpect(forwardedUrl(null)); - // } - @Test - void testFilterDoesNotForwardToIndexForDotFile() throws Exception { - mockMvc.perform(get("/file.js")).andExpect(status().isNotFound()); + void testFilterDoesNotForwardToIndexForApi() throws Exception { + mockMvc.perform(get("/api/test-results")).andExpect(status().isOk()).andExpect(forwardedUrl(null)); } @Test - void getBackendEndpoint() throws Exception { - mockMvc.perform(get("/test")).andExpect(status().isOk()).andExpect(forwardedUrl("/index.html")); + void testFilterDoesNotForwardToIndexForDotFile() throws Exception { + mockMvc.perform(get("/file.js")).andExpect(status().isNotFound()); } @Test @@ -65,17 +57,17 @@ void forwardUnmappedDeepMapping() throws Exception { } @Test - void getUnmappedFirstLevelFile() throws Exception { - mockMvc.perform(get("/foo.js")).andExpect(status().isNotFound()); + void getUnmappedThirdLevelFile() throws Exception { + mockMvc.perform(get("/foo/another/bar.js")).andExpect(status().isNotFound()); } @Test - void getUnmappedSecondLevelFile() throws Exception { - mockMvc.perform(get("/foo/bar.js")).andExpect(status().isNotFound()); + void executeRestSimulation() throws Exception { + mockMvc.perform(get(REST_URL_MAPPING)).andExpect(status().isOk()).andExpect(forwardedUrl(null)); } @Test - void getUnmappedThirdLevelFile() throws Exception { - mockMvc.perform(get("/foo/another/bar.js")).andExpect(status().isNotFound()); + void executeWsSimulation() throws Exception { + mockMvc.perform(post(WS_SERVLET_PATH)).andExpect(status().isNotFound()).andExpect(forwardedUrl(null)); } } diff --git a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/filter/SpaWebFilterTest.java b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/filter/SpaWebFilterTest.java new file mode 100644 index 000000000..ee2e306c4 --- /dev/null +++ b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/filter/SpaWebFilterTest.java @@ -0,0 +1,104 @@ +package org.citrusframework.simulator.ui.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import java.io.IOException; +import java.util.stream.Stream; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class SpaWebFilterTest { + + @Mock(name = "simulatorRestRequestMatcher") + private RequestMatcher simulatorRestRequestMatcherMock; + + @Mock + private HttpServletRequest requestMock; + + @Mock + private HttpServletResponse responseMock; + + @Mock + private FilterChain filterChainMock; + + @Mock + private RequestDispatcher requestDispatcherMock; + + @InjectMocks + private SpaWebFilter fixture; + + public static Stream shouldNotForwardPathToIndexHtml() { + return Stream.of( + Arguments.of("/api", ""), + Arguments.of("/api/somepath", ""), + Arguments.of("/v3/api-docs", ""), + Arguments.of("/v3/api-docs/somepath", ""), + Arguments.of("/some/absolute/path.", ""), + Arguments.of("path/without/leading/slash", ""), + Arguments.of("/server-1/api", "/server-1"), + Arguments.of("/server-1/api/somepath", "/server-1"), + Arguments.of("/server-1/v3/api-docs", "/server-1"), + Arguments.of("/server-1/v3/api-docs/somepath", "/server-1"), + Arguments.of("/server-1/some/absolute/path.", "/server-1"), + Arguments.of("/server-1path/without/leading/slash", "/server-1") + ); + } + + @MethodSource + @ParameterizedTest + void shouldNotForwardPathToIndexHtml(String requestUri, String contextPath) throws ServletException, IOException { + when(requestMock.getRequestURI()).thenReturn(requestUri); + when(requestMock.getContextPath()).thenReturn(contextPath); + + fixture.doFilterInternal(requestMock, responseMock, filterChainMock); + + verify(filterChainMock).doFilter(requestMock, responseMock); + verify(requestDispatcherMock, never()).forward(requestMock, responseMock); + } + + @Test + void shouldNotForwardRequestMatchingPathToIndexHtml() throws ServletException, IOException { + String requestUri = "/request-path"; + + when(requestMock.getRequestURI()).thenReturn(requestUri); + when(requestMock.getContextPath()).thenReturn(""); + + doReturn(true).when(simulatorRestRequestMatcherMock).matches(requestMock); + + fixture.doFilterInternal(requestMock, responseMock, filterChainMock); + + verify(filterChainMock).doFilter(requestMock, responseMock); + verify(requestDispatcherMock, never()).forward(requestMock, responseMock); + } + + @Test + void shouldForwardInvalidPathToIndexHtml() throws ServletException, IOException { + when(requestMock.getRequestDispatcher("/index.html")).thenReturn(requestDispatcherMock); + + when(requestMock.getRequestURI()).thenReturn("/somepath"); + when(requestMock.getContextPath()).thenReturn(""); + when(simulatorRestRequestMatcherMock.matches(requestMock)).thenReturn(false); + + fixture.doFilterInternal(requestMock, responseMock, filterChainMock); + + verify(requestDispatcherMock).forward(requestMock, responseMock); + verify(filterChainMock, never()).doFilter(requestMock, responseMock); + } +} diff --git a/simulator-ui/src/test/java/org/citrusframework/simulator/ui/test/TestApplication.java b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/test/TestApplication.java new file mode 100644 index 000000000..0ae348048 --- /dev/null +++ b/simulator-ui/src/test/java/org/citrusframework/simulator/ui/test/TestApplication.java @@ -0,0 +1,26 @@ +package org.citrusframework.simulator.ui.test; + +import org.citrusframework.simulator.scenario.AbstractSimulatorScenario; +import org.citrusframework.simulator.scenario.SimulatorScenario; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +/** + * Note, this may not rest in the root package of the {@code simulator-ui} because of the automatic package scan + * which {@link SpringBootApplication} performs. Hence, the {@code test} package. + */ +@SpringBootApplication +public class TestApplication { + + public static void main(String[] args) { + SpringApplication.run(TestApplication.class, args); + } + + @Bean("DEFAULT_SCENARIO") + public SimulatorScenario defaultScenario() { + return new AbstractSimulatorScenario() { + + }; + } +} diff --git a/simulator-ui/src/test/resources/application.properties b/simulator-ui/src/test/resources/application.properties new file mode 100644 index 000000000..5302dc8ee --- /dev/null +++ b/simulator-ui/src/test/resources/application.properties @@ -0,0 +1,5 @@ +citrus.simulator.rest.enabled=true +citrus.simulator.rest.url-mapping=/simulator/rest + +citrus.simulator.ws.enabled=true +citrus.simulator.ws.servlet-mapping=/simulator/ws