package com.xebialabs.restito.semantics;
import com.xebialabs.restito.builder.ensure.EnsureHttp;
import org.glassfish.grizzly.http.server.Response;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import static com.xebialabs.restito.semantics.Action.composite;
import static com.xebialabs.restito.semantics.Action.noop;
/**
* <p>Stub is not responsible for decision whether to execute action or not (e.g. when request matches XXX => do YYY)</p>
* <p>Just a wrapper for the {@link Action} which will should be performed when {@link Condition} is true.</p>
*
* @see com.xebialabs.restito.semantics.Action
* @see com.xebialabs.restito.semantics.Condition
*/
public class Stub {
private Condition when = Condition.custom(Predicates.alwaysTrue());
private Applicable action = noop();
private List<Applicable> actionSequence = new CopyOnWriteArrayList<>();
private Applicable exceededAction = null;
private int appliedTimes = 0;
/**
* How many times stub has been called
*/
private int expectedTimes = 0;
/**
* Should the sequence be completed or not in order to pass {@link EnsureHttp#gotStubsCommitmentsDone}
*/
private Boolean expectSequenceCompleted = false;
/**
* Creates a stub with action and condition
*/
public Stub(Condition when, Applicable action) {
this.when = when;
this.action = action;
}
public Stub(Condition when) {
this.when = when;
}
/**
* Creates a stub with action and condition
*/
public Stub(Condition when, ActionSequence actionSequence) {
this.when = when;
this.actionSequence.addAll(actionSequence.getActions());
}
/**
* Appends an extra condition to the stub.
*/
public Stub alsoWhen(final Condition extraCondition) {
this.when = Condition.composite(this.when, extraCondition);
return this;
}
/**
* Appends an extra action to the stub
*/
public Stub withExtraAction(final Applicable extraAction) {
action = composite(action, extraAction);
return this;
}
public Stub withSequenceItem(final Applicable nextWhat) {
actionSequence.add(nextWhat);
return this;
}
/**
* Checks whether the call satisfies condition of this stub
*/
public boolean isApplicable(Call call) {
return when.getPredicate().test(call) && (actionSequence.size() == 0 || exceededAction != null || actionSequence.size() > appliedTimes);
}
/**
* Executes all actions against the response.
*/
public Response apply(Response response) {
if (when instanceof ConditionWithApplicables) {
for (Applicable applicable : ((ConditionWithApplicables) when).getApplicables()) {
response = applicable.apply(response);
}
}
Applicable chosenAction;
if (actionSequence.isEmpty()) {
chosenAction = action;
} else if (actionSequence.size() > appliedTimes) {
chosenAction = composite(action, actionSequence.get(appliedTimes));
} else if(exceededAction != null) {
chosenAction = exceededAction;
} else {
chosenAction = action;
}
response = chosenAction.apply(response);
appliedTimes++;
return response;
}
public Stub withAction(Applicable action) {
this.action = action;
return this;
}
public Stub withActionSequence(List<Applicable> actionSequence) {
this.actionSequence = new CopyOnWriteArrayList<>(actionSequence);
return this;
}
public Stub withExceededAction(Applicable exceededAction) {
this.exceededAction = exceededAction;
return this;
}
public int getAppliedTimes() {
return appliedTimes;
}
public void setExpectedTimes(int expectedTimes) {
this.expectedTimes = expectedTimes;
}
public void setExpectSequenceCompleted(Boolean expectSequenceCompleted) {
this.expectSequenceCompleted = expectSequenceCompleted;
}
public List<Applicable> getActionSequence() {
return actionSequence;
}
public Boolean getExpectSequenceCompleted() {
return expectSequenceCompleted;
}
/**
* Get how many times stub expected to be called
*/
public int getExpectedTimes() {
return expectedTimes;
}
@Override
public String toString() {
return "Stub@" + this.hashCode();
}
}