package com.xing.beetle;

import com.rabbitmq.client.*;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Matchers;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import static org.mockito.Mockito.*;

/**
 *
 */
public class ClientTest {

    private Channel mockedChannelForClient(Client client) throws IOException {
        final ConnectionFactory mockFactory = mock(ConnectionFactory.class);
        final Connection mockConnection = mock(Connection.class);
        when(mockFactory.newConnection()).thenReturn(mockConnection);
        final Channel mockChannel = mock(Channel.class);
        when(mockConnection.createChannel()).thenReturn(mockChannel);
        client.setConnectionFactory(mockFactory);
        return mockChannel;
    }

    @Test
    public void connectDeclaresAMQPItems() throws URISyntaxException, IOException {
        Client client = Client.builder().addBroker(1234).build();

        final ConnectionFactory mockFactory = mock(ConnectionFactory.class);
        final Connection mockConnection = mock(Connection.class);
        when(mockFactory.newConnection()).thenReturn(mockConnection);

        final Channel mockChannel = mock(Channel.class);
        when(mockConnection.createChannel()).thenReturn(mockChannel);
        client.setConnectionFactory(mockFactory);

        client.registerQueue(Queue.builder().name("testQ").build());
        client.start();

        verify(mockChannel).exchangeDeclare("testQ", "topic", true);
        verify(mockChannel).queueDeclare("testQ", true, false, false, null);
        verify(mockChannel).queueBind("testQ", "testQ", "testQ");
        verifyNoMoreInteractions(mockChannel);

        client.stop();

        verify(mockConnection).createChannel();
        verify(mockConnection).addShutdownListener(client);
        verify(mockConnection).close();
        verifyNoMoreInteractions(mockConnection);
    }

    @Test
    public void connectionFailureSchedulesReconnect() throws URISyntaxException, IOException {
        final Client client = Client.builder().addBroker(1234).addBroker(1235).build();
        final Client clientSpy = spy(client);

        final ConnectionFactory mockFactory = mock(ConnectionFactory.class);
        final Connection mockConnection = mock(Connection.class);
        when(mockFactory.newConnection()).thenReturn(mockConnection).thenThrow(new IOException("Testing reconnection")).getMock();
        doNothing().when(clientSpy).scheduleReconnect(Matchers.<URI>any());

        final Channel mockChannel = mock(Channel.class);
        when(mockConnection.createChannel()).thenReturn(mockChannel);
        clientSpy.setConnectionFactory(mockFactory);

        clientSpy.start();

        verify(clientSpy).scheduleReconnect(Matchers.<URI>any());
    }

    @Test
    public void clientBuilderHasDefaultBroker() {
        final Client client = Client.builder().build();
        Assert.assertEquals("ClientBuilder should add at a least a default broker.", 1, client.getBrokerUris().size());
    }

    @Test
    public void clientStartStartsConsumers() throws IOException {
        final Client client = Client.builder().build();
        final Client clientSpy = spy(client);

        final Channel channel = mockedChannelForClient(clientSpy);

        Queue queue = Queue.builder().name("test-queue").build();
        clientSpy.registerQueue(queue);
        clientSpy.registerHandler(queue, new DefaultMessageHandler() {
            @Override
            public void process(Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
                // ignore
            }
        });
        clientSpy.start();
        verify(channel).basicConsume(eq("test-queue"), Matchers.<Consumer>anyObject());
    }
}

@@ -125,16 +125,61 @@
 atchers;
+%0Aimport org.mockito.stubbing.OngoingStubbing;
 %0A%0Aimport
@@ -256,16 +256,67 @@
 ception;
+%0Aimport java.util.ArrayList;%0Aimport java.util.List;
 %0A%0Aimport
@@ -854,16 +854,16 @@
 ctory);%0A
-
         
@@ -881,32 +881,1135 @@
 Channel;%0A    %7D%0A%0A
+    private List%3CConnection%3E mockedConnectionsForClient(Client client, int numConnections) throws IOException %7B%0A        final ConnectionFactory mockFactory = mock(ConnectionFactory.class);%0A        final OngoingStubbing%3CConnection%3E newConnectionStub = when(mockFactory.newConnection());%0A        List%3CConnection%3E conns = new ArrayList%3CConnection%3E(numConnections);%0A        // TODO OMG this is ugly. there's got to be a better way.%0A        OngoingStubbing%3CConnection%3E connectionOngoingStubbing = null;%0A        for (int i = 0; i %3C numConnections; i++) %7B%0A            final Connection mockConnection = mock(Connection.class);%0A            if (i == 0) %7B%0A                connectionOngoingStubbing = newConnectionStub.thenReturn(mockConnection);%0A            %7D else %7B%0A                connectionOngoingStubbing.thenReturn(mockConnection);%0A            %7D%0A            final Channel mockChannel = mock(Channel.class);%0A            when(mockConnection.createChannel()).thenReturn(mockChannel);%0A            conns.add(mockConnection);%0A        %7D%0A        client.setConnectionFactory(mockFactory);%0A        return conns;%0A    %7D%0A%0A
     @Test%0A    pu
@@ -4961,11 +4961,622 @@
 );%0A    %7D
+%0A%0A    @Test%0A    public void handlingExceptionsWhenStopping() throws IOException, URISyntaxException %7B%0A        final Client client = Client.builder().addBroker(1234).addBroker(1235).build();%0A        final Client clientSpy = spy(client);%0A%0A        final List%3CConnection%3E connections = mockedConnectionsForClient(clientSpy, 2);%0A        for (Connection connection : connections) %7B%0A            doThrow(IOException.class).when(connection).close();%0A        %7D%0A        clientSpy.start();%0A        clientSpy.stop();%0A        for (Connection connection : connections) %7B%0A            verify(connection).close();%0A        %7D%0A    %7D
 %0A%7D%0A

test properly closing all connections even if some throw IOExceptions when attempting to close the connection