/** * Copyright 2013 Twitter, 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 com.twitter.hbc.httpclient; import com.twitter.hbc.BasicReconnectionManager; import com.twitter.hbc.RateTracker; import com.twitter.hbc.ReconnectionManager; import com.twitter.hbc.core.HttpConstants; import com.twitter.hbc.core.HttpHosts; import com.twitter.hbc.core.endpoint.RawEndpoint; import com.twitter.hbc.core.processor.HosebirdMessageProcessor; import com.twitter.hbc.httpclient.auth.Authentication; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpUriRequest; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import java.io.IOException; import java.io.InputStream; import static org.junit.Assert.*; import static org.mockito.Mockito.*; public class ClientBaseTest { private HttpClient mock; private HttpResponse mockResponse; private StatusLine mockStatusLine; private Connection mockConnection; private ReconnectionManager mockReconnectionManager; private HosebirdMessageProcessor mockProcessor; private Authentication mockAuth; private RateTracker mockRateTracker; private InputStream mockInputStream; @Before public void setup() throws Exception { mock = mock(HttpClient.class); mockResponse = mock(HttpResponse.class); mockStatusLine = mock(StatusLine.class); mockConnection = mock(Connection.class); mockReconnectionManager = mock(BasicReconnectionManager.class); mockRateTracker = mock(RateTracker.class); mockInputStream = mock(InputStream.class); mockAuth = mock(Authentication.class); mockProcessor = mock(HosebirdMessageProcessor.class); HttpEntity mockHttpEntity = mock(HttpEntity.class); // set up required mocks to mock out all of the clientbase stuff when(mock.execute(any(HttpUriRequest.class))) .thenReturn(mockResponse); when(mockResponse.getStatusLine()) .thenReturn(mockStatusLine); when(mockResponse.getEntity()) .thenReturn(mockHttpEntity); when(mockHttpEntity.getContent()) .thenReturn(mockInputStream); } @Test public void testProperlyHandleSuccessfulConnection() { ClientBase clientBase = new ClientBase("name", mock, new HttpHosts("http://hi"), new RawEndpoint("/endpoint", HttpConstants.HTTP_GET), mockAuth, mockProcessor, mockReconnectionManager, mockRateTracker ); mockStatusLine = mock(StatusLine.class); when(mockStatusLine.getStatusCode()) .thenReturn(HttpConstants.Codes.SUCCESS); assertTrue(clientBase.handleConnectionResult(mockStatusLine)); InOrder inOrder = inOrder(mockStatusLine, mockReconnectionManager); inOrder.verify(mockStatusLine).getStatusCode(); inOrder.verify(mockReconnectionManager).resetCounts(); assertFalse(clientBase.isDone()); } @Test public void testHandleIOExceptionOnConnection() throws IOException { ClientBase clientBase = new ClientBase("name", mock, new HttpHosts("http://hi"), new RawEndpoint("/endpoint", HttpConstants.HTTP_GET), mockAuth, mockProcessor, mockReconnectionManager, mockRateTracker ); when(mockConnection.connect(any(HttpUriRequest.class))) .thenThrow(new IOException()); HttpUriRequest mockRequest = mock(HttpUriRequest.class); assertNull(clientBase.establishConnection(mockConnection, mockRequest)); InOrder inOrder = inOrder(mockConnection, mockReconnectionManager); inOrder.verify(mockConnection).connect(any(HttpUriRequest.class)); inOrder.verify(mockReconnectionManager).handleLinearBackoff(); assertFalse(clientBase.isDone()); } @Test public void testRetryTransientAuthFailures() { ClientBase clientBase = new ClientBase("name", mock, new HttpHosts("http://hi"), new RawEndpoint("/endpoint", HttpConstants.HTTP_GET), mockAuth, mockProcessor, mockReconnectionManager, mockRateTracker ); when(mockStatusLine.getStatusCode()) .thenReturn(401); when(mockStatusLine.getReasonPhrase()) .thenReturn("reason"); when(mockReconnectionManager.shouldReconnectOn400s()) .thenReturn(true, true, false); // auth failure 3 times. We'll retry the first two times, but give up on the 3rd clientBase.handleConnectionResult(mockStatusLine); clientBase.handleConnectionResult(mockStatusLine); verify(mockReconnectionManager, times(2)).handleExponentialBackoff(); assertFalse(clientBase.isDone()); clientBase.handleConnectionResult(mockStatusLine); verify(mockReconnectionManager, times(2)).handleExponentialBackoff(); assertTrue(clientBase.isDone()); } @Test public void testUnknownEndpointFails() { ClientBase clientBase = new ClientBase("name", mock, new HttpHosts("http://hi"), new RawEndpoint("/endpoint", HttpConstants.HTTP_GET), mockAuth, mockProcessor, mockReconnectionManager, mockRateTracker ); when(mockStatusLine.getStatusCode()) .thenReturn(404); when(mockStatusLine.getReasonPhrase()) .thenReturn("reason"); clientBase.handleConnectionResult(mockStatusLine); assertTrue(clientBase.isDone()); } @Test public void testServiceUnavailable() { ClientBase clientBase = new ClientBase("name", mock, new HttpHosts("http://hi"), new RawEndpoint("/endpoint", HttpConstants.HTTP_GET), mockAuth, mockProcessor, mockReconnectionManager, mockRateTracker ); when(mockStatusLine.getStatusCode()) .thenReturn(503); clientBase.handleConnectionResult(mockStatusLine); clientBase.handleConnectionResult(mockStatusLine); clientBase.handleConnectionResult(mockStatusLine); clientBase.handleConnectionResult(mockStatusLine); verify(mockReconnectionManager, times(4)).handleExponentialBackoff(); assertFalse(clientBase.isDone()); } }