Skip to content

Commit

Permalink
Merge branch 'features/mockHandler' of https://github.com/ivangsa/karate
Browse files Browse the repository at this point in the history
 into features/mockHandler
  • Loading branch information
Ivan Garcia Sainz-Aja committed May 3, 2021
2 parents bc67f9a + 4e8c6b0 commit 9e4913b
Showing 1 changed file with 75 additions and 41 deletions.
116 changes: 75 additions & 41 deletions karate-core/src/main/java/com/intuit/karate/core/MockHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,7 @@
import com.intuit.karate.http.Response;
import com.intuit.karate.http.ServerHandler;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.slf4j.Logger;
Expand Down Expand Up @@ -73,7 +69,7 @@ public class MockHandler implements ServerHandler {
private final Map<String, Variable> globals = new HashMap<>();
private boolean corsEnabled;

protected static final ThreadLocal<Request> LOCAL_REQUEST = new ThreadLocal<Request>();
protected static final ThreadLocal<Request> LOCAL_REQUEST = new ThreadLocal<>();
private String prefix = "";

public MockHandler withPrefix(String prefix) {
Expand All @@ -87,7 +83,7 @@ public MockHandler(Feature feature) {
}

public MockHandler(Feature feature, Map<String, Object> args) {
this(Arrays.asList(feature), args);
this(Collections.singletonList(feature), args);
}

public MockHandler(List<Feature> features) {
Expand All @@ -102,15 +98,7 @@ public MockHandler(List<Feature> features, Map<String, Object> args) {
Scenario dummy = new Scenario(feature, section, -1);
section.setScenario(dummy);
ScenarioRuntime runtime = new ScenarioRuntime(featureRuntime, dummy);
runtime.engine.setVariable(PATH_MATCHES, (Function<String, Boolean>) this::pathMatches);
runtime.engine.setVariable(PARAM_EXISTS, (Function<String, Boolean>) this::paramExists);
runtime.engine.setVariable(PARAM_VALUE, (Function<String, String>) this::paramValue);
runtime.engine.setVariable(METHOD_IS, (Function<String, Boolean>) this::methodIs);
runtime.engine.setVariable(TYPE_CONTAINS, (Function<String, Boolean>) this::typeContains);
runtime.engine.setVariable(ACCEPT_CONTAINS, (Function<String, Boolean>) this::acceptContains);
runtime.engine.setVariable(HEADER_CONTAINS, (BiFunction<String, String, Boolean>) this::headerContains);
runtime.engine.setVariable(BODY_PATH, (Function<String, Object>) this::bodyPath);
runtime.engine.init();
initiateScenarioRunTime(runtime);
if (feature.isBackgroundPresent()) {
// if we are within a scenario already e.g. karate.start(), preserve context
ScenarioEngine prevEngine = ScenarioEngine.get();
Expand All @@ -135,6 +123,23 @@ public MockHandler(List<Feature> features, Map<String, Object> args) {
}
}

/**
* Parse Scenario values from feature file ex- pathMatches(), method type - get/post
* @param runtime ScenarioRuntime
*/
private void initiateScenarioRunTime(ScenarioRuntime runtime)
{
runtime.engine.setVariable(PATH_MATCHES, (Function<String, Boolean>) this::pathMatches);
runtime.engine.setVariable(PARAM_EXISTS, (Function<String, Boolean>) this::paramExists);
runtime.engine.setVariable(PARAM_VALUE, (Function<String, String>) this::paramValue);
runtime.engine.setVariable(METHOD_IS, (Function<String, Boolean>) this::methodIs);
runtime.engine.setVariable(TYPE_CONTAINS, (Function<String, Boolean>) this::typeContains);
runtime.engine.setVariable(ACCEPT_CONTAINS, (Function<String, Boolean>) this::acceptContains);
runtime.engine.setVariable(HEADER_CONTAINS, (BiFunction<String, String, Boolean>) this::headerContains);
runtime.engine.setVariable(BODY_PATH, (Function<String, Object>) this::bodyPath);
runtime.engine.init();
}

private static final Result PASSED = Result.passed(0);
private static final String ALLOWED_METHODS = "GET, HEAD, POST, PUT, DELETE, PATCH";

Expand All @@ -160,16 +165,8 @@ public synchronized Response handle(Request req) { // note the [synchronized]
Thread.currentThread().setContextClassLoader(runtime.featureRuntime.suite.classLoader);
LOCAL_REQUEST.set(req);
req.processBody();
ScenarioEngine engine = new ScenarioEngine(runtime, new HashMap(globals));
ScenarioEngine.set(engine);
engine.init();
engine.setVariable(ScenarioEngine.REQUEST_URL_BASE, req.getUrlBase());
engine.setVariable(ScenarioEngine.REQUEST_URI, req.getPath());
engine.setVariable(ScenarioEngine.REQUEST_METHOD, req.getMethod());
engine.setVariable(ScenarioEngine.REQUEST_HEADERS, req.getHeaders());
engine.setVariable(ScenarioEngine.REQUEST, req.getBodyConverted());
engine.setVariable(REQUEST_PARAMS, req.getParams());
engine.setVariable(REQUEST_BYTES, req.getBody());
//Create Scenario engine
ScenarioEngine engine = createScenarioEngine(req, runtime);
Map<String, List<Map<String, Object>>> parts = req.getMultiParts();
if (parts != null) {
engine.setHiddenVariable(REQUEST_PARTS, parts); // TODO add to docs
Expand All @@ -185,19 +182,8 @@ public synchronized Response handle(Request req) { // note the [synchronized]
Variable response, responseStatus, responseHeaders, responseDelay;
ScenarioActions actions = new ScenarioActions(engine);
Result result = PASSED;
for (Step step : scenario.getSteps()) {
result = StepRuntime.execute(step, actions);
if (result.isAborted()) {
runtime.logger.debug("abort at {}:{}", feature, step.getLine());
break;
}
if (result.isFailed()) {
String message = "server-side scenario failed, " + feature + ":" + step.getLine()
+ "\n" + step.toString() + "\n" + result.getError().getMessage();
runtime.logger.error(message);
break;
}
}
//Execute Steps in Scenario
result = executeScenarioSteps(feature, runtime, scenario, actions, result);
engine.mockAfterScenario();
configureHeaders = engine.mockConfigureHeaders();
response = engine.vars.remove(ScenarioEngine.RESPONSE);
Expand Down Expand Up @@ -241,6 +227,53 @@ public synchronized Response handle(Request req) { // note the [synchronized]
return new Response(404);
}

/**
* Execute steps for every scenario identified
* @param feature
* @param runtime
* @param scenario
* @param actions
* @param result
* @return
*/
private Result executeScenarioSteps(Feature feature,
ScenarioRuntime runtime,
Scenario scenario,
ScenarioActions actions,
Result result)
{
for (Step step : scenario.getSteps()) {
result = StepRuntime.execute(step, actions);
if (result.isAborted()) {
runtime.logger.debug("abort at {}:{}", feature, step.getLine());
break;
}
if (result.isFailed()) {
String message = "server-side scenario failed, " + feature + ":" + step.getLine()
+ "\n" + step.toString() + "\n" + result.getError().getMessage();
runtime.logger.error(message);
break;
}
}
return result;
}

private ScenarioEngine createScenarioEngine(Request req,
ScenarioRuntime runtime)
{
ScenarioEngine engine = new ScenarioEngine(runtime, new HashMap<>(globals));
ScenarioEngine.set(engine);
engine.init();
engine.setVariable(ScenarioEngine.REQUEST_URL_BASE, req.getUrlBase());
engine.setVariable(ScenarioEngine.REQUEST_URI, req.getPath());
engine.setVariable(ScenarioEngine.REQUEST_METHOD, req.getMethod());
engine.setVariable(ScenarioEngine.REQUEST_HEADERS, req.getHeaders());
engine.setVariable(ScenarioEngine.REQUEST, req.getBodyConverted());
engine.setVariable(REQUEST_PARAMS, req.getParams());
engine.setVariable(REQUEST_BYTES, req.getBody());
return engine;
}

private boolean isMatchingScenario(Scenario scenario, ScenarioEngine engine) {
String expression = StringUtils.trimToNull(scenario.getName() + scenario.getDescription());
if (expression == null) {
Expand Down Expand Up @@ -275,7 +308,8 @@ public boolean pathMatches(String pattern) {

public boolean paramExists(String name) {
Map<String, List<String>> params = LOCAL_REQUEST.get().getParams();
return params == null ? false : params.containsKey(name);
return params != null && params.containsKey(name);

}

public String paramValue(String name) {
Expand All @@ -293,7 +327,7 @@ public boolean typeContains(String text) {

public boolean acceptContains(String text) {
String acceptHeader = LOCAL_REQUEST.get().getHeader("Accept");
return acceptHeader == null ? false : acceptHeader.contains(text);
return acceptHeader != null && acceptHeader.contains(text);
}

public boolean headerContains(String name, String value) {
Expand Down

0 comments on commit 9e4913b

Please sign in to comment.