/* * * Copyright 2017 Christopher Pilsworth * * 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 io.github.resilience4j.retrofit; import com.github.tomakehurst.wiremock.junit.WireMockRule; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RateLimiterConfig; import okhttp3.OkHttpClient; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.scalars.ScalarsConverterFactory; import java.io.IOException; import java.time.Duration; import java.util.concurrent.TimeUnit; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; /** * Tests the integration of the Retrofit HTTP client and {@link RateLimiter} */ public class RetrofitRateLimiterTest { @Rule public WireMockRule wireMockRule = new WireMockRule(); private static final RateLimiterConfig config = RateLimiterConfig.custom() .timeoutDuration(Duration.ofMillis(50)) .limitRefreshPeriod(Duration.ofSeconds(1)) .limitForPeriod(1) .build(); private RetrofitService service; private RateLimiter rateLimiter; @Before public void setUp() { final long TIMEOUT = 300; // ms OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .readTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .build(); this.rateLimiter = RateLimiter.of("backendName", config); this.service = new Retrofit.Builder() .addCallAdapterFactory(RateLimiterCallAdapter.of(rateLimiter)) .addConverterFactory(ScalarsConverterFactory.create()) .client(client) .baseUrl("http://localhost:8080/") .build() .create(RetrofitService.class); } @Test public void decorateSuccessfulCall() throws Exception { stubFor(get(urlPathEqualTo("/greeting")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "text/plain") .withBody("hello world"))); service.greeting().execute(); verify(1, getRequestedFor(urlPathEqualTo("/greeting"))); } @Test(expected = IOException.class) public void shouldNotCatchCallExceptionsInRateLimiter() throws Exception { stubFor(get(urlPathEqualTo("/greeting")) .willReturn(aResponse() .withStatus(200) .withFixedDelay(400))); service.greeting().execute(); } @Test public void decorateRateLimitedCall() throws Exception { stubFor(get(urlPathEqualTo("/greeting")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "text/plain") .withBody("hello world"))); final Response<String> execute = service.greeting().execute(); assertThat(execute.isSuccessful()) .describedAs("Response successful") .isTrue(); final Response<String> rateLimitedResponse = service.greeting().execute(); assertThat(rateLimitedResponse.isSuccessful()) .describedAs("Response successful") .isFalse(); assertThat(rateLimitedResponse.code()) .describedAs("HTTP Error Code") .isEqualTo(429); } @Test(expected = IllegalArgumentException.class) public void shouldThrowOnBadService() { BadRetrofitService badService = new Retrofit.Builder() .addCallAdapterFactory(RateLimiterCallAdapter.of(rateLimiter)) .baseUrl("http://localhost:8080/") .build() .create(BadRetrofitService.class); badService.greeting(); } }