// Copyright (c) 2007-Present Pivotal Software, Inc.  All rights reserved.
//
// This software, the RabbitMQ Java client library, is triple-licensed under the
// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2
// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see
// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2.  For the ASL,
// please see LICENSE-APACHE2.
//
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
// either express or implied. See the LICENSE file for specific language governing
// rights and limitations of this software.
//
// If you have any questions regarding licensing, please contact us at
// info@rabbitmq.com.


package com.rabbitmq.client.test.server;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import org.junit.Test;

import com.rabbitmq.client.GetResponse;
import com.rabbitmq.client.test.functional.BindingLifecycleBase;
import com.rabbitmq.tools.Host;

/**
 * This tests whether bindings are created and nuked properly.
 *
 * The tests attempt to declare durable queues on a secondary node, if
 * present, and that node is restarted as part of the tests while the
 * primary node is still running. That way we exercise any node-down
 * handler code in the server.
 *
 */
public class DurableBindingLifecycle extends BindingLifecycleBase {
    @Override
    protected void restart() throws IOException, TimeoutException {
        if (clusteredConnection != null) {
            clusteredConnection.abort();
            clusteredConnection = null;
            clusteredChannel = null;
            alternateConnection = null;
            alternateChannel = null;

            Host.invokeMakeTarget(
              "stop-node" +
              " start-background-broker" +
              " RABBITMQ_NODENAME=\'" + Host.nodenameB() + "\'" +
              " RABBITMQ_NODE_PORT=" + Host.node_portB() +
              " RABBITMQ_CONFIG_FILE=\'" + Host.config_fileB() + "\'"
            );
        }
        restartPrimary();
    }

    private void restartPrimary() throws IOException, TimeoutException {
        tearDown();
        bareRestart();
        setUp();
    }

    /**
     *   Tests whether durable bindings are correctly recovered.
     */
    @Test public void durableBindingRecovery() throws IOException, TimeoutException {
        declareDurableTopicExchange(X);
        declareAndBindDurableQueue(Q, X, K);

        restart();

        for (int i = 0; i < N; i++) {
            basicPublishVolatile(X, K);
        }

        assertDelivered(Q, N);

        deleteQueue(Q);
        deleteExchange(X);
    }

    /**
     * This tests whether the bindings attached to a durable exchange
     * are correctly blown away when the exhange is nuked.
     *
     * This complements a unit test for testing non-durable exhanges.
     * In that case, an exchange is deleted and you expect any
     * bindings hanging to it to be deleted as well. To verify this,
     * the exchange is deleted and then recreated.
     *
     * After the recreation, the old bindings should no longer exist
     * and hence any messages published to that exchange get routed to
     * /dev/null
     *
     * This test exercises the durable variable of that test, so the
     * main difference is that the broker has to be restarted to
     * verify that the durable routes have been turfed.
     */
    @Test public void durableBindingsDeletion() throws IOException, TimeoutException {
        declareDurableTopicExchange(X);
        declareAndBindDurableQueue(Q, X, K);

        deleteExchange(X);

        restart();

        declareDurableTopicExchange(X);

        for (int i = 0; i < N; i++) {
            basicPublishVolatile(X, K);
        }

        GetResponse response = channel.basicGet(Q, true);
        assertNull("The initial response SHOULD BE null", response);

        deleteQueue(Q);
        deleteExchange(X);
    }


    /**
     * This tests whether the default bindings for durable queues
     * are recovered properly.
     *
     * The idea is to create a durable queue, nuke the server and then
     * publish a message to it using the queue name as a routing key
     */
    @Test public void defaultBindingRecovery() throws IOException, TimeoutException {
        declareDurableQueue(Q);

        restart();

        basicPublishVolatile("", Q);

        GetResponse response = channel.basicGet(Q, true);
        assertNotNull("The initial response SHOULD NOT be null", response);

        deleteQueue(Q);
    }

    /**
     * Test that when we have a transient exchange bound to a durable
     * queue and the durable queue is on a cluster node that restarts,
     * we do not lose the binding.  See bug 24009.
     */
    @Test public void transientExchangeDurableQueue() throws IOException, TimeoutException {
        // This test depends on the second node in the cluster to keep
        // the transient X alive
        if (clusteredConnection != null) {
            channel.exchangeDeclare("transientX", "fanout", false);
            channel.queueDeclare("durableQ", true, false, false, null);
            channel.queueBind("durableQ", "transientX", "");

            restartPrimary();

            basicPublishVolatile("transientX", "");
            assertDelivered("durableQ", 1);

            deleteExchange("transientX");
            deleteQueue("durableQ");
        }
    }
}
