Skip to content

Commit

Permalink
fix(simulator-spring-boot): indicate simulation failure with http status
Browse files Browse the repository at this point in the history
  • Loading branch information
bbortt committed May 13, 2024
1 parent 4e42ef3 commit 4a4ffcd
Show file tree
Hide file tree
Showing 45 changed files with 1,004 additions and 538 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2017 the original author or authors.
* Copyright 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.
Expand All @@ -16,33 +16,33 @@

package org.citrusframework.simulator.sample.scenario;

import static org.citrusframework.actions.EchoAction.Builder.echo;
import static org.citrusframework.actions.FailAction.Builder.fail;
import static org.citrusframework.dsl.MessageSupport.MessageBodySupport.fromBody;

import org.citrusframework.simulator.scenario.AbstractSimulatorScenario;
import org.citrusframework.simulator.scenario.Scenario;
import org.citrusframework.simulator.scenario.ScenarioRunner;
import org.springframework.http.HttpStatus;
import org.citrusframework.simulator.scenario.SimulatorScenario;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import static org.citrusframework.actions.EchoAction.Builder.echo;
import static org.citrusframework.actions.FailAction.Builder.fail;

/**
* @author Christoph Deppisch
* This scenario fails expectantly, using the {@link org.citrusframework.actions.FailAction.Builder#fail(String)}
* method. From the view point of a {@link SimulatorScenario}, there is nothing wrong with it. It should therefore be
* viewed as a "successful simulation".
* <p>
* In contrary to this, the {@link ThrowScenario} does fail in an "uncontrolled" manner (by throwing an exception at
* runtime), therefore results in a "failed simulation".
*/
@Scenario("Fail")
@RequestMapping(value = "/services/rest/simulator/fail", method = RequestMethod.POST)
@RequestMapping(value = "/services/rest/simulator/fail", method = RequestMethod.GET)
public class FailScenario extends AbstractSimulatorScenario {

@Override
public void run(ScenarioRunner scenario) {
scenario.$(scenario.http()
.receive()
.post()
.message()
.body("<Failure xmlns=\"http://citrusframework.org/schemas/failure\">" +
"Fail!" +
"</Failure>"));
.get());

scenario.$(echo("Careful - I am gonna fail successfully (:"));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 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.sample.scenario;

import org.citrusframework.exceptions.CitrusRuntimeException;
import org.citrusframework.simulator.scenario.AbstractSimulatorScenario;
import org.citrusframework.simulator.scenario.Scenario;
import org.citrusframework.simulator.scenario.ScenarioRunner;
import org.citrusframework.simulator.scenario.SimulatorScenario;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import static org.citrusframework.actions.EchoAction.Builder.echo;

/**
* This scenario fails at runtime, unexpectedly. It must therefore be reported as failed {@link SimulatorScenario}.
* <p>
* On the other hand, if a simulation fails on purpose, see {@link FailScenario}, it must be a "successful simulation".
*/
@Scenario("Throw")
@RequestMapping(value = "/services/rest/simulator/throw", method = RequestMethod.GET)
public class ThrowScenario extends AbstractSimulatorScenario {

@Override
public void run(ScenarioRunner scenario) {
scenario.$(scenario.http()
.receive()
.get());

scenario.$(echo("Careful - I am gonna fail (:"));

throw new CitrusRuntimeException("Every failure is a step to success.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -227,22 +227,51 @@ public void testInterveningRequest() {
}

/**
* Sends a request to the server expecting it to purposefully fail a simulation.
* Sends a request to the server, expecting it to purposefully fail a simulation. The response code must therefore
* be {@link HttpStatus#OK}.
*
* @see org.citrusframework.simulator.sample.scenario.FailScenario
*/
@CitrusTest
public void testFailingSimulation() {
public void testSimulationFailingExpectantly() {
$(http().client(simulatorClient)
.send()
.post("fail")
.get("fail"));

$(http().client(simulatorClient)
.receive()
.response(HttpStatus.OK));
}

/**
* Sends a request to the server, expecting it to execute a simulation. The response should indicate the unexpected
* error, returning a {@link HttpStatus#INTERNAL_SERVER_ERROR}.
*
* @see org.citrusframework.simulator.sample.scenario.ThrowScenario
*/
@CitrusTest
public void testSimulationWithUnexpectedError() {
$(http().client(simulatorClient)
.send()
.get("throw")
.message()
.contentType(MediaType.APPLICATION_XML_VALUE)
.body("<Failure xmlns=\"http://citrusframework.org/schemas/failure\">" +
"Fail!" +
"</Failure>"));
.accept(MediaType.APPLICATION_JSON_VALUE));

$(http().client(simulatorClient)
.receive()
.response(HttpStatus.OK)); // TODO: Pretty sure this should be HttpStatus.INTERNAL_SERVER_ERROR
.response(HttpStatus.INTERNAL_SERVER_ERROR)
.message()
.body(
// language=json
"""
{
"timestamp":"@ignore@",
"status":555,
"error":"Http Status 555",
"path":"/services/rest/simulator/throw"
}
"""
));
}

@Configuration
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 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.endpoint;

import org.citrusframework.message.DefaultMessage;

/**
* An implementation of a {@link org.citrusframework.message.Message} that hints that a simulation has failed. Because
* of the nature of {@link java.util.concurrent.CompletableFuture<org.citrusframework.message.Message>}'s, there is no
* other way than mapping the response message in case of an error. Exception-propagation from the asynchronous thread
* back into the parent does not exist.
*
* @see SimulatorEndpointAdapter
*/
public class SimulationFailedUnexpectedlyException extends DefaultMessage {

public static final String EXCEPTION_TYPE = SimulationFailedUnexpectedlyException.class.getSimpleName() + ":Exception";

public SimulationFailedUnexpectedlyException(Throwable e) {
super(e);
}

@Override
public String getType() {
return EXCEPTION_TYPE;
}
}
Loading

0 comments on commit 4a4ffcd

Please sign in to comment.