package com.ctrip.framework.apollo.portal; import com.ctrip.framework.apollo.common.exception.ServiceException; import com.ctrip.framework.apollo.core.dto.ServiceDTO; import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.portal.component.AdminServiceAddressLocator; import com.ctrip.framework.apollo.portal.component.RetryableRestTemplate; import org.apache.http.HttpHost; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.HttpHostConnectException; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; import java.net.SocketTimeoutException; import java.util.Arrays; import java.util.Collections; import static org.mockito.Matchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class RetryableRestTemplateTest extends AbstractUnitTest { @Mock private AdminServiceAddressLocator serviceAddressLocator; @Mock private RestTemplate restTemplate; @InjectMocks private RetryableRestTemplate retryableRestTemplate; private String path = "app"; private String serviceOne = "http://10.0.0.1"; private String serviceTwo = "http://10.0.0.2"; private String serviceThree = "http://10.0.0.3"; private ResourceAccessException socketTimeoutException = new ResourceAccessException(""); private ResourceAccessException httpHostConnectException = new ResourceAccessException(""); private ResourceAccessException connectTimeoutException = new ResourceAccessException(""); private Object request = new Object(); private ResponseEntity<Object> entity = new ResponseEntity<>(HttpStatus.OK); @Before public void init() { socketTimeoutException.initCause(new SocketTimeoutException()); httpHostConnectException .initCause(new HttpHostConnectException(new ConnectTimeoutException(), new HttpHost(serviceOne, 80))); connectTimeoutException.initCause(new ConnectTimeoutException()); } @Test(expected = ServiceException.class) public void testNoAdminServer() { when(serviceAddressLocator.getServiceList(any())).thenReturn(Collections.emptyList()); retryableRestTemplate.get(Env.DEV, path, Object.class); } @Test(expected = ServiceException.class) public void testAllServerDown() { when(serviceAddressLocator.getServiceList(any())) .thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree))); when(restTemplate.getForObject(serviceOne + "/" + path, Object.class)).thenThrow(socketTimeoutException); when(restTemplate.getForObject(serviceTwo + "/" + path, Object.class)).thenThrow(httpHostConnectException); when(restTemplate.getForObject(serviceThree + "/" + path, Object.class)).thenThrow(connectTimeoutException); retryableRestTemplate.get(Env.DEV, path, Object.class); verify(restTemplate).getForObject(serviceOne + "/" + path, Object.class); verify(restTemplate).getForObject(serviceTwo + "/" + path, Object.class); verify(restTemplate).getForObject(serviceThree + "/" + path, Object.class); } @Test public void testOneServerDown() { Object result = new Object(); when(serviceAddressLocator.getServiceList(any())) .thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree))); when(restTemplate.getForObject(serviceOne + "/" + path, Object.class)).thenThrow(socketTimeoutException); when(restTemplate.getForObject(serviceTwo + "/" + path, Object.class)).thenReturn(result); when(restTemplate.getForObject(serviceThree + "/" + path, Object.class)).thenThrow(connectTimeoutException); Object o = retryableRestTemplate.get(Env.DEV, path, Object.class); verify(restTemplate).getForObject(serviceOne + "/" + path, Object.class); verify(restTemplate).getForObject(serviceTwo + "/" + path, Object.class); verify(restTemplate, times(0)).getForObject(serviceThree + "/" + path, Object.class); Assert.assertEquals(result, o); } @Test(expected = ResourceAccessException.class) public void testPostSocketTimeoutNotRetry(){ when(serviceAddressLocator.getServiceList(any())) .thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree))); when(restTemplate.postForEntity(serviceOne + "/" + path, request, Object.class)).thenThrow(socketTimeoutException); when(restTemplate.postForEntity(serviceTwo + "/" + path, request, Object.class)).thenReturn(entity); retryableRestTemplate.post(Env.DEV, path, request, Object.class); verify(restTemplate).postForEntity(serviceOne + "/" + path, request, Object.class); verify(restTemplate, times(0)).postForEntity(serviceTwo + "/" + path, request, Object.class); } @Test public void testDelete(){ when(serviceAddressLocator.getServiceList(any())) .thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree))); retryableRestTemplate.delete(Env.DEV, path); verify(restTemplate).delete(serviceOne + "/" + path); } @Test public void testPut(){ when(serviceAddressLocator.getServiceList(any())) .thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree))); retryableRestTemplate.put(Env.DEV, path, request); verify(restTemplate).put(serviceOne + "/" + path, request); } private ServiceDTO mockService(String homeUrl) { ServiceDTO serviceDTO = new ServiceDTO(); serviceDTO.setHomepageUrl(homeUrl); return serviceDTO; } }