com.basho.riak.client.TestRiakObject.java Source code

Java tutorial

Introduction

Here is the source code for com.basho.riak.client.TestRiakObject.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;

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

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import com.basho.riak.client.request.RequestMeta;
import com.basho.riak.client.response.FetchResponse;
import com.basho.riak.client.response.HttpResponse;
import com.basho.riak.client.response.StoreResponse;
import com.basho.riak.client.response.WalkResponse;
import com.basho.riak.client.util.Constants;

public class TestRiakObject {

    RiakObject impl;

    @Before
    public void setup() {
        impl = new RiakObject("b", "k");
    }

    @Test
    public void content_type_defaults_to_octet_stream() {
        assertEquals("application/octet-stream", impl.getContentType());
    }

    @Test
    public void links_never_null() {
        impl = new RiakObject("b", "k", null, null, null, null, null, null, null);
        assertNotNull(impl.getLinks());

        impl.setLinks((List<RiakLink>) null);
        assertNotNull(impl.getLinks());

        impl.copyData(new RiakObject(null, null));
        assertNotNull(impl.getLinks());
    }

    @Test
    public void usermeta_never_null() {
        impl = new RiakObject("b", "k", null, null, null, null, null, null, null);
        assertNotNull(impl.getUsermeta());

        impl.setUsermeta((Map<String, String>) null);
        assertNotNull(impl.getUsermeta());

        impl.copyData(new RiakObject(null, null));
        assertNotNull(impl.getUsermeta());
    }

    @Test
    public void copyData_does_deep_copy() {
        final String value = "value";
        final InputStream valueStream = mock(InputStream.class);
        final long valueStreamLength = 10;
        final String ctype = "ctype";
        final List<RiakLink> links = new ArrayList<RiakLink>();
        final Map<String, String> usermeta = new HashMap<String, String>();
        usermeta.put("testKey", "testValue");
        final String vclock = "vclock";
        final String lastmod = "lastmod";
        final String vtag = "vtag";
        final RiakLink link = new RiakLink("b", "l", "t");
        links.add(link);

        RiakObject copy = new RiakObject("b", "k2", value.getBytes(), ctype, links, usermeta, vclock, lastmod,
                vtag);
        copy.setValueStream(valueStream, valueStreamLength);
        impl.copyData(copy);

        assertEquals("b", impl.getBucket());
        assertEquals("k", impl.getKey());
        assertEquals(value, impl.getValue());
        assertSame(valueStream, impl.getValueStream());
        assertEquals(valueStreamLength, impl.getValueStreamLength().longValue());
        assertEquals(ctype, impl.getContentType());

        // links and impl links are equal
        assertEquals(links.size(), impl.numLinks());
        for (RiakLink l : links) {
            assertTrue(impl.hasLink(l));
        }

        // user meta are equal
        assertEquals(usermeta.size(), impl.numUsermetaItems());

        for (Map.Entry<String, String> e : usermeta.entrySet()) {
            assertTrue(impl.hasUsermetaItem(e.getKey()));
            assertEquals(e.getValue(), impl.getUsermetaItem(e.getKey()));
        }

        assertEquals(vclock, impl.getVclock());
        assertEquals(lastmod, impl.getLastmod());
        assertEquals(vtag, impl.getVtag());

        assertNotSame(copy.getValueAsBytes(), impl.getValueAsBytes());

        // assert that the collections *and* contents of the collections are not the same
        // for the copy and the original object
        assertEquals(copy.numLinks(), impl.numLinks());
        for (RiakLink l : impl.iterableLinks()) {
            assertTrue(copy.hasLink(l));
            l.setKey("new Key");
            assertFalse(copy.hasLink(l));
            assertTrue(impl.hasLink(l));
        }

        assertEquals(copy.numUsermetaItems(), impl.numUsermetaItems());
        for (String key : impl.usermetaKeys()) {
            assertTrue(impl.hasUsermetaItem(key));
            assertTrue(copy.hasUsermetaItem(key));
            assertEquals(copy.getUsermetaItem(key), impl.getUsermetaItem(key));
        }

        impl.removeUsermetaItem("testKey");
        assertEquals(0, impl.numUsermetaItems());
        assertEquals(1, copy.numUsermetaItems());
    }

