de.tuberlin.cit.livescale.messaging.endpoints.AMQPEndpointTest.java Source code

Java tutorial

Introduction

Here is the source code for de.tuberlin.cit.livescale.messaging.endpoints.AMQPEndpointTest.java

Source

/***********************************************************************************************************************
 *
 * Copyright (C) 2010 by the Stratosphere project (http://stratosphere.eu)
 *
 * 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 de.tuberlin.cit.livescale.messaging.endpoints;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.powermock.reflect.Whitebox;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.AMQP.Queue;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import de.tuberlin.cit.livescale.messaging.MessageCenter;
import de.tuberlin.cit.livescale.messaging.MessageFactory;
import de.tuberlin.cit.livescale.messaging.MessageManifest;
import de.tuberlin.cit.livescale.messaging.endpoints.AMQPEndpoint;
import de.tuberlin.cit.livescale.messaging.endpoints.MessageEndpoint;
import de.tuberlin.cit.livescale.messaging.endpoints.MessageEndpointListener;
import de.tuberlin.cit.livescale.messaging.messages.TestMessage;
import de.tuberlin.cit.livescale.messaging.messages.TestRequestMessage;

/**
 * Tests for the {@link AMQPEndpoint}
 * 
 * @author Bernd Louis
 * 
 */
public class AMQPEndpointTest {

    private Map<String, String> exampleConf;
    private Properties emptyConf;

    @Before
    public void setUp() {
        this.exampleConf = new HashMap<String, String>();
        this.exampleConf.put(AMQPEndpoint.CONFIG_KEY_BROKER_ADDRESS, "localhost");
        this.exampleConf.put(AMQPEndpoint.CONFIG_KEY_BROKER_PORT, String.valueOf(2342));
        this.exampleConf.put(AMQPEndpoint.CONFIG_KEY_ROUTING_KEY, "test");
        this.exampleConf.put(AMQPEndpoint.CONFIG_KEY_LISTEN_FOR_TASKS, String.valueOf(true));
        this.exampleConf.put(AMQPEndpoint.CONFIG_KEY_LISTEN_FOR_BROADCASTS, String.valueOf(true));
        this.exampleConf.put(AMQPEndpoint.CONFIG_KEY_EXCHANGE_NAME, "cit_stream_exchange");

        this.emptyConf = new Properties();
        this.emptyConf.setProperty("noConcurrency", String.valueOf(true));

    }

