org.smartdeveloperhub.harvesters.scm.backend.notification.CollectorControllerTest.java Source code

Java tutorial

Introduction

Here is the source code for org.smartdeveloperhub.harvesters.scm.backend.notification.CollectorControllerTest.java

Source

/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the Smart Developer Hub Project:
 *     http://www.smartdeveloperhub.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2015-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.smartdeveloperhub.harvesters.scm:scm-harvester-backend:0.3.0
 *   Bundle      : scm-harvester-backend-0.3.0.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.smartdeveloperhub.harvesters.scm.backend.notification;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

import mockit.Expectations;
import mockit.Mock;
import mockit.MockUp;
import mockit.Mocked;
import mockit.Verifications;
import mockit.integration.junit4.JMockit;

import org.apache.http.HttpHeaders;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.smartdeveloperhub.harvesters.scm.backend.pojos.Collector;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.AMQP.Queue;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.MessageProperties;

@RunWith(JMockit.class)
public class CollectorControllerTest extends NotificationTestHelper {

    @Test
    public void testCreatePublisher$nullCollector() throws Exception {
        try {
            CollectorController.createPublisher(null);
            fail("Should not create controller with null collector");
        } catch (final NullPointerException e) {
            assertThat(e.getMessage(), equalTo("Collector cannot be null"));
        }
    }

    @Test
    public void testCreateAnonymousCollector$nullNotificationQueue() throws Exception {
        try {
            CollectorController.createAnonymousReceiver(defaultCollector(), null);
            fail("Should not create receiver with null blocking queue");
        } catch (final NullPointerException e) {
            assertThat(e.getMessage(), equalTo("Notification queue cannot be null"));
        }
    }

    @Test
    public void testCreateNamedCollector$nullQueueName() throws Exception {
        try {
            CollectorController.createNamedReceiver(defaultCollector(), null, null);
            fail("Should not create receiver with null queue name");
        } catch (final NullPointerException e) {
            assertThat(e.getMessage(), equalTo("Queue name cannot be null"));
        }
    }

    @Test
    public void testCreateNamedCollector$nullNotificationQueue() throws Exception {
        try {
            CollectorController.createNamedReceiver(defaultCollector(), "name", null);
            fail("Should not create receiver with null blocking queue");
        } catch (final NullPointerException e) {
            assertThat(e.getMessage(), equalTo("Notification queue cannot be null"));
        }
    }

    @Test
    public void testCollector() {
        final Collector defaultCollector = defaultCollector();
        final CollectorController sut = collectorController(defaultCollector);
        assertThat(sut.collector(), sameInstance(defaultCollector));
    }

    @Test
    public void testActualQueueName$requiresBeingConnected() {
        final CollectorController sut = defaultController();
        try {
            sut.actualQueueName();
            fail("Should not allow retrieving the actual queue name without being connected");
        } catch (final IllegalStateException e) {
            assertThat(e.getMessage(), equalTo("Not connected"));
        }
    }

    @Test
    public void testActualQueueName$onlyAvailableForReceivers() throws ControllerException {
        final CollectorController sut = CollectorController.createPublisher(defaultCollector());
        sut.connect();
        try {
            assertThat(sut.actualQueueName(), nullValue());
        } finally {
            sut.disconnect();
        }
    }

    @Test
    public void testActualQueueName$anonymous() throws ControllerException {
        final LinkedBlockingQueue<SuspendedNotification> queue = new LinkedBlockingQueue<SuspendedNotification>();
        final Collector defaultCollector = defaultCollector();
        final CollectorController sut = CollectorController.createAnonymousReceiver(defaultCollector, queue);
        try {
            sut.connect();
            assertThat(sut.actualQueueName(), notNullValue());
        } finally {
            sut.disconnect();
        }
    }

    @Test
    public void testActualQueueName$named() throws ControllerException {
        final CollectorController sut = defaultController();
        try {
            sut.connect();
            assertThat(sut.actualQueueName(), equalTo("name"));
        } finally {
            sut.disconnect();
        }
    }

    @Test
    public void testQueueName$anonymous() throws ControllerException {
        final LinkedBlockingQueue<SuspendedNotification> queue = new LinkedBlockingQueue<SuspendedNotification>();
        final Collector defaultCollector = defaultCollector();
        final CollectorController sut = CollectorController.createAnonymousReceiver(defaultCollector, queue);
        assertThat(sut.queueName(), nullValue());
    }