    @Test
    public void copyData_copies_null_data() {
        final String value = "value";
        final String ctype = "ctype";
        final List<RiakLink> links = new ArrayList<RiakLink>();
        final Map<String, String> usermeta = new HashMap<String, String>();
        final String vclock = "vclock";
        final String lastmod = "lastmod";
        final String vtag = "vtag";
        final RiakLink link = new RiakLink("b", "l", "t");
        links.add(link);

        impl = new RiakObject("b", "k", value.getBytes(), ctype, links, usermeta, vclock, lastmod, vtag);
        impl.copyData(new RiakObject(null, null));

        assertEquals("b", impl.getBucket());
        assertEquals("k", impl.getKey());
        assertNull(impl.getValue());
        assertEquals(0, impl.numLinks());
        assertEquals(0, impl.numUsermetaItems());
        assertNull(impl.getVclock());
        assertNull(impl.getLastmod());
        assertNull(impl.getVtag());
    }

    @Test
    public void updateMeta_nulls_out_meta_when_given_null_response() {
        final String vclock = "vclock";
        final String lastmod = "lastmod";
        final String vtag = "vtag";

        impl = new RiakObject("b", "k", null, null, null, null, vclock, lastmod, vtag);
        impl.updateMeta((StoreResponse) null);

        assertNull(impl.getVclock());
        assertNull(impl.getLastmod());
        assertNull(impl.getVtag());

        impl = new RiakObject("b", "k", null, null, null, null, vclock, lastmod, vtag);
        impl.updateMeta((FetchResponse) null);

        assertNull(impl.getVclock());
        assertNull(impl.getLastmod());
        assertNull(impl.getVtag());
    }

    @Test
    public void value_stream_is_separate_from_value() {
        final String value = "value";
        final byte[] isvalue = "isbytes".getBytes();
        final InputStream is = new ByteArrayInputStream(isvalue);

        impl.setValue(value);
        impl.setValueStream(is);

        assertEquals(value, impl.getValue());
        assertSame(is, impl.getValueStream());
    }

