com.couchbase.client.core.endpoint.config.ConfigHandlerTest.java Source code

Java tutorial

Introduction

Here is the source code for com.couchbase.client.core.endpoint.config.ConfigHandlerTest.java

Source

/*
 * Copyright (c) 2016 Couchbase, 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.couchbase.client.core.endpoint.config;

import com.couchbase.client.core.endpoint.AbstractEndpoint;
import com.couchbase.client.core.env.CoreEnvironment;
import com.couchbase.client.core.message.ResponseStatus;
import com.couchbase.client.core.message.config.BucketConfigRequest;
import com.couchbase.client.core.message.config.BucketConfigResponse;
import com.couchbase.client.core.message.config.BucketStreamingRequest;
import com.couchbase.client.core.message.config.BucketStreamingResponse;
import com.couchbase.client.core.message.config.ConfigRequest;
import com.couchbase.client.core.message.config.FlushRequest;
import com.couchbase.client.core.message.config.FlushResponse;
import com.couchbase.client.core.message.config.GetDesignDocumentsRequest;
import com.couchbase.client.core.message.config.GetDesignDocumentsResponse;
import com.couchbase.client.core.util.CollectingResponseEventSink;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.DefaultLastHttpContent;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action1;
import rx.schedulers.Schedulers;

import java.net.InetAddress;
import java.nio.charset.Charset;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
 * Verifies the functionality of the {@link ConfigHandler}.
 *
 * @author Michael Nitschinger
 * @since 1.0
 */
public class ConfigHandlerTest {

    /**
     * The default charset used for all interactions.
     */
    private static final Charset CHARSET = CharsetUtil.UTF_8;

    /**
     * The queue in which requests are pushed to only test decodes in isolation manually.
     */
    private Queue<ConfigRequest> requestQueue;

    /**
     * The channel in which the handler is tested.
     */
    private EmbeddedChannel channel;

    /**
     * The actual handler.
     */
    private ConfigHandler handler;

    /**
     * Represents a custom event sink that collects all events pushed into it.
     */
    private CollectingResponseEventSink eventSink;

    @Before
    @SuppressWarnings("unchecked")
    public void setup() {
        CoreEnvironment environment = mock(CoreEnvironment.class);
        when(environment.scheduler()).thenReturn(Schedulers.computation());
        AbstractEndpoint endpoint = mock(AbstractEndpoint.class);
        when(endpoint.environment()).thenReturn(environment);
        when(environment.userAgent()).thenReturn("Couchbase Client Mock");

        eventSink = new CollectingResponseEventSink();
        requestQueue = new ArrayDeque<ConfigRequest>();
        handler = new ConfigHandler(endpoint, eventSink, requestQueue, true);
        channel = new EmbeddedChannel(handler);
    }

    @After
    public void cleanup() {
        channel.close().awaitUninterruptibly();
    }

    @Test
    public void shouldEncodeBucketConfigRequest() throws Exception {
        BucketConfigRequest request = new BucketConfigRequest("/path/", InetAddress.getLocalHost(), "bucket",
                "password");

        channel.writeOutbound(request);
        HttpRequest outbound = (HttpRequest) channel.readOutbound();

        assertEquals(HttpMethod.GET, outbound.getMethod());
        assertEquals(HttpVersion.HTTP_1_1, outbound.getProtocolVersion());
        assertEquals("/path/bucket", outbound.getUri());
        assertTrue(outbound.headers().contains(HttpHeaders.Names.AUTHORIZATION));
        assertTrue(outbound.headers().contains(HttpHeaders.Names.HOST));
        assertEquals("Basic YnVja2V0OnBhc3N3b3Jk", outbound.headers().get(HttpHeaders.Names.AUTHORIZATION));
        assertEquals("Couchbase Client Mock", outbound.headers().get(HttpHeaders.Names.USER_AGENT));
    }