    @Test
    public void testQueueName$named() throws ControllerException {
        final CollectorController sut = defaultController();
        assertThat(sut.queueName(), equalTo("name"));
    }

    @Test
    public void testConnect$cannotConnectTwice() throws ControllerException {
        final CollectorController sut = defaultController();
        sut.connect();
        try {
            sut.connect();
            fail("Should not allow connecting twice");
        } catch (final IllegalStateException e) {
            assertThat(e.getMessage(), equalTo("Already connected"));
        }
    }

    @Test
    public void testConnect$shouldConnectIfExchangeAlreadyAvailable(@Mocked final Channel channel)
            throws Exception {
        new MockUp<ConnectionManager>() {
            private boolean connected = false;

            @Mock(invocations = 1)
            void connect() {
                this.connected = true;
            }

            @Mock(invocations = 1)
            void disconnect() {
            }

            @Mock
            boolean isConnected() {
                return this.connected;
            }

            @Mock
            Channel channel() {
                return channel;
            }
        };
        new MockUp<FailureAnalyzer>() {
            @Mock
            boolean isExchangeDeclarationRecoverable(final IOException e) {
                return true;
            }
        };
        final Collector defaultCollector = defaultCollector();
        new Expectations() {
            {
                channel.exchangeDeclare(defaultCollector.getExchangeName(), "topic", true);
                this.result = new IOException("Failure");
            }
        };
        final CollectorController sut = collectorController(defaultCollector);
        try {
            sut.connect();
        } finally {
            sut.disconnect();
        }
    }

