Class MockDecisionModel
- All Implemented Interfaces:
DecisionModel
DecisionModel for tests: it needs no network, answers the way the test tells it to, and records the
requests it receives.
Script the answers step by step, one step per request. For example, a model that answers every yes/no question with yes:
MockDecisionModel model = MockDecisionModel.thatAnswersYesNoQuestions(question -> 0.9).thenRepeats();
Every that... method creates a mock with its first step, and every then... method adds a step. Each
step answers exactly one request. A request after the last step fails with an IllegalStateException that
shows the questions of the request, so a test notices when the code asks the model more often than the script
expects, unless the code under test handles the error itself. decide(...) throws it, and
decideAsync(...) returns a future that fails with it. To answer every further request with the last step,
end the script with thenRepeats().
A script can give different answers to the same question, for example to a loop that asks whether its task is done and stops at the third answer:
MockDecisionModel model = MockDecisionModel.thatAnswersYesNoQuestions(question -> 0.1)
.thenAnswersYesNoQuestions(question -> 0.1)
.thenAnswersYesNoQuestions(question -> 0.9);
It can also let the first request fail and the next one succeed, for example to test your own retry logic:
MockDecisionModel model = MockDecisionModel.thatFails(new RateLimitException("Too many requests"))
.thenAnswersYesNoQuestions(question -> 0.9);
A step can compute its answers from the request with thatAnswers(Function),
thatAnswersQuestions(Function) or thatAnswersYesNoQuestions(ToDoubleFunction), for example to
answer concurrent requests by their questions: thatAnswersQuestions(question -> ...).thenRepeats(). If the
function throws, the request fails with that exception, unchanged.
Like on every mock in this package, thatResponds(...) and thenResponds(...) give a step as the whole
response, here a DecisionResponse: the request is answered with the answers and the metadata of that
response, for example the token usage that a cost check reads, checked like the answers of a real model, so a choice
answer then also carries the options of its question.
The model checks its answers like a real one: a request with a question that got no answer fails with an
InvalidDecisionResponseException. Unless a step gives the whole response, a response reports the model name
of the request.
decideAsync(...) completes on another thread, where a step that computes its answers also computes them.
A mock remembers which steps were taken and which requests it received, so create a new one for each test. A mock
shared between tests, for example as a bean, fails with a clear message as soon as its script runs out, unless the
script ends with thenRepeats(). The steps are taken in the order the requests arrive. A mock keeps every
request it receives, so it is not meant for load tests or other long runs.
-
Method Summary
Modifier and TypeMethodDescriptiondoDecide(DecisionRequest request) The provider hook behindDecisionModel.decide(DecisionRequest).doDecideAsync(DecisionRequest request) Answers likedoDecide(DecisionRequest), with the next step of the script, and completes the future on another thread, where a step that computes its answers also computes them.The last request received so far.The listeners notified of every request, response and error of this model.request()The only request received so far.requests()A copy of the requests received so far, in the order they arrived.static MockDecisionModelthatAnswers(Function<? super DecisionRequest, ? extends Map<String, ? extends DecisionAnswer>> answers) A model that answers the first request with the answers computed from it, keyed by question name.static MockDecisionModelthatAnswers(Map<String, ? extends DecisionAnswer> answers) A model that answers the first request with the given answers, keyed by question name.static MockDecisionModelthatAnswersQuestions(Function<Question, ? extends DecisionAnswer> answer) A model that answers every question of the first request with the answer computed from the question.static MockDecisionModelthatAnswersYesNoQuestions(ToDoubleFunction<YesNoQuestion> probability) A model that answers every yes/no question of the first request with the probability of "yes", from 0 to 1, computed from the question.static MockDecisionModelthatFails(RuntimeException exception) A model whose first request fails with exactly the given exception.static MockDecisionModelthatResponds(DecisionResponse response) A model that answers the first request with the answers and the metadata of the given response, for example its token usage or model name, to test code that reads them, such as a cost check.thenAnswers(Function<? super DecisionRequest, ? extends Map<String, ? extends DecisionAnswer>> answers) Adds a step: the next request is answered with the answers computed from it, keyed by question name.thenAnswers(Map<String, ? extends DecisionAnswer> answers) Adds a step: the next request is answered with the given answers, keyed by question name.thenAnswersQuestions(Function<Question, ? extends DecisionAnswer> answer) Adds a step: every question of the next request is answered with the answer computed from the question.thenAnswersYesNoQuestions(ToDoubleFunction<YesNoQuestion> probability) Adds a step: every yes/no question of the next request is answered with the probability of "yes", from 0 to 1, computed from the question, seethatAnswersYesNoQuestions(ToDoubleFunction).thenFails(RuntimeException exception) Adds a step: the next request fails with exactly the given exception.Answers every further request with the last step of the script.thenResponds(DecisionResponse response) Adds a step: the next request is answered with the answers and the metadata of the given response, for example its token usage or model name, seethatResponds(DecisionResponse).withListeners(DecisionModelListener... listeners) Notifies the given listeners of each call, like a real model does.withListeners(List<DecisionModelListener> listeners) Notifies the given listeners of each call, like a real model does.Methods inherited from class Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface DecisionModel
decide, decideAsync, defaultRequestParameters, modelName, provider
-
Method Details
-
thenAnswers
Adds a step: the next request is answered with the given answers, keyed by question name.- Throws:
IllegalArgumentException- if the answers are empty, or if a name is blank or an answer isnullIllegalStateException- if the script already ends withthenRepeats()
-
thenAnswers
public MockDecisionModel thenAnswers(Function<? super DecisionRequest, ? extends Map<String, ? extends DecisionAnswer>> answers) Adds a step: the next request is answered with the answers computed from it, keyed by question name.- Throws:
IllegalStateException- if the script already ends withthenRepeats()
-
thenAnswersQuestions
Adds a step: every question of the next request is answered with the answer computed from the question.- Throws:
IllegalStateException- if the script already ends withthenRepeats()
-
thenAnswersYesNoQuestions
Adds a step: every yes/no question of the next request is answered with the probability of "yes", from 0 to 1, computed from the question, seethatAnswersYesNoQuestions(ToDoubleFunction).- Throws:
IllegalStateException- if the script already ends withthenRepeats()
-
thenResponds
Adds a step: the next request is answered with the answers and the metadata of the given response, for example its token usage or model name, seethatResponds(DecisionResponse).- Throws:
IllegalStateException- if the script already ends withthenRepeats()
-
thenFails
Adds a step: the next request fails with exactly the given exception.- Throws:
IllegalStateException- if the script already ends withthenRepeats()
-
thenRepeats
Answers every further request with the last step of the script. Only the last step repeats; the script does not start over. A last step that computes its answers computes them again for each request. Without it, a request after the last step fails. It must be the last call of the script.- Throws:
IllegalStateException- if the script already ends withthenRepeats()
-
doDecide
Description copied from interface:DecisionModelThe provider hook behindDecisionModel.decide(DecisionRequest).- Specified by:
doDecidein interfaceDecisionModel- Parameters:
request- the request, with the default parameters already applied.- Returns:
- one answer per question, keyed by question name.
-
doDecideAsync
Answers likedoDecide(DecisionRequest), with the next step of the script, and completes the future on another thread, where a step that computes its answers also computes them. It never throws: anullrequest gives a failed future.- Specified by:
doDecideAsyncin interfaceDecisionModel- Parameters:
request- the request, with the default parameters already applied.- Returns:
- a
CompletableFutureof the answers, keyed by question name.
-
requests
A copy of the requests received so far, in the order they arrived. -
request
The only request received so far.- Throws:
IllegalStateException- if the mock received no request or several requests.
-
lastRequest
The last request received so far.- Throws:
IllegalStateException- if the mock received no request
-
thatAnswers
A model that answers the first request with the given answers, keyed by question name. A question without an answer makes the request fail, seeMockDecisionModel.thatAnswersQuestions(Function)andthatAnswersYesNoQuestions(ToDoubleFunction)need no question names. Add the next steps withthen...methods, or answer every request with these answers:thatAnswers(answers).thenRepeats().- Throws:
IllegalArgumentException- if the answers are empty, or if a name is blank or an answer isnull
-
thatAnswers
public static MockDecisionModel thatAnswers(Function<? super DecisionRequest, ? extends Map<String, ? extends DecisionAnswer>> answers) A model that answers the first request with the answers computed from it, keyed by question name. Add the next steps withthen...methods, or compute the answers to every request withthenRepeats(). -
thatAnswersQuestions
public static MockDecisionModel thatAnswersQuestions(Function<Question, ? extends DecisionAnswer> answer) A model that answers every question of the first request with the answer computed from the question. Add the next steps withthen...methods, or answer the questions of every request withthenRepeats(). -
thatAnswersYesNoQuestions
public static MockDecisionModel thatAnswersYesNoQuestions(ToDoubleFunction<YesNoQuestion> probability) A model that answers every yes/no question of the first request with the probability of "yes", from 0 to 1, computed from the question. A request with a question that is not a yes/no question fails with anIllegalStateException; to answer other questions, usethatAnswersQuestions(Function)orthatAnswers(Map). Add the next steps withthen...methods, or answer the questions of every request withthenRepeats(). -
thatResponds
A model that answers the first request with the answers and the metadata of the given response, for example its token usage or model name, to test code that reads them, such as a cost check. The model still checks the answers like a real one: a request with a question that the response does not answer fails, and a choice answer gets the options of its question, so the response is not exactly the given one. Add the next steps withthen...methods, or answer every request with this response:thatResponds(response).thenRepeats(). -
thatFails
A model whose first request fails with exactly the given exception. To let every request fail, addthenRepeats(). To let later requests succeed, for example to test your own retry logic, add steps withthen...methods. -
withListeners
Notifies the given listeners of each call, like a real model does. -
withListeners
Notifies the given listeners of each call, like a real model does. -
listeners
Description copied from interface:DecisionModelThe listeners notified of every request, response and error of this model.- Specified by:
listenersin interfaceDecisionModel
-