diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientExceptionTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientExceptionTests.java new file mode 100644 index 000000000..f2f2766c5 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientExceptionTests.java @@ -0,0 +1,136 @@ +/* + * Copyright 2013-2016 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.springframework.cloud.sleuth.instrument.web.client; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + +import org.junit.After; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.boot.test.WebIntegrationTest; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.netflix.feign.EnableFeignClients; +import org.springframework.cloud.netflix.feign.FeignClient; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.assertions.SleuthAssertions; +import org.springframework.cloud.sleuth.trace.TestSpanContextHolder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.rules.SpringClassRule; +import org.springframework.test.context.junit4.rules.SpringMethodRule; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.client.RestTemplate; + +import com.netflix.loadbalancer.BaseLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +import static junitparams.JUnitParamsRunner.$; + +@RunWith(JUnitParamsRunner.class) +@SpringApplicationConfiguration(classes = { WebClientExceptionTests.TestConfiguration.class }) +@WebIntegrationTest(value = { "spring.application.name=exceptionservice" }, randomPort = true) +public class WebClientExceptionTests { + + @ClassRule public static final SpringClassRule SCR = new SpringClassRule(); + @Rule public final SpringMethodRule springMethodRule = new SpringMethodRule(); + + @Autowired TestFeignInterfaceWithException testFeignInterfaceWithException; + @Autowired @LoadBalanced RestTemplate template; + @Autowired Tracer tracer; + + @After + public void close() { + TestSpanContextHolder.removeCurrentSpan(); + } + + // issue #198 + @Test + @Parameters + @SuppressWarnings("unchecked") + public void shouldCloseSpanUponException(ResponseEntityProvider provider) throws IOException { + Span span = this.tracer.createSpan("new trace"); + + try { + provider.get(this); + Assert.fail("should throw an exception"); + } catch (RuntimeException e) { + SleuthAssertions.then(e).hasRootCauseInstanceOf(IOException.class); + } + + SleuthAssertions.then(this.tracer.getCurrentSpan()).isEqualTo(span); + this.tracer.close(span); + } + + private Object[] parametersForShouldCloseSpanUponException() { + return $((ResponseEntityProvider) (tests) -> tests.testFeignInterfaceWithException.shouldFailToConnect(), + (ResponseEntityProvider) (tests) -> tests.template.getForEntity("http://exceptionservice/", Map.class)); + } + + @FeignClient("exceptionservice") + public interface TestFeignInterfaceWithException { + @RequestMapping(method = RequestMethod.GET, value = "/") + ResponseEntity shouldFailToConnect(); + } + + @Configuration + @EnableAutoConfiguration + @EnableFeignClients + @RibbonClient(value = "exceptionservice", configuration = ExceptionServiceRibbonClientConfiguration.class) + public static class TestConfiguration { + + @LoadBalanced + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + } + + + @Configuration + public static class ExceptionServiceRibbonClientConfiguration { + + @Bean + public ILoadBalancer exceptionServiceRibbonLoadBalancer() { + BaseLoadBalancer balancer = new BaseLoadBalancer(); + balancer.setServersList( + Collections.singletonList(new Server("invalid.host.to.break.tests", 1234))); + return balancer; + } + + } + + @FunctionalInterface + interface ResponseEntityProvider { + ResponseEntity get(WebClientExceptionTests webClientTests); + } +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientTests.java index f510d1b81..b5880bc6e 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientTests.java @@ -16,7 +16,6 @@ package org.springframework.cloud.sleuth.instrument.web.client; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -25,7 +24,6 @@ import java.util.Map; import java.util.Random; import org.junit.After; -import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -38,10 +36,9 @@ import org.springframework.boot.test.WebIntegrationTest; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.feign.EnableFeignClients; import org.springframework.cloud.netflix.feign.FeignClient; -import org.springframework.cloud.netflix.ribbon.RibbonClients; +import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; -import org.springframework.cloud.sleuth.assertions.SleuthAssertions; import org.springframework.cloud.sleuth.event.ClientReceivedEvent; import org.springframework.cloud.sleuth.event.ClientSentEvent; import org.springframework.cloud.sleuth.trace.TestSpanContextHolder; @@ -79,7 +76,6 @@ public class WebClientTests { @Rule public final SpringMethodRule springMethodRule = new SpringMethodRule(); @Autowired TestFeignInterface testFeignInterface; - @Autowired TestFeignInterfaceWithException testFeignInterfaceWithException; @Autowired @LoadBalanced RestTemplate template; @Autowired Listener listener; @Autowired Tracer tracer; @@ -171,29 +167,6 @@ public class WebClientTests { (ResponseEntityProvider) (tests) -> tests.template.getForEntity("http://fooservice/noresponse", String.class)); } - // issue #198 - @Test - @Parameters - @SuppressWarnings("unchecked") - public void shouldCloseSpanUponException(ResponseEntityProvider provider) throws IOException { - Span span = this.tracer.createSpan("new trace"); - - try { - provider.get(this); - Assert.fail("should throw an exception"); - } catch (RuntimeException e) { - SleuthAssertions.then(e).hasRootCauseInstanceOf(IOException.class); - } - - SleuthAssertions.then(this.tracer.getCurrentSpan()).isEqualTo(span); - this.tracer.close(span); - } - - private Object[] parametersForShouldCloseSpanUponException() { - return $((ResponseEntityProvider) (tests) -> tests.testFeignInterfaceWithException.shouldFailToConnect(), - (ResponseEntityProvider) (tests) -> tests.template.getForEntity("http://exceptionService/", Map.class)); - } - private void thenRegisteredClientSentAndReceivedEvents() { then(this.listener.getEvents().size()).isEqualTo(2); then(this.listener.getEvents().get(0)).isExactlyInstanceOf(ClientSentEvent.class); @@ -224,16 +197,10 @@ public class WebClientTests { ResponseEntity noResponseBody(); } - @FeignClient(name = "exceptionService", url = "http://invalid.host.to.break.tests") - public interface TestFeignInterfaceWithException { - @RequestMapping(method = RequestMethod.GET, value = "/") - ResponseEntity shouldFailToConnect(); - } - @Configuration @EnableAutoConfiguration @EnableFeignClients - @RibbonClients(defaultConfiguration = SimpleRibbonClientConfiguration.class) + @RibbonClient(value = "fooservice", configuration = SimpleRibbonClientConfiguration.class) public static class TestConfiguration { @Bean @@ -309,7 +276,6 @@ public class WebClientTests { then(parentId).isNotEmpty(); then(spanId).isNotEmpty(); } - } @Configuration @@ -325,7 +291,6 @@ public class WebClientTests { Collections.singletonList(new Server("localhost", this.port))); return balancer; } - } @FunctionalInterface