    @Test
    public void testConnect$shouldCleanUpOnExchangeSetupFailure(@Mocked final Channel channel) throws Exception {
        new MockUp<ConnectionManager>() {
            private boolean connected = false;

            @Mock(invocations = 1)
            void connect() {
                this.connected = true;
            }

            @Mock(invocations = 1)
            void disconnect() {
            }

            @Mock
            boolean isConnected() {
                return this.connected;
            }

            @Mock
            Channel channel() {
                return channel;
            }
        };
        final Collector defaultCollector = defaultCollector();
        new Expectations() {
            {
                channel.exchangeDeclare(defaultCollector.getExchangeName(), "topic", true);
                this.result = new IOException("Failure");
            }
        };
        final CollectorController sut = collectorController(defaultCollector);
        try {
            sut.connect();
            fail("Should not connect on failure");
        } catch (final ControllerException e) {
            assertThat(e.getMessage(),
                    equalTo("Could not create exchange named '" + defaultCollector.getExchangeName() + "'"));
            assertThat(e.getCause(), instanceOf(IOException.class));
            assertThat(e.getCause().getMessage(), equalTo("Failure"));
        }
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testConnect$shouldCleanUpOnQueueDeclarationFailure(@Mocked final Channel channel) throws Exception {
        new MockUp<ConnectionManager>() {
            private boolean connected = false;

            @Mock(invocations = 1)
            void connect() {
                this.connected = true;
            }

            @Mock(invocations = 1)
            void disconnect() {
            }

            @Mock
            boolean isConnected() {
                return this.connected;
            }

            @Mock
            Channel channel() {
                return channel;
            }
        };
        final Collector defaultCollector = defaultCollector();
        new Expectations() {
            {
                channel.queueDeclare((String) this.any, true, true, true, (Map<String, Object>) this.any);
                this.result = new IOException("Failure");
            }
        };
        final CollectorController sut = collectorController(defaultCollector);
        try {
            sut.connect();
            fail("Should not connect on failure");
        } catch (final ControllerException e) {
            assertThat(e.getMessage(), equalTo("Could not create queue named 'name'"));
            assertThat(e.getCause(), instanceOf(IOException.class));
            assertThat(e.getCause().getMessage(), equalTo("Failure"));
        }
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testConnect$shouldCleanUpOnQueueBindingFailure(@Mocked final Channel channel,
            @Mocked final Queue.DeclareOk ok) throws Exception {
        new MockUp<ConnectionManager>() {
            private boolean connected = false;

            @Mock(invocations = 1)
            void connect() {
                this.connected = true;
            }

            @Mock(invocations = 1)
            void disconnect() {
            }

            @Mock
            boolean isConnected() {
                return this.connected;
            }

            @Mock
            Channel channel() {
                return channel;
            }
        };
        final Collector defaultCollector = defaultCollector();
        new Expectations() {
            {
                channel.queueDeclare((String) this.any, true, true, true, (Map<String, Object>) this.any);
                this.result = ok;
                ok.getQueue();
                this.result = "actualQueueName";
                channel.queueBind("actualQueueName", defaultCollector.getExchangeName(),
                        Notifications.ROUTING_KEY_PATTERN);
                this.result = new IOException("Failure");
            }
        };
        final CollectorController sut = collectorController(defaultCollector);
        try {
            sut.connect();
            fail("Should not connect on failure");
        } catch (final ControllerException e) {
            assertThat(e.getMessage(),
                    equalTo("Could not bind queue 'actualQueueName' to exchange '"
                            + defaultCollector.getExchangeName() + "' using routing key '"
                            + Notifications.ROUTING_KEY_PATTERN + "'"));
            assertThat(e.getCause(), instanceOf(IOException.class));
            assertThat(e.getCause().getMessage(), equalTo("Failure"));
        }
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testConnect$shouldCleanUpOnConsumerRegistrationFailure(@Mocked final Channel channel,
            @Mocked final Queue.DeclareOk ok) throws Exception {
        new MockUp<ConnectionManager>() {
            private boolean connected = false;

            @Mock(invocations = 1)
            void connect() {
                this.connected = true;
            }

            @Mock(invocations = 1)
            void disconnect() {
            }

            @Mock
            boolean isConnected() {
                return this.connected;
            }

            @Mock
            Channel channel() {
                return channel;
            }
        };
        final Collector defaultCollector = defaultCollector();
        new Expectations() {
            {
                channel.queueDeclare((String) this.any, true, true, true, (Map<String, Object>) this.any);
                this.result = ok;
                ok.getQueue();
                this.result = "actualQueueName";
                channel.basicConsume("actualQueueName", false, (Consumer) this.any);
                this.result = new IOException("Failure");
            }
        };
        final CollectorController sut = collectorController(defaultCollector);
        try {
            sut.connect();
            fail("Should not connect on failure");
        } catch (final ControllerException e) {
            assertThat(e.getMessage(), equalTo("Could not register consumer for queue 'actualQueueName'"));
            assertThat(e.getCause(), instanceOf(IOException.class));
            assertThat(e.getCause().getMessage(), equalTo("Failure"));
        }
    }

    @Test
    public void testDisconnect$shouldUnlockOnFailure(@Mocked final Channel channel) throws Exception {
        new MockUp<ConnectionManager>() {
            private boolean connected = false;

            @Mock(invocations = 1)
            void connect() {
                this.connected = true;
            }

            @Mock(invocations = 1)
            void disconnect() {
                throw new RuntimeException("Failure");
            }

            @Mock
            boolean isConnected() {
                return this.connected;
            }

            @Mock
            Channel channel() {
                return channel;
            }
        };
        final CollectorController sut = defaultController();
        sut.connect();
        try {
            sut.disconnect();
            fail("Should not shallow exceptions");
        } catch (final RuntimeException e) {
            assertThat(e.getMessage(), equalTo("Failure"));
        }
    }

    @Test
    public void testDisconnect$isIdempotent() throws ControllerException, IOException {
        final CollectorController sut = CollectorController.createPublisher(defaultCollector());
        sut.disconnect();
        sut.disconnect();
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testDisconnect$ignoreCleanerFailures(@Mocked final Channel channel,
            @Mocked final Queue.DeclareOk ok) throws Exception {
        new MockUp<ConnectionManager>() {
            private boolean connected = false;

            @Mock(invocations = 1)
            void connect() {
                this.connected = true;
            }

            @Mock(invocations = 1)
            void disconnect() {
            }

            @Mock
            boolean isConnected() {
                return this.connected;
            }

            @Mock
            Channel channel() {
                return channel;
            }
        };
        final Collector defaultCollector = defaultCollector();
        final CollectorController sut = collectorController(defaultCollector);
        new Expectations() {
            {
                channel.queueDeclare((String) this.any, true, true, true, (Map<String, Object>) this.any);
                this.result = ok;
                ok.getQueue();
                this.result = "actualQueueName";
                channel.basicConsume("actualQueueName", false, (Consumer) this.any);
                channel.queueUnbind("actualQueueName", defaultCollector.getExchangeName(), (String) this.any);
                this.result = new IOException("failure");
            }
        };
        sut.connect();
        sut.disconnect();
    }

    @Test
    public void testPublishEvent$failsIfCannotSerializeEvent() throws ControllerException {
        new MockUp<EventUtil>() {
            @Mock
            String marshall(final Event e) throws IOException {
                throw new IOException("Failure");
            }
        };
        final Collector collector = defaultCollector();
        final RepositoryCreatedEvent event = new RepositoryCreatedEvent();
        final CollectorController sut = CollectorController.createPublisher(collector);
        sut.connect();
        try {
            sut.publishEvent(event);
            fail("Should not publishing null events");
        } catch (final ControllerException e) {
            assertThat(e.getMessage(), equalTo("Could not serialize event " + event));
            assertThat(e.getBrokerHost(), equalTo(collector.getBrokerHost()));
            assertThat(e.getBrokerPort(), equalTo(collector.getBrokerPort()));
            assertThat(e.getVirtualHost(), equalTo(collector.getVirtualHost()));
            assertThat(e.getCause(), instanceOf(IOException.class));
            assertThat(e.getCause().getMessage(), equalTo("Failure"));
        }
    }

    @Test
    public void testPublishEvent$requiresBeingConnected() throws ControllerException, IOException {
        final CollectorController sut = CollectorController.createPublisher(defaultCollector());
        try {
            sut.publishEvent("payload", "type");
            fail("Should not publishing events without being connected");
        } catch (final IllegalStateException e) {
            assertThat(e.getMessage(), equalTo("Not connected"));
        }
    }

    @Test
    public void testPublishEvent$usesCurrentChannelAndImplementsNotificationProtocol(@Mocked final Channel channel)
            throws Exception {
        new MockUp<ConnectionManager>() {
            private boolean connected = false;

            @Mock(invocations = 1)
            void connect() {
                this.connected = true;
            }

            @Mock(invocations = 1)
            void disconnect() {
            }

            @Mock
            boolean isConnected() {
                return this.connected;
            }

            @Mock
            Channel channel() {
                return channel;
            }

            @Mock(invocations = 1)
            Channel currentChannel() {
                return channel;
            }

            @Mock(invocations = 0)
            void discardChannel() {
            }
        };
        final Collector defaultCollector = defaultCollector();
        new Expectations() {
            {
                channel.basicPublish(defaultCollector.getExchangeName(),
                        Notifications.routingKey(RepositoryCreatedEvent.class), true, (BasicProperties) this.any,
                        (byte[]) this.any);
            }
        };
        final CollectorController sut = CollectorController.createPublisher(defaultCollector);
        final RepositoryCreatedEvent event = new RepositoryCreatedEvent();
        event.setInstance(defaultCollector.getInstance());
        event.setNewRepositories(Arrays.asList("1", "2"));
        sut.connect();
        try {
            sut.publishEvent(event);
        } finally {
            sut.disconnect();
        }
        new Verifications() {
            {
                BasicProperties props;
                byte[] body;
                channel.basicPublish(defaultCollector.getExchangeName(),
                        Notifications.routingKey(RepositoryCreatedEvent.class), true, props = withCapture(),
                        body = withCapture());
                assertThat(new String(body), equalTo(EventUtil.marshall(event)));
                assertThat(props.getHeaders().get(HttpHeaders.CONTENT_TYPE), equalTo((Object) Notifications.MIME));
                assertThat(props.getDeliveryMode(),
                        equalTo(MessageProperties.MINIMAL_PERSISTENT_BASIC.builder().build().getDeliveryMode()));
            }
        };
    }

    @Test
    public void testPublishEvent$discardsCurrentChannelOnCheckedException(@Mocked final Channel channel)
            throws Exception {
        new MockUp<ConnectionManager>() {
            private boolean connected = false;

            @Mock(invocations = 1)
            void connect() {
                this.connected = true;
            }

            @Mock(invocations = 1)
            void disconnect() {
            }

            @Mock
            boolean isConnected() {
                return this.connected;
            }

            @Mock
            Channel channel() {
                return channel;
            }

            @Mock(invocations = 1)
            Channel currentChannel() {
                return channel;
            }

            @Mock(invocations = 1)
            void discardChannel() {
            }
        };
        final Collector defaultCollector = defaultCollector();
        new Expectations() {
            {
                channel.basicPublish(defaultCollector.getExchangeName(),
                        Notifications.routingKey(RepositoryCreatedEvent.class), true, (BasicProperties) this.any,
                        (byte[]) this.any);
                this.result = new IOException("Failure");
            }
        };
        final CollectorController sut = CollectorController.createPublisher(defaultCollector);
        final RepositoryCreatedEvent event = new RepositoryCreatedEvent();
        event.setInstance(defaultCollector.getInstance());
        event.setNewRepositories(Arrays.asList("1", "2"));
        sut.connect();
        try {
            sut.publishEvent(event);
            fail("Should fail to publish an event if the RabbitMQ client fails");
        } catch (final ControllerException e) {
            assertThat(e.getMessage(), containsString(EventUtil.marshall(event)));
            assertThat(e.getMessage(), containsString(defaultCollector.getExchangeName()));
            assertThat(e.getMessage(), containsString(Notifications.routingKey(RepositoryCreatedEvent.class)));
            assertThat(e.getMessage(), containsString(defaultCollector.getBrokerHost()));
            assertThat(e.getMessage(), containsString(":" + defaultCollector.getBrokerPort()));
            assertThat(e.getMessage(), containsString(defaultCollector.getVirtualHost()));
            assertThat(e.getCause(), instanceOf(IOException.class));
            assertThat(e.getCause().getMessage(), equalTo("Failure"));
        } finally {
            sut.disconnect();
        }
    }

    @Test
    public void testPublishEvent$discardsCurrentChannelOnRuntimeException(@Mocked final Channel channel)
            throws Exception {
        new MockUp<ConnectionManager>() {
            private boolean connected = false;

            @Mock(invocations = 1)
            void connect() {
                this.connected = true;
            }

            @Mock(invocations = 1)
            void disconnect() {
            }

            @Mock
            boolean isConnected() {
                return this.connected;
            }

            @Mock
            Channel channel() {
                return channel;
            }

            @Mock(invocations = 1)
            Channel currentChannel() {
                return channel;
            }

            @Mock(invocations = 1)
            void discardChannel() {
            }
        };
        final Collector defaultCollector = defaultCollector();
        new Expectations() {
            {
                channel.basicPublish(defaultCollector.getExchangeName(),
                        Notifications.routingKey(RepositoryCreatedEvent.class), true, (BasicProperties) this.any,
                        (byte[]) this.any);
                this.result = new RuntimeException("Failure");
            }
        };
        final CollectorController sut = CollectorController.createPublisher(defaultCollector);
        final RepositoryCreatedEvent event = new RepositoryCreatedEvent();
        event.setInstance(defaultCollector.getInstance());
        event.setNewRepositories(Arrays.asList("1", "2"));
        sut.connect();
        try {
            sut.publishEvent(event);
            fail("Should fail to publish an event if the RabbitMQ client fails");
        } catch (final ControllerException e) {
            assertThat(e.getMessage(), containsString(EventUtil.marshall(event)));
            assertThat(e.getMessage(), containsString(defaultCollector.getExchangeName()));
            assertThat(e.getMessage(), containsString(Notifications.routingKey(RepositoryCreatedEvent.class)));
            assertThat(e.getMessage(), containsString(defaultCollector.getBrokerHost()));
            assertThat(e.getMessage(), containsString(":" + defaultCollector.getBrokerPort()));
            assertThat(e.getMessage(), containsString(defaultCollector.getVirtualHost()));
            assertThat(e.getCause(), instanceOf(RuntimeException.class));
            assertThat(e.getCause().getMessage(), equalTo("Failure"));
        } finally {
            sut.disconnect();
        }
    }

    private CollectorController defaultController() {
        final Collector defaultCollector = defaultCollector();
        final CollectorController sut = collectorController(defaultCollector);
        return sut;
    }

    private CollectorController collectorController(final Collector defaultCollector) {
        final LinkedBlockingQueue<SuspendedNotification> queue = new LinkedBlockingQueue<SuspendedNotification>();
        final CollectorController sut = CollectorController.createNamedReceiver(defaultCollector, "name", queue);
        return sut;
    }

}