    @Test
    public void testConfiguration() {
        AMQPEndpoint ep = new AMQPEndpoint();
        ep.configure(this.exampleConf);

        this.<String>checkEquality(ep, AMQPEndpoint.CONFIG_KEY_BROKER_ADDRESS);
        this.<Integer>checkEquality(ep, AMQPEndpoint.CONFIG_KEY_BROKER_PORT);
        this.<String>checkEquality(ep, AMQPEndpoint.CONFIG_KEY_ROUTING_KEY);
        this.<Boolean>checkEquality(ep, AMQPEndpoint.CONFIG_KEY_LISTEN_FOR_TASKS);
        this.<Boolean>checkEquality(ep, AMQPEndpoint.CONFIG_KEY_LISTEN_FOR_BROADCASTS);
        this.<String>checkEquality(ep, AMQPEndpoint.CONFIG_KEY_EXCHANGE_NAME);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testConfigurationFail() {
        new AMQPEndpoint().configure(new HashMap<String, String>());
    }

    /**
     * This helper casts back from the original type <code>T</code> and checks
     * if that equals the value in the example configuration.
     * 
     * ATTN: this will fail if the actual property names of {@link AMQPEndpoint}
     * won't match their config keys. In that case you gotta check yourself
     * using <code>assertEquals</code>
     * 
     * @param <T>
     *            the type to try to cast to
     * @param ep
     *            the endpoint
     * @param key
     *            the config_key
     */
    private <T> void checkEquality(AMQPEndpoint ep, String key) {
        assertEquals(this.exampleConf.get(key),
                String.valueOf(Whitebox.<T>getInternalState(ep, key, AMQPEndpoint.class)));
    }

    @Test
    public void testInferedConfiguration() throws IOException, URISyntaxException {
        // Note: like all "Infered" Tests this relies on the test-configuration
        // located
        // in src/test/resources
        MessageCenter mc = new MessageCenter(false);
        Collection<MessageEndpoint> eps = Whitebox.getInternalState(mc, "messageEndpoints");
        boolean hasAMQPEndpoint = false;
        for (MessageEndpoint ep : eps) {
            if (ep instanceof AMQPEndpoint) {
                hasAMQPEndpoint = true;
                break;
            }
        }
        assertTrue("MessageCenter should have an instanceof AMQPEndpoint in its endpoints", hasAMQPEndpoint);
        assertTrue(mc.hasEndpointToURI(new URI("amqp", "myRabbit", "", "")));
    }

    @Test(expected = IllegalStateException.class)
    public void testStartUnconfigured() {
        new AMQPEndpoint().start();
    }

    /**
     * Creates an instance of {@link AMQPEndpoint} whilst
     * mocking all RabbitMQ-Classes / ifaces involved.
     * 
     * @throws IOException
     */
    @Test
    public void testAMQPLifecycle() throws IOException {
        // prepare mocks
        ConnectionFactory fac = mock(ConnectionFactory.class);
        Connection con = mock(Connection.class);
        Channel chan = mock(Channel.class);
        Queue.DeclareOk declareOK = mock(Queue.DeclareOk.class);

        // ConnectionFactory
        when(fac.newConnection()).thenReturn(con);
        // Connection
        when(con.createChannel()).thenReturn(chan);
        // Channel
        when(chan.queueDeclare()).thenReturn(declareOK);
        // DeclareOK result object
        String queueName = "testQueue";
        when(declareOK.getQueue()).thenReturn(queueName);

        AMQPEndpoint ep = new AMQPEndpoint();
        Whitebox.setInternalState(ep, "connectionFactory", fac);

        ep.configure(this.exampleConf);
        ep.start();

        // verify "important" connect methods were called
        verify(fac).newConnection();
        verify(con).createChannel();
        verify(chan).queueDeclare();
        verify(declareOK).getQueue();
        // in the example conf we're bindingn to 3 queues, let's check that
        String exchange = this.exampleConf.get(AMQPEndpoint.CONFIG_KEY_EXCHANGE_NAME);
        String routingKey = this.exampleConf.get(AMQPEndpoint.CONFIG_KEY_ROUTING_KEY);
        // listens for broadcasts
        verify(chan, times(1)).queueBind(eq(queueName), eq(exchange), eq("broadcast"));
        // listens for tasks
        verify(chan, times(1)).queueBind(eq("cit_stream_tasks_queue_" + routingKey), eq(exchange),
                eq("task." + routingKey));
        // listens for broadcasts to broadcast.test
        verify(chan, times(1)).queueBind(eq(queueName), eq(exchange), eq("broadcast." + routingKey));
        String exclusiveQueueName = Whitebox.getInternalState(ep, "exclusiveQueueName");
        assertEquals(queueName, exclusiveQueueName);
    }

    /**
     * Tests sending a "normal" non-{@link de.tuberlin.cit.livescale.messaging.RequestMessage}
     * 
     * @throws IOException
     * @throws URISyntaxException
     */
    @Test
    public void testAMQPSendNoResponse() throws IOException, URISyntaxException {
        ConnectionFactory fac = mock(ConnectionFactory.class);
        Connection con = mock(Connection.class);
        Channel chan = mock(Channel.class);
        Queue.DeclareOk declareOK = mock(Queue.DeclareOk.class);

        // ConnectionFactory
        when(fac.newConnection()).thenReturn(con);
        // Connection
        when(con.createChannel()).thenReturn(chan);
        // Channel
        when(chan.queueDeclare()).thenReturn(declareOK);
        // DeclareOK result object
        String queueName = "testQueue";
        when(declareOK.getQueue()).thenReturn(queueName);

        AMQPEndpoint ep = new AMQPEndpoint();
        ep.setName("amqpTest");
        Whitebox.setInternalState(ep, "connectionFactory", fac);
        ep.configure(this.exampleConf);

        // configuredRoutingKey
        String routingKey = this.exampleConf.get(AMQPEndpoint.CONFIG_KEY_ROUTING_KEY);
        URI targetURI = new URI("amqp:///test");

        MessageCenter mc = new MessageCenter(false, this.emptyConf);
        mc.addEndpoint(ep);
        mc.startAllEndpoints();
        assertTrue(mc.hasEndpointToURI(targetURI));

        // kickin the jams
        mc.send(new TestMessage(), targetURI);
        // verify(chan, times(1)).basicPublish(eq(""), eq(routingKey), (BasicProperties) isNull(), (byte[]) any());
    }

    /**
     * Tests message arrival on a {@link AMQPEndpoint} and
     * correctness of the generated responseURI in the
     * {@link MessageManifest} 
     * 
     * @throws IOException
     * @throws URISyntaxException
     */
    @Test
    public void testArrival() throws IOException, URISyntaxException {
        ConnectionFactory fac = mock(ConnectionFactory.class);
        Connection con = mock(Connection.class);
        Channel chan = mock(Channel.class);
        Queue.DeclareOk declareOK = mock(Queue.DeclareOk.class);

        // ConnectionFactory
        when(fac.newConnection()).thenReturn(con);
        // Connection
        when(con.createChannel()).thenReturn(chan);
        // Channel
        when(chan.queueDeclare()).thenReturn(declareOK);
        // DeclareOK result object
        String queueName = "testQueue";
        when(declareOK.getQueue()).thenReturn(queueName);

        AMQPEndpoint ep = new AMQPEndpoint();
        String endpointName = "amqpTest";
        ep.setName(endpointName);
        Whitebox.setInternalState(ep, "connectionFactory", fac);
        ep.configure(this.exampleConf);
        // hookup a listener
        MessageEndpointListener listener = mock(MessageEndpointListener.class);
        ep.addMessageEndpointListener(listener);

        // kickin the jams 
        ep.start();
        // hook up the consumer / manually call the callback
        ArgumentCaptor<DefaultConsumer> consumer = ArgumentCaptor.forClass(DefaultConsumer.class);
        // should in total consume the own queue and the tasks queue
        // depends on config settings
        verify(chan, times(2)).basicConsume(anyString(), anyBoolean(), (Consumer) anyObject());
        verify(chan).basicConsume(eq(queueName), anyBoolean(), consumer.capture());
        String replyQueueName = "replyQueue";
        // faux envelope
        Envelope envelope = new Envelope(-1l, false, "daExchange", "daRoutingKey");
        // faux properties
        BasicProperties props = new BasicProperties.Builder().replyTo(replyQueueName)
                .appId(UUID.randomUUID().toString()).build();
        // faux message 
        TestRequestMessage message = new TestRequestMessage();
        // faux manifest
        MessageManifest mmIn = new MessageManifest(message, new URI("amqp:///targetQueue"));
        // call the callback function
        consumer.getValue().handleDelivery("leTag", envelope, props, MessageFactory.encode(mmIn));
        ArgumentCaptor<MessageManifest> mm = ArgumentCaptor.forClass(MessageManifest.class);
        verify(listener).handleMessageReceived(mm.capture());
        assertEquals("amqp", mm.getValue().getResponseURI().getScheme());
        assertEquals(endpointName, mm.getValue().getResponseURI().getAuthority());
        assertEquals("/" + replyQueueName, mm.getValue().getResponseURI().getPath());
    }

    /**
     * 
     */
    @Test
    public void testURIParser() throws URISyntaxException {
        URI target1 = new URI("amqp://name/routingKey");
        AMQPEndpoint.URIParser parser = new AMQPEndpoint.URIParser(target1);
        assertEquals("name", parser.getInstanceName());
        assertEquals("routingKey", parser.getRoutingKey());

        URI target2 = new URI("amqp://name/routingKey?exchange=daExchange");
        parser = new AMQPEndpoint.URIParser(target2);
        assertEquals("name", parser.getInstanceName());
        assertEquals("routingKey", parser.getRoutingKey());
        assertEquals("daExchange", parser.getExchangeName());

        URI target3 = new URI("amqp:///routingKey?exchange=daExchange");
        parser = new AMQPEndpoint.URIParser(target3);
        assertEquals("amqp", parser.getScheme());
        assertNull(parser.getInstanceName());
        assertEquals("routingKey", parser.getRoutingKey());
        assertEquals("daExchange", parser.getExchangeName());
    }

    /**
     * Tests parser and 
     * 
     * @throws URISyntaxException
     */
    @Test
    public void testURIBuilder() throws URISyntaxException {
        String routingKey = "leRoute";
        String instanceName = "leInstance";
        String exchangeName = "leExchange";
        URI uri = new AMQPEndpoint.URIBuilder().routingKey(routingKey).instanceName(instanceName)
                .exchangeName(exchangeName).build();
        AMQPEndpoint.URIParser parser = new AMQPEndpoint.URIParser(new URI(uri.toString()));
        assertEquals(routingKey, parser.getRoutingKey());
        assertEquals(instanceName, parser.getInstanceName());
        assertEquals(exchangeName, parser.getExchangeName());
    }

    @Test
    public void testURIBuilderRoutingKeyConcat() throws URISyntaxException {
        URI uri = new AMQPEndpoint.URIBuilder().routingKey(new String[] { "one", "two", "three" }).build();
        assertEquals(uri.getPath(), "/one.two.three");
    }
}