/* * Copyright 2013 Netflix, Inc. * * 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 feign; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.junit.Rule; import org.junit.Test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import feign.codec.Decoder; import feign.codec.Encoder; import static feign.assertj.MockWebServerAssertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public class FeignBuilderTest { @Rule public final MockWebServer server = new MockWebServer(); @Test public void testDefaults() throws Exception { server.enqueue(new MockResponse().setBody("response data")); String url = "http://localhost:" + server.getPort(); TestInterface api = Feign.builder().target(TestInterface.class, url); Response response = api.codecPost("request data"); assertEquals("response data", Util.toString(response.body().asReader())); assertThat(server.takeRequest()) .hasBody("request data"); } /** Shows exception handling isn't required to coerce 404 to null or empty */ @Test public void testDecode404() throws Exception { server.enqueue(new MockResponse().setResponseCode(404)); server.enqueue(new MockResponse().setResponseCode(404)); server.enqueue(new MockResponse().setResponseCode(400)); String url = "http://localhost:" + server.getPort(); TestInterface api = Feign.builder().decode404().target(TestInterface.class, url); assertThat(api.getQueues("/")).isEmpty(); // empty, not null! assertThat(api.decodedPost()).isNull(); // null, not empty! try { // ensure other 400 codes are not impacted. api.decodedPost(); failBecauseExceptionWasNotThrown(FeignException.class); } catch (FeignException e) { assertThat(e.status()).isEqualTo(400); } } @Test public void testUrlPathConcatUrlTrailingSlash() throws Exception { server.enqueue(new MockResponse().setBody("response data")); String url = "http://localhost:" + server.getPort() + "/"; TestInterface api = Feign.builder().target(TestInterface.class, url); api.codecPost("request data"); assertThat(server.takeRequest()).hasPath("/"); } @Test public void testUrlPathConcatNoPathOnRequestLine() throws Exception { server.enqueue(new MockResponse().setBody("response data")); String url = "http://localhost:" + server.getPort() + "/"; TestInterface api = Feign.builder().target(TestInterface.class, url); api.getNoPath(); assertThat(server.takeRequest()).hasPath("/"); } @Test public void testUrlPathConcatNoInitialSlashOnPath() throws Exception { server.enqueue(new MockResponse().setBody("response data")); String url = "http://localhost:" + server.getPort() + "/"; TestInterface api = Feign.builder().target(TestInterface.class, url); api.getNoInitialSlashOnSlash(); assertThat(server.takeRequest()).hasPath("/api/thing"); } @Test public void testUrlPathConcatNoInitialSlashOnPathNoTrailingSlashOnUrl() throws Exception { server.enqueue(new MockResponse().setBody("response data")); String url = "http://localhost:" + server.getPort(); TestInterface api = Feign.builder().target(TestInterface.class, url); api.getNoInitialSlashOnSlash(); assertThat(server.takeRequest()).hasPath("/api/thing"); } @Test public void testOverrideEncoder() throws Exception { server.enqueue(new MockResponse().setBody("response data")); String url = "http://localhost:" + server.getPort(); Encoder encoder = new Encoder() { @Override public void encode(Object object, Type bodyType, RequestTemplate template) { template.body(object.toString()); } }; TestInterface api = Feign.builder().encoder(encoder).target(TestInterface.class, url); api.encodedPost(Arrays.asList("This", "is", "my", "request")); assertThat(server.takeRequest()) .hasBody("[This, is, my, request]"); } @Test public void testOverrideDecoder() throws Exception { server.enqueue(new MockResponse().setBody("success!")); String url = "http://localhost:" + server.getPort(); Decoder decoder = new Decoder() { @Override public Object decode(Response response, Type type) { return "fail"; } }; TestInterface api = Feign.builder().decoder(decoder).target(TestInterface.class, url); assertEquals("fail", api.decodedPost()); assertEquals(1, server.getRequestCount()); } @Test public void testProvideRequestInterceptors() throws Exception { server.enqueue(new MockResponse().setBody("response data")); String url = "http://localhost:" + server.getPort(); RequestInterceptor requestInterceptor = new RequestInterceptor() { @Override public void apply(RequestTemplate template) { template.header("Content-Type", "text/plain"); } }; TestInterface api = Feign.builder().requestInterceptor(requestInterceptor).target(TestInterface.class, url); Response response = api.codecPost("request data"); assertEquals(Util.toString(response.body().asReader()), "response data"); assertThat(server.takeRequest()) .hasHeaders("Content-Type: text/plain") .hasBody("request data"); } @Test public void testProvideInvocationHandlerFactory() throws Exception { server.enqueue(new MockResponse().setBody("response data")); String url = "http://localhost:" + server.getPort(); final AtomicInteger callCount = new AtomicInteger(); InvocationHandlerFactory factory = new InvocationHandlerFactory() { private final InvocationHandlerFactory delegate = new Default(); @Override public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) { callCount.incrementAndGet(); return delegate.create(target, dispatch); } }; TestInterface api = Feign.builder().invocationHandlerFactory(factory).target(TestInterface.class, url); Response response = api.codecPost("request data"); assertEquals("response data", Util.toString(response.body().asReader())); assertEquals(1, callCount.get()); assertThat(server.takeRequest()) .hasBody("request data"); } @Test public void testSlashIsEncodedInPathParams() throws Exception { server.enqueue(new MockResponse().setBody("response data")); String url = "http://localhost:" + server.getPort(); TestInterface api = Feign.builder().target(TestInterface.class, url); api.getQueues("/"); assertThat(server.takeRequest()) .hasPath("/api/queues/%2F"); } @Test public void testBasicDefaultMethod() throws Exception { String url = "http://localhost:" + server.getPort(); TestInterface api = Feign.builder().target(TestInterface.class, url); String result = api.independentDefaultMethod(); assertThat(result.equals("default result")); } @Test public void testDefaultCallingProxiedMethod() throws Exception { server.enqueue(new MockResponse().setBody("response data")); String url = "http://localhost:" + server.getPort(); TestInterface api = Feign.builder().target(TestInterface.class, url); Response response = api.defaultMethodPassthrough(); assertEquals("response data", Util.toString(response.body().asReader())); assertThat(server.takeRequest()).hasPath("/"); } interface TestInterface { @RequestLine("GET") Response getNoPath(); @RequestLine("GET api/thing") Response getNoInitialSlashOnSlash(); @RequestLine("POST /") Response codecPost(String data); @RequestLine("POST /") void encodedPost(List<String> data); @RequestLine("POST /") String decodedPost(); @RequestLine(value = "GET /api/queues/{vhost}", decodeSlash = false) byte[] getQueues(@Param("vhost") String vhost); default String independentDefaultMethod() { return "default result"; } default Response defaultMethodPassthrough() { return getNoPath(); } } }