Skip to content

Commit

Permalink
feat(simulator-ui): updated reset capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
bbortt committed Aug 29, 2024
1 parent 7ef3e28 commit 1329b57
Show file tree
Hide file tree
Showing 25 changed files with 437 additions and 231 deletions.
43 changes: 34 additions & 9 deletions simulator-docs/src/main/asciidoc/concepts-advanced.adoc
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[[concepts-advanced]]
[[advanced-concepts]]
= Advanced Concepts

[[concept-advanced-execution-modes]]
[[advanced-concepts-execution-modes]]
== Execution Modes in Citrus Simulator

The Citrus Simulator offers different modes of operation to accommodate various testing scenarios and requirements.
These modes dictate how the simulator executes the test scenarios.
It comes with two modes, a synchronous and an asynchronous one, providing flexibility in how interactions are simulated and tested.

[[concept-advanced-execution-sync-mode]]
[[advanced-concepts-execution-sync-mode]]
=== Synchronous Execution Mode

The synchronous execution mode ensures that scenarios are executed one after the other, in a single thread.
Expand All @@ -31,7 +31,7 @@ citrus:
mode: sync
----

[[concept-advanced-execution-async-mode]]
[[advanced-concepts-execution-async-mode]]
=== Asynchronous Execution Mode

In asynchronous execution mode, scenarios are executed concurrently in separate threads, allowing for parallel processing.
Expand All @@ -57,7 +57,7 @@ citrus:
threads: 10
----

[[concept-advanced-execution-custom-mode]]
[[advanced-concepts-execution-custom-mode]]
=== Custom Executors

For advanced scenarios, you have the flexibility to provide custom executors by implementing the `ScenarioExecutorService` interface.
Expand All @@ -78,14 +78,15 @@ public ScenarioExecutorService customScenarioExecutorService() {

This custom executor will then be used by the simulator to execute scenarios according to the logic you've implemented.

== Best Practices
[[advanced-concepts-execution-mode-best-practices]]
=== Best Practices

- Use the _synchronous mode_ as the standard, for linear simulations where data consistency matters or when debugging to ensure straightforward tracing of actions and outcomes.
- Opt for the _asynchronous mode_ only when explicitly needed, when simulating more complex scenarios that involve intermediate synchronous messages.

By understanding and appropriately configuring the execution modes of the Citrus Simulator, you can tailor the simulation environment to best suit your testing needs, whether you require precise control over scenario execution or need to simulate high-volume, concurrent interactions.

[[concept-advanced-database-schema]]
[[advanced-concepts-database-schema]]
== Database Schema

In some cases, it may be useful to keep the database schema in mind.
Expand All @@ -94,8 +95,8 @@ This visual representation can help understand the relationships and structure o

image::database-schema.png[Database Schema, title="Database Schema of the Citrus Simulator"]

[[concept-advanced-runtime-scenario-registration]]
== Registering Simulator Scenarios at Runtime
[[advanced-concepts-scenario-cache]]
== Scenario Cache

Registering simulator scenarios at runtime is a perfectly valid approach.
However, it's crucial to ensure that the scenario cache used by the simulator remains synchronized.
Expand All @@ -107,6 +108,7 @@ Therefore, after making modifications, it's necessary to call `ScenarioLookupSer

The following Java source code illustrates how to register a custom scenario and update the scenario cache:

.Custom Scenario Bean Registration
[source,java]
----
import org.citrusframework.simulator.service.ScenarioLookupService;
Expand All @@ -128,3 +130,26 @@ public class MyCustomBeanConfiguration {
}
}
----

[[advanced-concepts-housekeeping]]
== Housekeeping

The Simulator has the ability to reset all recorded Test Results and Executions, either using the <<rest-api,REST API>> or <<user-interface-integration,User Interface>>.
This functionality is useful for smaller and/or ephemeral simulations, but can be problematic for long-lived or even central services.
In such cases, it is recommended to switch the endpoint off and set up your own housekeeping.

