com.basho.riak.client.http.util.TestClientHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.basho.riak.client.http.util.TestClientHelper.java

Source

/*
 * This file is provided to you 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.basho.riak.client.http.util;

import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import com.basho.riak.client.http.RiakConfig;
import com.basho.riak.client.http.RiakObject;
import com.basho.riak.client.http.request.RequestMeta;
import com.basho.riak.client.http.response.HttpResponse;
import com.basho.riak.client.http.response.RiakExceptionHandler;
import com.basho.riak.client.http.response.RiakIORuntimeException;
import com.basho.riak.client.http.response.RiakResponseRuntimeException;
import com.basho.riak.client.http.response.StreamHandler;
import com.basho.riak.client.http.util.ClientHelper;
import com.basho.riak.client.http.util.ClientUtils;
import com.basho.riak.client.http.util.Constants;
import com.basho.riak.client.util.CharsetUtils;

public class TestClientHelper {

    final RiakConfig config = new RiakConfig("http://127.0.0.1:8098/riak");
    final String bucket = "bucket";
    final String key = "key";
    final String walkSpec = "walkSpec";
    final String mrJob = "mrJob";
    final String clientId = "test";
    final RequestMeta meta = new RequestMeta();

    @Mock
    HttpClient mockHttpClient;
    @Mock
    JSONObject schema;
    @Mock
    RiakObject object;
    @Mock
    org.apache.http.HttpResponse mockHttpResponse;
    @Mock
    HttpEntity mockHttpEntity;
    ClientHelper impl;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);

        when(object.getBucket()).thenReturn(bucket);
        when(object.getKey()).thenReturn(key);

        impl = new ClientHelper(config, clientId);
        impl.setHttpClient(mockHttpClient);
    }

    @Test
    public void client_helper_uses_passed_in_client_id() throws UnsupportedEncodingException {
        assertEquals(clientId, CharsetUtils.asUTF8String(impl.getClientId()));
    }

    @Test
    public void client_helper_generates_client_id_if_null() {
        impl = new ClientHelper(config, null);
        assertNotNull(impl.getClientId());
        assertEquals(4, impl.getClientId().length);
    }

    @Test
    public void fetch_defaults_to_not_streaming() throws IOException {
        stubResponse(true);
        impl = spy(impl);
        impl.fetch(bucket, key, meta);
        verify(impl).fetch(bucket, key, meta, false);
    }

    @Test
    public void setBucketSchema_PUTs_to_bucket_URL() throws IOException {
        when(mockHttpClient.execute(any(HttpRequestBase.class))).thenAnswer(pathVerifier("/" + bucket));
        stubResponse(false);
        impl.setBucketSchema(bucket, schema, meta);
        verify(mockHttpClient).execute(any(HttpPut.class));
    }

    @Test
    public void getBucketSchema_GETs_bucket_URL() throws IOException {
        when(mockHttpClient.execute(any(HttpRequestBase.class))).thenAnswer(pathVerifier("/" + bucket));
        stubResponse(false);
        impl.getBucketSchema(bucket, meta);
        verify(mockHttpClient).execute(any(HttpGet.class));
    }

    @Test
    public void getBucketSchema_adds_no_keys_qp() throws IOException {
        RequestMeta meta = spy(new RequestMeta());
        stubResponse(true);
        impl = spy(impl);
        impl.getBucketSchema(bucket, meta);
        verify(meta).setQueryParam(Constants.QP_KEYS, Constants.NO_KEYS);
        verify(impl).executeMethod(eq(bucket), anyString(), any(HttpGet.class), same(meta), eq(false));
    }

    @Test
    public void listBucket_GETs_bucket_URL() throws IOException {
        when(mockHttpClient.execute(any(HttpRequestBase.class))).thenAnswer(pathVerifier("/" + bucket));
        stubResponse(false);
        impl.listBucket(bucket, meta, false);
        verify(mockHttpClient).execute(any(HttpGet.class));
    }

    @Test
    public void listBuckets_GET_adds_qp() throws IOException {
        impl = spy(impl);
        stubResponse(true);
        impl.listBuckets();
        ArgumentCaptor<RequestMeta> metaCaptor = ArgumentCaptor.forClass(RequestMeta.class);
        verify(impl).executeMethod(eq((String) null), eq((String) null), any(HttpGet.class), metaCaptor.capture());

        RequestMeta capturedMeta = metaCaptor.getValue();
        assertEquals(capturedMeta.getQueryParam(Constants.QP_BUCKETS), Constants.LIST_BUCKETS);
    }

    @Test
    public void listBucket_adds_keys_qp_when_streaming_response() throws IOException {
        RequestMeta meta = spy(new RequestMeta());
        impl = spy(impl);
        stubResponse(true);
        impl.listBucket(bucket, meta, true);
        verify(meta).setQueryParam(Constants.QP_KEYS, Constants.STREAM_KEYS);
        verify(impl).executeMethod(eq(bucket), anyString(), any(HttpGet.class), same(meta), eq(true));
    }

    @Test
    public void listBucket_add_keys_equals_true_qp_if_not_streaming_response() throws IOException {
        RequestMeta meta = spy(new RequestMeta());
        impl = spy(impl);
        stubResponse(true);
        impl.listBucket(bucket, meta, false);
        verify(meta).setQueryParam(eq(Constants.QP_KEYS), eq(Constants.INCLUDE_KEYS));
        verify(impl).executeMethod(eq(bucket), anyString(), any(HttpGet.class), same(meta), eq(false));
    }

    @Test
    public void store_PUTs_object_URL() throws IOException {
        when(mockHttpClient.execute(any(HttpRequestBase.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key));
        stubResponse(false);
        impl.store(object, meta);
        verify(mockHttpClient).execute(any(HttpPut.class));
    }

    @Test
    public void store_sets_client_id() throws IOException {
        stubResponse(true);
        impl.store(object, meta);
        assertEquals(ClientUtils.encodeClientId(clientId), meta.getClientId());
    }

    @Test
    public void store_doesnt_overwrite_client_id() throws IOException {
        stubResponse(true);
        meta.setClientId("clientId");
        impl.store(object, meta);
        assertEquals("clientId", meta.getClientId());
    }

    @Test
    public void store_sets_connection_header() throws IOException {
        stubResponse(true);
        impl.store(object, meta);
        assertEquals("keep-alive", meta.getHeader(Constants.HDR_CONNECTION));
    }

    @Test
    public void store_doesnt_overwrite_connection_header() throws IOException {
        stubResponse(true);
        meta.setHeader(Constants.HDR_CONNECTION, "connection");
        impl.store(object, meta);
        assertEquals("connection", meta.getHeader(Constants.HDR_CONNECTION));
    }

    @Test
    public void fetchMeta_HEADs_object_URL() throws IOException {
        when(mockHttpClient.execute(any(HttpRequestBase.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key));
        stubResponse(false);
        impl.fetchMeta(bucket, key, meta);
        verify(mockHttpClient).execute(any(HttpHead.class));
    }

    @Test
    public void fetch_GETs_object_URL() throws IOException {
        when(mockHttpClient.execute(any(HttpRequestBase.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key));
        stubResponse(false);
        impl.fetch(bucket, key, meta);
        verify(mockHttpClient).execute(any(HttpGet.class));
    }

    @Test
    public void stream_GETs_object_URL() throws IOException {
        when(mockHttpClient.execute(any(HttpRequestBase.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key));
        stubResponse(false);
        impl.stream(bucket, key, mock(StreamHandler.class), meta);
        verify(mockHttpClient).execute(any(HttpGet.class));
    }

    @Test
    public void delete_DELETEs_object_URL() throws IOException {
        stubResponse(false);
        when(mockHttpClient.execute(any(HttpRequestBase.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key));
        impl.delete(bucket, key, meta);
        verify(mockHttpClient).execute(any(HttpDelete.class));
    }

    @Test
    public void walk_GETs_object_URL() throws IOException {
        when(mockHttpClient.execute(any(HttpRequestBase.class)))
                .thenAnswer(pathVerifier("/" + bucket + "/" + key + "/" + walkSpec));
        stubResponse(false);
        impl.walk(bucket, key, walkSpec, meta);
        verify(mockHttpClient).execute(any(HttpGet.class));
    }

    @Test
    public void mapreduce_POSTs_to_mapred_URL() throws IOException {
        when(mockHttpClient.execute(any(HttpRequestBase.class))).thenAnswer(pathVerifier("/mapred"));
        stubResponse(false);
        impl.mapReduce(mrJob, meta);
        verify(mockHttpClient).execute(any(HttpPost.class));
    }

    @Test
    public void all_methods_add_query_params() throws IOException {
        stubResponse(true);
        impl.setBucketSchema(bucket, schema, meta);
        impl.listBucket(bucket, meta, false);
        impl.store(object, meta);
        impl.fetchMeta(bucket, key, meta);
        impl.fetch(bucket, key, meta);
        impl.delete(bucket, key, meta);
        impl.walk(bucket, key, walkSpec, meta);
        impl.mapReduce(mrJob, meta);
    }

    @Test
    public void execute_method_adds_headers() throws IOException {
        stubResponse(true);
        HttpRequestBase mockHttpRequestBase = mock(HttpRequestBase.class);
        meta.setHeader("p", "v");

        impl.executeMethod(null, null, mockHttpRequestBase, meta);

        verify(mockHttpClient).execute(mockHttpRequestBase);
        verify(mockHttpRequestBase).addHeader("p", "v");
    }

    @Test
    public void execute_method_adds_query_params() throws IOException, URISyntaxException {
        stubResponse(true);
        HttpRequestBase mockHttpRequestBase = mock(HttpRequestBase.class);
        when(mockHttpRequestBase.getURI()).thenReturn(new URI("http://127.0.0.1:8098/riak"));
        meta.setQueryParam("p", "v");

        impl.executeMethod(null, null, mockHttpRequestBase, meta);

        ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class);

        verify(mockHttpClient).execute(mockHttpRequestBase);
        verify(mockHttpRequestBase).setURI(uriCaptor.capture());

        assertTrue(uriCaptor.getValue().getQuery().contains("p=v"));
    }

    @Test
    public void execute_method_without_stream_response_closes_connection() throws IOException {
        HttpRequestBase mockHttpRequestBase = mock(HttpRequestBase.class);

        InputStream is = new ByteArrayInputStream(
                CharsetUtils.utf8StringToBytes("a horse a horse my kingdom for a horse"));
        stubResponse(true);
        is = spy(is);
        when(mockHttpEntity.getContent()).thenReturn(is);
        impl.executeMethod(null, null, mockHttpRequestBase, meta);
        verify(is).close(); // close releases the connection, we have to trust hc authors on that
    }

    @Test
    public void execute_method_with_stream_response_returns_null_body() throws IOException {
        HttpRequestBase mockHttpRequestBase = mock(HttpRequestBase.class);
        stubResponse(true);
        HttpResponse r = impl.executeMethod(null, null, mockHttpRequestBase, meta, true);

        assertNull(r.getBody());
    }

    @Test
    public void execute_method_with_stream_response_doesnt_consume_stream_or_close_connection() throws IOException {
        HttpRequestBase mockHttpRequestBase = mock(HttpRequestBase.class);
        InputStream is = new ByteArrayInputStream(
                CharsetUtils.utf8StringToBytes("a horse a horse my kingdom for a horse"));
        stubResponse(true);
        is = spy(is);
        when(mockHttpEntity.getContent()).thenReturn(is);
        impl.executeMethod(null, null, mockHttpRequestBase, meta, true);

        verify(is, never()).close();
    }

    @Test
    public void execute_method_defers_exceptions_to_toss() throws IOException {
        HttpRequestBase mockHttpRequestBase = mock(HttpRequestBase.class);

        impl = spy(impl);
        doReturn(null).when(impl).toss(any(RiakIORuntimeException.class));
        when(mockHttpClient.execute(any(HttpRequestBase.class))).thenThrow(new IOException());

        impl.executeMethod(null, null, mockHttpRequestBase, meta);
        impl.executeMethod(null, null, mockHttpRequestBase, meta);

        verify(impl, times(2)).toss(any(RiakIORuntimeException.class));
    }

    @Test(expected = RiakIORuntimeException.class)
    public void toss_throws_io_exception_if_no_exception_handler_installed() {
        impl.toss(new RiakIORuntimeException());
    }

    @Test(expected = RiakResponseRuntimeException.class)
    public void toss_throws_response_exception_if_no_exception_handler_installed() {
        impl.toss(new RiakResponseRuntimeException(null));
    }

    @Test
    public void toss_doesnt_throw_if_exception_handler_installed() {
        impl.setExceptionHandler(mock(RiakExceptionHandler.class));
        impl.toss(new RiakIORuntimeException());
        impl.toss(new RiakResponseRuntimeException(null));
    }

    @Test
    public void store_will_use_post_when_no_key_is_provided() throws ClientProtocolException, IOException {
        when(mockHttpClient.execute(any(HttpUriRequest.class))).thenReturn(mockHttpResponse);
        RiakObject objectWithNoKey = new RiakObject(bucket, null);
        impl.store(objectWithNoKey, meta);
        ArgumentCaptor<HttpRequestBase> httpRequestBaseCaptor = ArgumentCaptor.forClass(HttpRequestBase.class);
        verify(mockHttpClient).execute(httpRequestBaseCaptor.capture());

        HttpRequestBase requestObject = httpRequestBaseCaptor.getValue();
        assertEquals("Store without key needs to be POST for Riak to understand.", "POST",
                requestObject.getMethod());
    }

    @Test
    public void store_will_return_key_generated_by_server_when_no_key_is_provided()
            throws ClientProtocolException, IOException {
        String generatedKey = "abc123";
        Header locationHeader = new BasicHeader("Location", "/riak/" + bucket + "/" + generatedKey);
        when(mockHttpResponse.getFirstHeader("Location")).thenReturn(locationHeader);
        when(mockHttpClient.execute(any(HttpUriRequest.class))).thenReturn(mockHttpResponse);
        RiakObject objectWithNoKey = new RiakObject(bucket, null);
        HttpResponse storeResponse = impl.store(objectWithNoKey, meta);

        assertEquals("Key generated by server should be assigned to object in response.", generatedKey,
                storeResponse.getKey());
    }

    private Answer<org.apache.http.HttpResponse> pathVerifier(final String pathSuffix) {
        return new Answer<org.apache.http.HttpResponse>() {
            public org.apache.http.HttpResponse answer(InvocationOnMock invocation) throws Throwable {
                String path = ((HttpRequestBase) invocation.getArguments()[0]).getURI().getPath();
                assertTrue("URL path should end with " + pathSuffix + " but was '" + path + "'",
                        path.endsWith(pathSuffix) || path.endsWith(pathSuffix + "?"));
                return mockHttpResponse;
            }
        };
    }

    private void stubResponse(boolean stubRequest) throws IOException {
        if (stubRequest) {
            when(mockHttpClient.execute(any(HttpRequestBase.class))).thenReturn(mockHttpResponse);
        }
        when(mockHttpResponse.getStatusLine())
                .thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
        when(mockHttpResponse.getEntity()).thenReturn(mockHttpEntity);
    }
}