    @Test
    public void convenience_riak_client_methods_defer_to_riak_client() {
        RiakClient mockRiakClient = mock(RiakClient.class);
        RequestMeta mockRequestMeta = mock(RequestMeta.class);
        StoreResponse mockStoreResponse = mock(StoreResponse.class);
        FetchResponse mockFetchResponse = mock(FetchResponse.class);
        HttpResponse mockHttpResponse = mock(HttpResponse.class);
        WalkResponse mockWalkResponse = mock(WalkResponse.class);

        when(mockRiakClient.store(any(RiakObject.class), any(RequestMeta.class))).thenReturn(mockStoreResponse);
        impl = new RiakObject(mockRiakClient, "b", "k");
        StoreResponse sr1 = impl.store();
        StoreResponse sr2 = impl.store(mockRequestMeta);

        verify(mockRiakClient).store(impl, null);
        verify(mockRiakClient).store(impl, mockRequestMeta);
        assertSame(mockStoreResponse, sr1);
        assertSame(mockStoreResponse, sr2);

        when(mockRiakClient.fetchMeta(anyString(), anyString(), any(RequestMeta.class)))
                .thenReturn(mockFetchResponse);
        FetchResponse fr1 = impl.fetchMeta();
        FetchResponse fr2 = impl.fetchMeta(mockRequestMeta);

        verify(mockRiakClient).fetchMeta("b", "k", null);
        verify(mockRiakClient).fetchMeta("b", "k", mockRequestMeta);
        assertSame(mockFetchResponse, fr1);
        assertSame(mockFetchResponse, fr2);

        when(mockRiakClient.fetch(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockFetchResponse);
        fr1 = impl.fetch();
        fr2 = impl.fetch(mockRequestMeta);

        verify(mockRiakClient).fetch("b", "k", null);
        verify(mockRiakClient).fetch("b", "k", mockRequestMeta);
        assertSame(mockFetchResponse, fr1);
        assertSame(mockFetchResponse, fr2);

        when(mockRiakClient.delete(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockHttpResponse);
        HttpResponse hr1 = impl.delete();
        HttpResponse hr2 = impl.delete(mockRequestMeta);

        verify(mockRiakClient).delete("b", "k", null);
        verify(mockRiakClient).delete("b", "k", mockRequestMeta);
        assertSame(mockHttpResponse, hr1);
        assertSame(mockHttpResponse, hr2);

        when(mockRiakClient.walk(anyString(), anyString(), anyString(), any(RequestMeta.class)))
                .thenReturn(mockWalkResponse);
        WalkResponse wr1 = impl.walk().run();
        WalkResponse wr2 = impl.walk().run(mockRequestMeta);

        verify(mockRiakClient).walk("b", "k", "_,_,_/", null);
        verify(mockRiakClient).walk("b", "k", "_,_,_/", mockRequestMeta);
        assertSame(mockWalkResponse, wr1);
        assertSame(mockWalkResponse, wr2);
    }

    @Test
    public void store_updates_meta_on_success() {
        RiakClient mockRiakClient = mock(RiakClient.class);
        StoreResponse mockStoreResponse = mock(StoreResponse.class);

        when(mockRiakClient.store(any(RiakObject.class), any(RequestMeta.class))).thenReturn(mockStoreResponse);
        when(mockStoreResponse.isSuccess()).thenReturn(true);
        impl = spy(new RiakObject(mockRiakClient, "b", "k"));
        impl.store();

        verify(impl).updateMeta(mockStoreResponse);
    }

    @Test
    public void store_does_not_modify_meta_on_failure() {
        RiakClient mockRiakClient = mock(RiakClient.class);
        StoreResponse mockStoreResponse = mock(StoreResponse.class);

        when(mockRiakClient.store(any(RiakObject.class), any(RequestMeta.class))).thenReturn(mockStoreResponse);
        when(mockStoreResponse.isSuccess()).thenReturn(false);
        impl = spy(new RiakObject(mockRiakClient, "b", "k"));
        impl.store();

        verify(impl, never()).updateMeta(any(StoreResponse.class));
        verify(impl, never()).copyData(any(RiakObject.class));
    }

    @Test
    public void fetchMeta_updates_meta_on_success() {
        RiakClient mockRiakClient = mock(RiakClient.class);
        FetchResponse mockFetchResponse = mock(FetchResponse.class);

        when(mockRiakClient.fetchMeta(anyString(), anyString(), any(RequestMeta.class)))
                .thenReturn(mockFetchResponse);
        when(mockFetchResponse.isSuccess()).thenReturn(true);
        impl = spy(new RiakObject(mockRiakClient, "b", "k"));
        impl.fetchMeta();

        verify(impl).updateMeta(mockFetchResponse);
    }

    @Test
    public void fetchMeta_does_not_modify_meta_on_failure() {
        RiakClient mockRiakClient = mock(RiakClient.class);
        FetchResponse mockFetchResponse = mock(FetchResponse.class);

        when(mockRiakClient.fetchMeta(anyString(), anyString(), any(RequestMeta.class)))
                .thenReturn(mockFetchResponse);
        when(mockFetchResponse.isSuccess()).thenReturn(false);
        impl = spy(new RiakObject(mockRiakClient, "b", "k"));
        impl.fetchMeta();

        verify(impl, never()).updateMeta(any(FetchResponse.class));
        verify(impl, never()).copyData(any(RiakObject.class));
    }

    @Test
    public void fetch_shallow_copies_object_on_success() {
        RiakClient mockRiakClient = mock(RiakClient.class);
        FetchResponse mockFetchResponse = mock(FetchResponse.class);
        RiakObject mockRiakObject = mock(RiakObject.class);
        when(mockRiakClient.fetch(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockFetchResponse);
        when(mockFetchResponse.isSuccess()).thenReturn(true);
        when(mockFetchResponse.getObject()).thenReturn(mockRiakObject);
        impl = spy(new RiakObject(mockRiakClient, "b", "k"));
        impl.fetch();

        verify(impl).shallowCopy(mockRiakObject);
        verify(mockFetchResponse).setObject(impl);
    }

    @Test
    public void fetch_does_not_update_data_on_failure() {
        RiakClient mockRiakClient = mock(RiakClient.class);
        FetchResponse mockFetchResponse = mock(FetchResponse.class);

        when(mockRiakClient.fetch(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockFetchResponse);
        when(mockFetchResponse.isSuccess()).thenReturn(false);
        when(mockFetchResponse.getObject()).thenReturn(null);
        impl = spy(new RiakObject(mockRiakClient, "b", "k"));
        impl.fetch();

        verify(impl, never()).updateMeta(any(FetchResponse.class));
        verify(impl, never()).shallowCopy(any(RiakObject.class));
        verify(impl, never()).copyData(any(RiakObject.class));
    }

    @Test
    public void walk_returns_one_step_correctly() {
        RiakClient mockRiakClient = mock(RiakClient.class);

        impl = new RiakObject(mockRiakClient, "b", "k");

        impl.walk().run();
        verify(mockRiakClient).walk("b", "k", "_,_,_/", null);

        impl.walk(false).run();
        verify(mockRiakClient).walk("b", "k", "_,_,0/", null);

        impl.walk("bucket").run();
        verify(mockRiakClient).walk("b", "k", "bucket,_,_/", null);

        impl.walk("bucket", false).run();
        verify(mockRiakClient).walk("b", "k", "bucket,_,0/", null);

        impl.walk("bucket", "tag").run();
        verify(mockRiakClient).walk("b", "k", "bucket,tag,_/", null);

        impl.walk("bucket", "tag", false).run();
        verify(mockRiakClient).walk("b", "k", "bucket,tag,0/", null);
    }

    @Test
    public void walk_returns_multiple_step_correctly() {
        RiakClient mockRiakClient = mock(RiakClient.class);
        impl = new RiakObject(mockRiakClient, "b", "k");

        impl.walk().walk("bucket").walk("bucket", "tag", false).run();

        verify(mockRiakClient).walk("b", "k", "_,_,_/bucket,_,_/bucket,tag,0/", null);
    }

    // The following could be combined as "convenience_riak_client_methods_throw_if_no_associated_riak_client"
    @Test(expected = IllegalStateException.class)
    public void store_throws_if_no_associated_riak_client() {
        impl.store();
    }

    @Test(expected = IllegalStateException.class)
    public void fetchMeta_throws_if_no_associated_riak_client() {
        impl.fetchMeta();
    }

    @Test(expected = IllegalStateException.class)
    public void fetch_throws_if_no_associated_riak_client() {
        impl.fetch();
    }

    @Test(expected = IllegalStateException.class)
    public void delete_throws_if_no_associated_riak_client() {
        impl.delete();
    }

    @Test(expected = IllegalStateException.class)
    public void walk_throws_if_no_associated_riak_client() {
        impl.walk().run();
    }

    @SuppressWarnings("unchecked")
    @Test
    public void write_to_http_method_gives_value_stream_priority_over_value() {
        final String value = "value";
        final byte[] isvalue = "isbytes".getBytes();
        final InputStream is = new ByteArrayInputStream(isvalue);
        final ByteArrayOutputStream os = new ByteArrayOutputStream();
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        when(mockHttpMethod.getPath()).thenReturn("/path/to/object");
        doAnswer(new Answer() {
            public Object answer(InvocationOnMock invocation) throws Throwable {
                ((RequestEntity) invocation.getArguments()[0]).writeRequest(os);
                return null;
            }
        }).when(mockHttpMethod).setRequestEntity(any(RequestEntity.class));

        impl.setValue(value);
        impl.setValueStream(is);
        impl.writeToHttpMethod(mockHttpMethod);

        assertArrayEquals(os.toByteArray(), isvalue);
    }

    @Test
    public void write_to_http_method_always_sets_entity_even_if_value_is_null() {
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        impl.setValue((String) null);
        impl.setValueStream(null);
        impl.writeToHttpMethod(mockHttpMethod);

        verify(mockHttpMethod).setRequestEntity((RequestEntity) notNull());
    }

    @Test
    public void write_to_http_method_sets_link_header() {
        final RiakLink link = new RiakLink("b", "l", "t");
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        when(mockHttpMethod.getPath()).thenReturn("/riak/b/k");

        impl.addLink(link);
        impl.writeToHttpMethod(mockHttpMethod);

        verify(mockHttpMethod).addRequestHeader(eq(Constants.HDR_LINK), contains("</riak/b/l>; riaktag=\"t\""));
    }

    @Test
    public void write_to_http_method_doesnt_sets_link_header_if_no_links() {
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        impl.writeToHttpMethod(mockHttpMethod);

        verify(mockHttpMethod, never()).addRequestHeader(eq(Constants.HDR_LINK), anyString());
    }

    @Test
    public void write_to_http_method_sets_user_meta_headers() {
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        impl.addUsermetaItem("k", "v");
        impl.writeToHttpMethod(mockHttpMethod);

        verify(mockHttpMethod).setRequestHeader(Constants.HDR_USERMETA_REQ_PREFIX + "k", "v");
    }

    @Test
    public void write_to_http_method_doesnt_sets_user_meta_headers_if_no_usermeta() {
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        impl.writeToHttpMethod(mockHttpMethod);

        verify(mockHttpMethod, never()).setRequestHeader(contains(Constants.HDR_USERMETA_REQ_PREFIX), anyString());
    }

    @Test
    public void write_to_http_method_sets_vclock() {
        final String vclock = "vclock";
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        impl = new RiakObject("b", "k", null, null, null, null, vclock, null, null);
        impl.writeToHttpMethod(mockHttpMethod);

        verify(mockHttpMethod).setRequestHeader(Constants.HDR_VCLOCK, vclock);
    }

    @Test
    public void get_base_path_finds_empty_base_path() {
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        when(mockHttpMethod.getPath()).thenReturn(null);
        assertEquals("", impl.getBasePathFromHttpMethod(mockHttpMethod));

        when(mockHttpMethod.getPath()).thenReturn("");
        assertEquals("", impl.getBasePathFromHttpMethod(mockHttpMethod));

        when(mockHttpMethod.getPath()).thenReturn("/");
        assertEquals("", impl.getBasePathFromHttpMethod(mockHttpMethod));

        when(mockHttpMethod.getPath()).thenReturn("/b");
        assertEquals("", impl.getBasePathFromHttpMethod(mockHttpMethod));

        when(mockHttpMethod.getPath()).thenReturn("/b/k");
        assertEquals("", impl.getBasePathFromHttpMethod(mockHttpMethod));
    }

    @Test
    public void get_base_path_finds_one_element_base_path() {
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        when(mockHttpMethod.getPath()).thenReturn("/riak/b/k");
        assertEquals("/riak", impl.getBasePathFromHttpMethod(mockHttpMethod));
    }

    @Test
    public void get_base_path_finds_multiple_element_base_path() {
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        when(mockHttpMethod.getPath()).thenReturn("/path/to/riak/b/k");
        assertEquals("/path/to/riak", impl.getBasePathFromHttpMethod(mockHttpMethod));
    }

    @Test
    public void get_base_path_handles_trailing_slash() {
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        when(mockHttpMethod.getPath()).thenReturn("/riak/b/k/");
        assertEquals("/riak", impl.getBasePathFromHttpMethod(mockHttpMethod));
    }

    /*
     * Encapsulated links
     */

    @Test
    public void linkEncapsulationIsAsGoodAsDirectAccess() {
        final List<RiakLink> links = Arrays.asList(new RiakLink("b", "l", "t"), new RiakLink("b", "e", "c"),
                new RiakLink("g", "c", "s"), new RiakLink("q", "p", "c"));

        final RiakObject riakObject = new RiakObject("b", "k", "v".getBytes(), "", links,
                (Map<String, String>) null, "", "", "");

        assertEquals(links.size(), riakObject.numLinks());
        assertTrue(riakObject.hasLinks());

        for (RiakLink link : riakObject.iterableLinks()) {
            assertTrue(links.contains(link));
        }

        //updates on iterator fail
        try {
            riakObject.iterableLinks().iterator().remove();
            fail("Expected UnsupportedOperationException");
        } catch (UnsupportedOperationException e) {
            //NO-OP
        }

        //updates on collection succeed
        final RiakLink addedLink = new RiakLink("s", "e", "c");
        riakObject.getLinks().add(addedLink);
        assertTrue(linkPresent(riakObject, addedLink));

        riakObject.getLinks().remove(addedLink);
        assertFalse(linkPresent(riakObject, addedLink));

        //updates from encapsulation API succeed
        riakObject.addLink(addedLink);
        assertTrue(linkPresent(riakObject, addedLink));

        riakObject.removeLink(addedLink);
        assertFalse(linkPresent(riakObject, addedLink));

        riakObject.addLink(addedLink);
        assertTrue(riakObject.hasLink(addedLink));

        riakObject.removeLink(addedLink);
        assertFalse(riakObject.hasLink(addedLink));
    }

    /**
     * Checks if the riakObject's collection of RiakLinks contains the passed RiakLink.
     * @param riakObject the RiakObject to test
     * @param link the RiakLink to test
     * @return true if the RiakObject's collection of links contains the RiakLink.
     */
    private boolean linkPresent(final RiakObject riakObject, final RiakLink link) {
        boolean linkPresent = false;

        for (RiakLink l : riakObject.iterableLinks()) {
            if (l.equals(link)) {
                linkPresent = true;
            }
        }
        return linkPresent;
    }

    /*
     * Encapsulated user meta
     */
    @Test
    public void userMetaEncapsulationIsAsGoodAsDirectAccess() {
        final Map<String, String> userMeta = new HashMap<String, String>();
        userMeta.put("acl", "admin");
        userMeta.put("my-meta", "my-value");

        final RiakObject riakObject = new RiakObject("b", "k", "v".getBytes(), "", null, userMeta, "", "", "");

        assertTrue(riakObject.hasUsermeta());
        assertTrue(riakObject.hasUsermetaItem("acl"));
        assertTrue(riakObject.hasUsermetaItem("my-meta"));

        userMeta.clear();

        assertTrue(riakObject.hasUsermetaItem("acl"));
        assertTrue(riakObject.hasUsermetaItem("my-meta"));

        riakObject.addUsermetaItem("my-meta2", "my-value2");

        assertTrue(riakObject.hasUsermetaItem("my-meta2"));
        assertEquals("my-value2", riakObject.getUsermetaItem("my-meta2"));

        riakObject.removeUsermetaItem("acl");

        assertFalse(riakObject.hasUsermetaItem("acl"));

        final Map<String, String> leakedUserMeta = riakObject.getUsermeta();

        assertEquals("my-value2", leakedUserMeta.get("my-meta2"));
        assertEquals("my-value", leakedUserMeta.get("my-meta"));

        // still writes through
        leakedUserMeta.put("my-meta3", "my-value3");
        assertTrue(riakObject.hasUsermetaItem("my-meta3"));
        assertEquals("my-value3", riakObject.getUsermetaItem("my-meta3"));
    }

    @Test
    public void modifyLinksAndWriteToMethodConcurrently() throws InterruptedException {
        final RiakObject riakObject = new RiakObject("b", "k", "v".getBytes());
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        when(mockHttpMethod.getPath()).thenReturn("/riak/b/k");

        final int cnt = 20;
        Thread[] threads = new Thread[cnt];

        for (int i = 0; i < cnt; i++) {
            threads[i] = new Thread(new Runnable() {

                public void run() {
                    String bucket = UUID.randomUUID().toString();
                    String key = UUID.randomUUID().toString();
                    String tag = UUID.randomUUID().toString();
                    int cnt = 0;
                    while (true) {
                        riakObject.addLink(new RiakLink(bucket + cnt, key + cnt, tag + cnt));
                        cnt++;
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }

                }
            });
            threads[i].setDaemon(true);
            threads[i].start();
        }

        Thread.sleep(500);

        riakObject.writeToHttpMethod(mockHttpMethod);
    }

    @Test
    public void modifyUserMetaAndWriteToMethodConcurrently() throws InterruptedException {
        final RiakObject riakObject = new RiakObject("b", "k", "v".getBytes());
        final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class);

        when(mockHttpMethod.getPath()).thenReturn("/riak/b/k");

        final int cnt = 20;
        Thread[] threads = new Thread[cnt];

        for (int i = 0; i < cnt; i++) {
            threads[i] = new Thread(new Runnable() {

                public void run() {
                    String key = UUID.randomUUID().toString();
                    String tag = UUID.randomUUID().toString();
                    int cnt = 0;
                    while (true) {
                        riakObject.addUsermetaItem(key + cnt, tag + cnt);
                        cnt++;
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }

                }
            });
            threads[i].setDaemon(true);
            threads[i].start();
        }

        Thread.sleep(500);

        riakObject.writeToHttpMethod(mockHttpMethod);
    }

    @Test
    public void valueByteArraySafelyEncapsulated() {
        final byte[] value = "vvvv".getBytes();
        final RiakObject riakObject = new RiakObject("b", "k", value);

        assertArrayEquals("vvvv".getBytes(), riakObject.getValueAsBytes());
        value[0] = 'b';
        assertArrayEquals("vvvv".getBytes(), riakObject.getValueAsBytes());

        byte[] roInternalValue = riakObject.getValueAsBytes();
        roInternalValue[0] = 'z';

        assertArrayEquals("vvvv".getBytes(), riakObject.getValueAsBytes());
    }
}