Blink
Blink

Reputation: 1554

Spring Integration mocking failure of outbound gateway

I'm trying to mock a failure of an outbound gateway in a unit test like this:

MessageHandler mockCognitiveAnalyze =
        mockMessageHandler().handleNextAndReply(m -> ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
this.mockIntegrationContext.substituteMessageHandlerFor("cognitiveServicesReadEndpoint", mockCognitiveAnalyze);

What I would expect is that when the cognitiveServicesReadEndpoint is invoked, it won't succeed and the flow should be finished (or in my case, a retry advice is applied so it should be repeated). But what happens is, no exception or error will be thrown and the next handle is invoked. The flow looks like this:

.handle(Http.outboundGateway(cognitiveServicesUri + "/vision/v3.0/read/analyze")
                .mappedRequestHeaders("Ocp-Apim-Subscription-Key")
                .mappedResponseHeaders("Operation-Location"),
        c -> c.advice(this.advices.retryAdvice())
                .id("cognitiveServicesReadEndpoint"))
.transform(p -> "")
.handle(Http.outboundGateway(h -> h.getHeaders().get("Operation-Location"))
        .mappedRequestHeaders("Ocp-Apim-Subscription-Key")
        .httpMethod(HttpMethod.GET)
        .expectedResponseType(String.class), this.advices.ocrSpec())

Any idea how can I setup the mock handler to properly throw an exception?

Side note: .transform(p -> "") is needed for proper handling of the headers, see Spring Integration HTTP Outbound Gateway header not forwarder on a consecutive request

UPDATE1

This is how the handler looks at the moment:

.handle(Http.outboundGateway(h -> String.format("%s%s", uri, h.getHeaders()
        .get(CustomHeaders.DOCUMENT_ID.name())))
        .httpMethod(HttpMethod.GET)
        .expectedResponseType(byte[].class), c -> c.advice(this.advices.retryAdvice())
        .id("endpoint1"))
.wireTap(sf -> sf.enrichHeaders(h -> h.header("ocp-apim-subscription-key", computerVisionApiKey))
        .handle(Http.outboundGateway(cognitiveServicesUri + "/vision/v3.0/read/analyze")
                        .mappedRequestHeaders("Ocp-Apim-Subscription-Key")
                        .mappedResponseHeaders("Operation-Location"),
                c -> c.advice(this.advices.retryAdvice())
                        .id("cognitiveServicesReadEndpoint"))

And the testing code:

MessageHandler mockCognitiveAnalyze = mockMessageHandler().handleNext(m -> {
    throw new HttpClientErrorException(HttpStatus.UNAUTHORIZED);
});
this.mockIntegrationContext.substituteMessageHandlerFor("cognitiveServicesReadEndpoint",
        mockCognitiveAnalyze);

Upvotes: 0

Views: 405

Answers (2)

Artem Bilan
Artem Bilan

Reputation: 121542

The MockMessageHandler.handleNextAndReply() provides a Function which is invoked in the handleMessageInternal(). Since there is no interaction with the Resttemplate (you fully substitute it), your HttpStatus.UNAUTHORIZED is not translated into an exception.

You probably should simulate a RestTemplate logic on the matter in that your handleNextAndReply() and throw respective exception instead.

The RestTemplate has the logic like this:

protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
    ResponseErrorHandler errorHandler = getErrorHandler();
    boolean hasError = errorHandler.hasError(response);
    if (logger.isDebugEnabled()) {
        try {
            int code = response.getRawStatusCode();
            HttpStatus status = HttpStatus.resolve(code);
            logger.debug("Response " + (status != null ? status : code));
        }
        catch (IOException ex) {
            // ignore
        }
    }
    if (hasError) {
        errorHandler.handleError(url, method, response);
    }
}

So, you need to borrow and idea from the errorHandler which is a DefaultResponseErrorHandler and can throw respective exception from the provided ClientHttpResponse. Or you just can go ahead and throw an HttpClientErrorException.Unauthorized directly.

UPDATE

Something like this:

MockMessageHandler mockMessageHandler = mockMessageHandler();
mockMessageHandler.handleNext(message -> { 
         throw new HttpClientErrorException.create(...);
});

this.mockIntegrationContext.substituteMessageHandlerFor("httpGateway", mockMessageHandler);

Upvotes: 2

Blink
Blink

Reputation: 1554

This is a final solution:

MessageHandler mockCognitiveAnalyze = mockMessageHandler().handleNextAndReply(m -> {
    throw new HttpClientErrorException(HttpStatus.UNAUTHORIZED);
});

Upvotes: 1

Related Questions