To disable the functionality in both back- and frontend, configure the below property in the Spring Boot configuration files:

.Example `application.properties`
[source, properties]
----
citrus.simulator.simulation-results.reset-enabled=false
----

.Example `application.yml`
----
citrus:
simulator:
test-results:
reset-enabeld: false
----
2 changes: 2 additions & 0 deletions simulator-docs/src/main/asciidoc/rest-api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ For each listed resource, the following operations are supported:

All REST resources adhere to this pattern, with exceptions noted in subsequent sections.

The endpoint `/api/test-results` additionally supports the `DELETE` request that removes all recorded Test Results and Executions.

[[receive-single-test-result]]
=== Receive SINGLE Test-Parameter

Expand Down
27 changes: 16 additions & 11 deletions simulator-docs/src/main/asciidoc/user-interface.adoc
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
[[user-interface]]
= User Interface

The simulator application, initiated as a Spring Boot web application, offers an intuitive user interface to enhance user interaction and efficiency.
Upon launching the simulator and navigating to http://localhost:8080 in your browser, you are greeted with the default welcome page, designed for straightforward interaction and focused on displaying JSON response data from the simulator REST API.

image:default-ui.png[The default Simulator UI]
image:default-ui.png[The default Simulator UI, title="Default UI of the Citrus Simulator"]

For an enriched experience, the simulator supports a more advanced user interface built with Angular, providing comprehensive administrative capabilities.
This enhanced UI allows users to effortlessly monitor the simulator's status and review detailed logs of executed scenarios and their outcomes.

[[user-interface-integration]]
== Integrating the Angular-based UI

To integrate the advanced Angular-based UI into your simulator project, add the following Maven dependency:

.Citrus Simulator UI Dependency
[source,xml]
-----
<!-- Simulator web ui -->
Expand All @@ -28,30 +31,32 @@ This information can typically be found in the project documentation or the repo
Upon successful integration and starting the simulator, the Angular-based UI becomes accessible at http://localhost:8080, automatically enhancing the default user interface without any additional configuration.
The simulator dashboard provides a comprehensive overview of your project, presenting key metrics and insights at a glance.