    @Test
    public void shouldDecodeSuccessBucketConfigResponse() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(200, "OK"));
        HttpContent responseChunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("foo", CHARSET));
        HttpContent responseChunk2 = new DefaultLastHttpContent(Unpooled.copiedBuffer("bar", CHARSET));

        BucketConfigRequest requestMock = mock(BucketConfigRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader, responseChunk1, responseChunk2);
        channel.readInbound();

        assertEquals(1, eventSink.responseEvents().size());
        BucketConfigResponse event = (BucketConfigResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.SUCCESS, event.status());
        assertEquals("foobar", event.config());
        assertTrue(requestQueue.isEmpty());
    }

    @Test
    public void shouldDecodeAuthFailureBucketConfigResponse() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(401, "Unauthorized"));
        HttpContent responseChunk = LastHttpContent.EMPTY_LAST_CONTENT;

        BucketConfigRequest requestMock = mock(BucketConfigRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader, responseChunk);

        assertEquals(1, eventSink.responseEvents().size());
        BucketConfigResponse event = (BucketConfigResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.FAILURE, event.status());
        assertEquals("Unauthorized", event.config());
        assertTrue(requestQueue.isEmpty());
    }

    @Test
    public void shouldDecodeNotFoundBucketConfigResponse() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(404, "Object Not Found"));
        HttpContent responseChunk = new DefaultLastHttpContent(
                Unpooled.copiedBuffer("Not found.", CharsetUtil.UTF_8));

        BucketConfigRequest requestMock = mock(BucketConfigRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader, responseChunk);

        assertEquals(1, eventSink.responseEvents().size());
        BucketConfigResponse event = (BucketConfigResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.NOT_EXISTS, event.status());
        assertEquals("Not found.", event.config());
        assertTrue(requestQueue.isEmpty());
    }

    @Test
    public void shouldEncodeFlushRequest() {
        FlushRequest request = new FlushRequest("bucket", "password");

        channel.writeOutbound(request);
        HttpRequest outbound = (HttpRequest) channel.readOutbound();

        assertEquals(HttpMethod.POST, outbound.getMethod());
        assertEquals(HttpVersion.HTTP_1_1, outbound.getProtocolVersion());
        assertEquals("/pools/default/buckets/bucket/controller/doFlush", outbound.getUri());
        assertTrue(outbound.headers().contains(HttpHeaders.Names.AUTHORIZATION));
        assertTrue(outbound.headers().contains(HttpHeaders.Names.HOST));
        assertEquals("Basic YnVja2V0OnBhc3N3b3Jk", outbound.headers().get(HttpHeaders.Names.AUTHORIZATION));
        assertEquals("Couchbase Client Mock", outbound.headers().get(HttpHeaders.Names.USER_AGENT));
    }

    @Test
    public void shouldDecodeSuccessFlushResponse() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(200, "OK"));
        HttpContent responseChunk = LastHttpContent.EMPTY_LAST_CONTENT;

        FlushRequest requestMock = mock(FlushRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader, responseChunk);

        assertEquals(1, eventSink.responseEvents().size());
        FlushResponse event = (FlushResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.SUCCESS, event.status());
        assertEquals("OK", event.content());
        assertTrue(requestQueue.isEmpty());
    }

    @Test
    public void shouldDecodeFlushNotEnabledResponse() throws Exception {
        String content = "{\"_\":\"Flush is disabled for the bucket\"}";
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(400, "Bad Request"));
        HttpContent responseChunk = new DefaultLastHttpContent(Unpooled.copiedBuffer(content, CharsetUtil.UTF_8));

        FlushRequest requestMock = mock(FlushRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader, responseChunk);

        assertEquals(1, eventSink.responseEvents().size());
        FlushResponse event = (FlushResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.INVALID_ARGUMENTS, event.status());
        assertEquals("{\"_\":\"Flush is disabled for the bucket\"}", event.content());
        assertTrue(requestQueue.isEmpty());
    }

    @Test
    public void shouldDecodeListDesignDocumentsResponse() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(200, "OK"));
        HttpContent responseChunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("foo", CharsetUtil.UTF_8));
        HttpContent responseChunk2 = new DefaultLastHttpContent(Unpooled.copiedBuffer("bar", CharsetUtil.UTF_8));

        GetDesignDocumentsRequest requestMock = mock(GetDesignDocumentsRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader, responseChunk1, responseChunk2);

        assertEquals(1, eventSink.responseEvents().size());
        GetDesignDocumentsResponse event = (GetDesignDocumentsResponse) eventSink.responseEvents().get(0)
                .getMessage();

        assertEquals(ResponseStatus.SUCCESS, event.status());
        assertEquals("foobar", event.content());
        assertTrue(requestQueue.isEmpty());
    }

    @Test
    public void shouldEncodeBucketStreamingRequest() throws Exception {
        BucketStreamingRequest request = new BucketStreamingRequest("/path/", "bucket", "password");

        channel.writeOutbound(request);
        HttpRequest outbound = (HttpRequest) channel.readOutbound();

        assertEquals(HttpMethod.GET, outbound.getMethod());
        assertEquals(HttpVersion.HTTP_1_1, outbound.getProtocolVersion());
        assertEquals("/path/bucket", outbound.getUri());
        assertTrue(outbound.headers().contains(HttpHeaders.Names.AUTHORIZATION));
        assertEquals("Basic YnVja2V0OnBhc3N3b3Jk", outbound.headers().get(HttpHeaders.Names.AUTHORIZATION));
        assertEquals("Couchbase Client Mock", outbound.headers().get(HttpHeaders.Names.USER_AGENT));
    }

    @Test
    public void shouldDecodeInitialBucketStreamingResponse() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(200, "OK"));

        BucketStreamingRequest requestMock = mock(BucketStreamingRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader);

        assertEquals(1, eventSink.responseEvents().size());
        BucketStreamingResponse event = (BucketStreamingResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.SUCCESS, event.status());
        assertNotNull(event.configs());
        assertNotNull(event.host());
        assertEquals(0, requestQueue.size());
        ReferenceCountUtil.releaseLater(event);
        ReferenceCountUtil.releaseLater(responseHeader);
    }

    @Test
    public void shouldPushSubsequentChunks() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(200, "OK"));
        HttpContent responseChunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("config", CharsetUtil.UTF_8));
        HttpContent responseChunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("\n\n\n\n", CharsetUtil.UTF_8));

        BucketStreamingRequest requestMock = mock(BucketStreamingRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader, responseChunk1, responseChunk2);

        assertEquals(1, eventSink.responseEvents().size());
        BucketStreamingResponse event = (BucketStreamingResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.SUCCESS, event.status());
        assertNotNull(event.configs());
        assertNotNull(event.host());

        Observable<String> configs = event.configs();
        assertEquals("config", configs.toBlocking().first());
    }

    @Test
    public void shouldPushMixedSizeChunksCorrectly() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(200, "OK"));
        HttpContent responseChunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("conf", CharsetUtil.UTF_8));
        HttpContent responseChunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("ig\n", CharsetUtil.UTF_8));

        BucketStreamingRequest requestMock = mock(BucketStreamingRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader, responseChunk1, responseChunk2);

        assertEquals(1, eventSink.responseEvents().size());
        BucketStreamingResponse event = (BucketStreamingResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.SUCCESS, event.status());
        assertNotNull(event.configs());
        assertNotNull(event.host());

        Observable<String> configs = event.configs();

        final CountDownLatch latch = new CountDownLatch(2);
        configs.forEach(new Action1<String>() {
            @Override
            public void call(String config) {
                assertTrue(config.equals("config") || config.equals("new"));
                latch.countDown();
            }
        });

        HttpContent responseChunk3 = new DefaultHttpContent(Unpooled.copiedBuffer("\n\n\nne", CharsetUtil.UTF_8));
        HttpContent responseChunk4 = new DefaultHttpContent(Unpooled.copiedBuffer("w\n\n\n\n", CharsetUtil.UTF_8));

        channel.writeInbound(responseChunk3, responseChunk4);
        assertTrue(latch.await(1, TimeUnit.SECONDS));
    }

    @Test
    public void shouldDecodeFailingInitialBucketStreamingResponse() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(404, "Object Not Found"));

        BucketStreamingRequest requestMock = mock(BucketStreamingRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader);

        assertEquals(1, eventSink.responseEvents().size());
        BucketStreamingResponse event = (BucketStreamingResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.NOT_EXISTS, event.status());
        assertNull(event.configs());
        assertNotNull(event.host());
        assertEquals(0, requestQueue.size());
        ReferenceCountUtil.releaseLater(responseHeader);
        ReferenceCountUtil.releaseLater(event);

    }

    @Test
    public void shouldResetStateIfStreamCloses() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(200, "OK"));
        HttpContent responseChunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("conf", CharsetUtil.UTF_8));
        HttpContent responseChunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("ig\n", CharsetUtil.UTF_8));

        BucketStreamingRequest requestMock = mock(BucketStreamingRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader, responseChunk1, responseChunk2);

        assertEquals(1, eventSink.responseEvents().size());
        BucketStreamingResponse event = (BucketStreamingResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.SUCCESS, event.status());
        assertNotNull(event.configs());
        assertNotNull(event.host());

        Observable<String> configs = event.configs();

        final CountDownLatch latch = new CountDownLatch(3);
        configs.subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {
                latch.countDown();
            }

            @Override
            public void onError(Throwable e) {
                assertTrue(false);
            }

            @Override
            public void onNext(String config) {
                assertTrue(config.equals("config") || config.equals("new"));
                latch.countDown();
            }
        });

        HttpContent responseChunk3 = new DefaultHttpContent(Unpooled.copiedBuffer("\n\n\nne", CharsetUtil.UTF_8));
        HttpContent responseChunk4 = new DefaultLastHttpContent(
                Unpooled.copiedBuffer("w\n\n\n\n", CharsetUtil.UTF_8));

        channel.writeInbound(responseChunk3, responseChunk4);
        assertTrue(latch.await(1, TimeUnit.SECONDS));
    }

    @Test
    public void shouldCloseStreamIfChannelDies() throws Exception {
        HttpResponse responseHeader = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                new HttpResponseStatus(200, "OK"));
        HttpContent responseChunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("conf", CharsetUtil.UTF_8));
        HttpContent responseChunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("ig\n\n\n\n", CharsetUtil.UTF_8));

        BucketStreamingRequest requestMock = mock(BucketStreamingRequest.class);
        requestQueue.add(requestMock);
        channel.writeInbound(responseHeader, responseChunk1, responseChunk2);

        assertEquals(1, eventSink.responseEvents().size());
        BucketStreamingResponse event = (BucketStreamingResponse) eventSink.responseEvents().get(0).getMessage();

        assertEquals(ResponseStatus.SUCCESS, event.status());
        assertNotNull(event.configs());
        assertNotNull(event.host());

        Observable<String> configs = event.configs();

        final CountDownLatch latch = new CountDownLatch(1);
        configs.subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {
                latch.countDown();
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(String s) {

            }
        });

        channel.pipeline().remove(handler);
        channel.disconnect().awaitUninterruptibly();
        assertTrue(latch.await(1, TimeUnit.SECONDS));
    }

    @Test
    public void shouldNotBreakLinesOnLongAuth() throws Exception {
        String longPassword = "thisIsAveryLongPasswordWhichShouldNotContainLineBreaksAfterEncodingOtherwise"
                + "itWillBreakTheRequestResponseFlowWithTheServer";
        BucketConfigRequest request = new BucketConfigRequest("/path/", InetAddress.getLocalHost(), "bucket",
                longPassword);

        channel.writeOutbound(request);
        HttpRequest outbound = (HttpRequest) channel.readOutbound();

        assertEquals(HttpMethod.GET, outbound.getMethod());
        assertEquals(HttpVersion.HTTP_1_1, outbound.getProtocolVersion());
        assertEquals("/path/bucket", outbound.getUri());
        assertTrue(outbound.headers().contains(HttpHeaders.Names.AUTHORIZATION));
        assertNotNull(outbound.headers().get(HttpHeaders.Names.AUTHORIZATION));
        assertTrue(outbound.headers().contains(HttpHeaders.Names.HOST));
        assertEquals("Couchbase Client Mock", outbound.headers().get(HttpHeaders.Names.USER_AGENT));
    }

}