image:dashboard.png[The Dashboard provides a quick overview of the simulator's metrics and activity]
image:dashboard.png[Citrus Simulator Dashboard, title="Dashboard providing a quick overview of the simulator's metrics and activity"]

[[ui-scenarios]]
Using the "Reset"-button, it's also possible to delete all recorded Test Results and Executions.

[[user-interface-scenarios]]
== Scenarios

The "Scenarios" tab within the user interface displays all scenarios available for automatic mapping upon handling incoming requests.
This tab not only lists the scenarios but also offers functionalities such as initiating scenario executions directly from the UI.

image:scenario-list.png[List of available scenarios in the simulator]
image:scenario-list.png[Scenario Executions, title="List of available scenarios in the simulator"]

Selecting any scenario from the list opens a detailed view of that specific scenario.
This view includes comprehensive information about the scenario, such as the messages processed during executions and the results of each execution, providing valuable insights into the behavior and outcome of the scenario.

image:scenario-details.png[Detailed view of a scenario,including execution messages and outcomes]
image:scenario-details.png[Scenario Details, title="Detailed view of a scenario, including execution details"]

Each scenario detail page is designed to offer a deep dive into the scenario's workings, including the input and output data, any validations or assertions applied, and a step-by-step breakdown of the scenario's execution path.
This level of detail aids in understanding each scenario's role within the simulator and troubleshooting any issues that may arise.

[[ui-scenario-results]]
[[user-interface-scenario-results]]
== Scenario Executions

Every execution of a scenario within the simulator is meticulously recorded, with the results readily accessible through the user interface for review and analysis.

image:scenario-executions.png[Overview of scenario executions,showing a list with statuses and key details]
image:scenario-executions.png[Scenario Executions, title="Overview of scenario executions, showing a list with statuses and key details"]

This view also provides rich filter possibilities, by name, status and so on. Even by headers of recorded messages.

Expand All @@ -68,14 +73,14 @@ Combining multiple patterns: Separate multiple filter expressions with a semicol

There is a helping dialog available to the right of the message-header input field.

[[ui-scenario-results]]
[[user-interface-execution-results]]
=== Viewing Execution Results

The results of each scenario execution provide a comprehensive overview, including the outcome (such as Passed, Failed, or Errored), execution duration, and the parameters and data utilized during the execution.
You can even see full stacktraces to debug your scenario failures.
To access the detailed results of a specific execution, simply select the desired entry from the list.

image:scenario-execution-details.png[Detailed view of a scenario execution,including request/response data and any logged messages or errors]
image:scenario-execution-details.png[Scenario Execution Details, title="Detailed view of a scenario execution, including request/response data and any logged messages or errors"]

*Detailed Execution Insights*

Expand All @@ -99,13 +104,13 @@ Furthermore, the detailed logs and error messages serve as a direct insight into

Leveraging the execution details effectively can significantly enhance the efficiency of testing cycles and contribute to a more robust and reliable testing process within the Citrus framework.

[[ui-entities]]
[[user-interface-entities]]
== Exploring Database Entities

For instances when you require more detailed information or are unable to locate specific data within the simulator's UI, the "Entities" drop-down menu offers a solution.
Located at the top of the user interface, this menu provides direct access to all database entities used by the simulator, allowing for an in-depth review of the underlying data.

[[ui-entities-access]]
[[user-interface-entities-access]]
=== Accessing Database Entities

To explore the database entities:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.springframework.test.context.ContextConfiguration;
import org.testng.annotations.Test;

import static java.lang.String.format;
import static org.citrusframework.http.actions.HttpActionBuilder.http;

/**
Expand All @@ -57,26 +58,24 @@ public class SimulatorSwaggerIT extends TestNGCitrusSpringSupport {

@CitrusTest
public void testUiInfo() {
$(http().client(simulatorUiClient)
.send()
.get("/api/manage/info")
.message()
.accept(MediaType.APPLICATION_JSON_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE));

$(http().client(simulatorUiClient)
.receive()
.response(HttpStatus.OK)
.message()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.body("{" +
"\"simulator\":" +
"{" +
"\"name\":\"REST Petstore Simulator\"," +
"\"version\":\"@ignore@\"" +
"}," +
"\"activeProfiles\": []" +
"}"));
$(http().client(simulatorUiClient).send().get("/api/manage/info").message()
.accept(MediaType.APPLICATION_JSON_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE));

$(http().client(simulatorUiClient).receive().response(HttpStatus.OK).message()
.contentType(MediaType.APPLICATION_JSON_VALUE).body(
"{"
+ "\"simulator\":"
+ "{"
+ "\"name\":\"REST Petstore Simulator\","
+ "\"version\":\"@ignore@\""
+ "},"
+ "\"config\":"
+ "{\n"
+ "\"reset-results-enabled\": \"true\"\n"
+ "},"
+ "\"activeProfiles\": []"
+ "}"));
}

@CitrusTest
Expand Down Expand Up @@ -238,14 +237,14 @@ public static class EndpointConfig {
@Bean
public HttpClient petstoreClient() {
return CitrusEndpoints.http().client()
.requestUrl(String.format("http://localhost:%s/petstore/v2", 8080))
.requestUrl(format("http://localhost:%s/petstore/v2", 8080))
.build();
}

@Bean
public HttpClient simulatorUiClient() {
return CitrusEndpoints.http().client()
.requestUrl(String.format("http://localhost:%s", 8080))
.requestUrl(format("http://localhost:%s", 8080))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ public class SimulatorConfigurationProperties implements EnvironmentAware, Initi
*/
private String outboundJsonDictionary = "outbound-json-dictionary.properties";

private SimulationResults simulationResults = new SimulationResults();

@Override
public void setEnvironment(Environment environment) {
inboundXmlDictionary = environment.getProperty(SIMULATOR_INBOUND_XML_DICTIONARY_PROPERTY, environment.getProperty(SIMULATOR_INBOUND_XML_DICTIONARY_ENV, inboundXmlDictionary));
Expand All @@ -114,4 +116,15 @@ public void setEnvironment(Environment environment) {
public void afterPropertiesSet() {
logger.info("Using the simulator configuration: {}", this);
}

@Getter
@Setter
@ToString
public static class SimulationResults {

/**
* Specifies whether the test results shall be deletable or not. If you're working with a long-lived citrus-simulator and disable this, make sure to manually take care of housekeeping!
*/
private boolean resetEnabled = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.citrusframework.simulator.web.rest;

import org.citrusframework.simulator.config.SimulatorConfigurationProperties;
import org.citrusframework.simulator.config.SimulatorConfigurationProperties.SimulationResults;
import org.citrusframework.simulator.model.TestResult;
import org.citrusframework.simulator.service.TestResultQueryService;
import org.citrusframework.simulator.service.TestResultService;
Expand All @@ -42,6 +44,8 @@
import java.util.List;
import java.util.Optional;

import static org.springframework.http.HttpStatus.NOT_IMPLEMENTED;

/**
* REST controller for managing {@link TestResult}.
*/
Expand All @@ -51,12 +55,14 @@ public class TestResultResource {

private static final Logger logger = LoggerFactory.getLogger(TestResultResource.class);

private final SimulatorConfigurationProperties simulatorConfigurationProperties;
private final TestResultService testResultService;
private final TestResultQueryService testResultQueryService;

private final TestResultMapper testResultMapper;

public TestResultResource(TestResultService testResultService, TestResultQueryService testResultQueryService, TestResultMapper testResultMapper) {
public TestResultResource(SimulatorConfigurationProperties simulatorConfigurationProperties, TestResultService testResultService, TestResultQueryService testResultQueryService, TestResultMapper testResultMapper) {
this.simulatorConfigurationProperties = simulatorConfigurationProperties;
this.testResultService = testResultService;
this.testResultQueryService = testResultQueryService;
this.testResultMapper = testResultMapper;
Expand All @@ -80,14 +86,27 @@ public ResponseEntity<List<TestResultDTO>> getAllTestResults(TestResultCriteria

/**
* {@code DELETE /test-results} : delete all the testResults.
* <p>
* Functionality can be disabled using the
* property {@link SimulationResults#isResetEnabled()}, in which case an HTTP 501 "Not
* Implemented" code will be returned.
*
* @return the {@link ResponseEntity} with status {@code 201 (NO CONTENT)}.
*/
@DeleteMapping("/test-results")
public ResponseEntity<Void> deleteAllTestResults() {
logger.debug("REST request to delete all TestResults");
testResultService.deleteAll();
return ResponseEntity.noContent().build();
if (simulatorConfigurationProperties.getSimulationResults().isResetEnabled()) {
logger.debug("REST request to delete all TestResults");
testResultService.deleteAll();
return ResponseEntity.noContent().build();
} else {
logger.warn("REST request to delete all TestResults, but reset is disabled!");
return ResponseEntity.status(NOT_IMPLEMENTED)
.header(
"message",
"Resetting TestResults is disabled on this simulator, see property 'citrus.simulator.simulation-results.reset-enabled' for more information!")
.build();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ management.info.git.mode=full
# Properties for the about page
info.simulator.name=Citrus Simulator
info.simulator.version=@project.version@
info.config.reset-results-enabled=${citrus.simulator.simulation-results.reset-enabled:true}

# Logging
logging.level.org.citrusframework.simulator=INFO
Expand Down
Loading

0 comments on commit 1329b57

Please sign in to comment.