// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <asiolink/io_service.h>
#include <d2/d2_cfg_mgr.h>
#include <d2/d2_cfg_mgr.h>
#include <d2/nc_remove.h>
#include <dns/messagerenderer.h>
#include <nc_test_utils.h>

#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <gtest/gtest.h>

using namespace std;
using namespace isc;
using namespace isc::d2;
using namespace isc::util;


namespace {

/// @brief Test class derived from NameRemoveTransaction to provide visibility
// to protected methods.
class NameRemoveStub : public NameRemoveTransaction {
public:
    NameRemoveStub(asiolink::IOServicePtr& io_service,
                   dhcp_ddns::NameChangeRequestPtr& ncr,
                   DdnsDomainPtr& forward_domain,
                   DdnsDomainPtr& reverse_domain,
                   D2CfgMgrPtr& cfg_mgr)
        : NameRemoveTransaction(io_service, ncr, forward_domain,
                                reverse_domain, cfg_mgr),
          simulate_send_exception_(false),
          simulate_build_request_exception_(false) {
    }

    virtual ~NameRemoveStub() {
    }

    /// @brief Simulates sending update requests to the DNS server
    ///
    /// This method simulates the initiation of an asynchronous send of
    /// a DNS update request. It overrides the actual sendUpdate method in
    /// the base class, thus avoiding an actual send, yet still increments
    /// the update attempt count and posts a next event of NOP_EVT.
    ///
    /// It will also simulate an exception-based failure of sendUpdate, if
    /// the simulate_send_exception_ flag is true.
    ///
    /// @param comment Parameter is unused, but present in base class method
    ///
    virtual void sendUpdate(const std::string& /* comment */) {
        if (simulate_send_exception_) {
            // Make the flag a one-shot by resetting it.
            simulate_send_exception_ = false;
            // Transition to failed.
            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
            return;
        }

        // Update send attempt count and post a NOP_EVT.
        setUpdateAttempts(getUpdateAttempts() + 1);
        postNextEvent(StateModel::NOP_EVT);
    }

    /// @brief Prepares the initial D2UpdateMessage
    ///
    /// This method overrides the NameChangeTransaction implementation to
    /// provide the ability to simulate an exception throw in the build
    /// request logic.
    /// If the one-shot flag, simulate_build_request_exception_ is true,
    /// this method will throw an exception, otherwise it will invoke the
    /// base class method, providing normal functionality.
    ///
    /// For parameter description see the NameChangeTransaction implementation.
    virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain) {
        if (simulate_build_request_exception_) {
            simulate_build_request_exception_ = false;
            isc_throw (NameRemoveTransactionError,
                       "Simulated build requests exception");
        }

        return (NameChangeTransaction::prepNewRequest(domain));
    }

    /// @brief Simulates receiving a response
    ///
    /// This method simulates the completion of a DNSClient send.  This allows
    /// the state handler logic devoted to dealing with IO completion to be
    /// fully exercised without requiring any actual IO.  The two primary
    /// pieces of information gleaned from IO completion are the DNSClient
    /// status which indicates whether or not the IO exchange was successful
    /// and the rcode, which indicates the server's reaction to the request.
    ///
    /// This method updates the transaction's DNS status value to that of the
    /// given parameter, and then constructs and DNS update response message
    /// with the given rcode value.  To complete the simulation it then posts
    /// a next event of IO_COMPLETED_EVT.
    ///
    /// @param status simulated DNSClient status
    /// @param rcode  simulated server response code
    void fakeResponse(const DNSClient::Status& status,
                      const dns::Rcode& rcode) {
        // Set the DNS update status.  This is normally set in
        // DNSClient IO completion handler.
        setDnsUpdateStatus(status);

        // Construct an empty message with the given Rcode.
        D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND));
        msg->setRcode(rcode);

        // Set the update response to the message.
        setDnsUpdateResponse(msg);

        // Post the IO completion event.
        postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
    }

    /// @brief Selects the first forward server.
    /// Some state handlers require a server to have been selected.
    /// This selects a server without going through the state
    /// transition(s) to do so.
    bool selectFwdServer() {
        if (getForwardDomain()) {
            initServerSelection(getForwardDomain());
            selectNextServer();
            return (getCurrentServer().get() != 0);
        }

        return (false);
    }

    /// @brief Selects the first reverse server.
    /// Some state handlers require a server to have been selected.
    /// This selects a server without going through the state
    /// transition(s) to do so.
    bool selectRevServer() {
        if (getReverseDomain()) {
            initServerSelection(getReverseDomain());
            selectNextServer();
            return (getCurrentServer().get() != 0);
        }

        return (false);
    }

    /// @brief One-shot flag which will simulate sendUpdate failure if true.
    bool simulate_send_exception_;

    /// @brief One-shot flag which will simulate an exception when sendUpdate
    /// failure if true.
    bool simulate_build_request_exception_;

    using StateModel::postNextEvent;
    using StateModel::setState;
    using StateModel::initDictionaries;
    using NameRemoveTransaction::defineEvents;
    using NameRemoveTransaction::verifyEvents;
    using NameRemoveTransaction::defineStates;
    using NameRemoveTransaction::verifyStates;
    using NameRemoveTransaction::readyHandler;
    using NameRemoveTransaction::selectingFwdServerHandler;
    using NameRemoveTransaction::getCurrentServer;
    using NameRemoveTransaction::removingFwdAddrsHandler;
    using NameRemoveTransaction::setDnsUpdateStatus;
    using NameRemoveTransaction::removingFwdRRsHandler;
    using NameRemoveTransaction::selectingRevServerHandler;
    using NameRemoveTransaction::removingRevPtrsHandler;
    using NameRemoveTransaction::processRemoveOkHandler;
    using NameRemoveTransaction::processRemoveFailedHandler;
    using NameRemoveTransaction::buildRemoveFwdAddressRequest;
    using NameRemoveTransaction::buildRemoveFwdRRsRequest;
    using NameRemoveTransaction::buildRemoveRevPtrsRequest;
};

typedef boost::shared_ptr<NameRemoveStub> NameRemoveStubPtr;

/// @brief Test fixture for testing NameRemoveTransaction
///
/// Note this class uses NameRemoveStub class to exercise non-public
/// aspects of NameRemoveTransaction.
class NameRemoveTransactionTest : public TransactionTest {
public:
    NameRemoveTransactionTest() {
    }

    virtual ~NameRemoveTransactionTest() {
    }

    /// @brief Creates a transaction which requests an IPv4 DNS update.
    ///
    /// The transaction is constructed around a predefined (i.e. "canned")
    /// IPv4 NameChangeRequest. The request has both forward and reverse DNS
    /// changes requested.  Based upon the change mask, the transaction
    /// will have either the forward, reverse, or both domains populated.
    ///
    /// @param change_mask determines which change directions are requested
    NameRemoveStubPtr makeTransaction4(int change_mask) {
        // Creates IPv4 remove request, forward, and reverse domains.
        setupForIPv4Transaction(dhcp_ddns::CHG_REMOVE, change_mask);

        // Now create the test transaction as would occur in update manager.
        return (NameRemoveStubPtr(new NameRemoveStub(io_service_, ncr_,
                                                     forward_domain_,
                                                     reverse_domain_,
                                                     cfg_mgr_)));
    }

    /// @brief Creates a transaction which requests an IPv6 DNS update.
    ///
    /// The transaction is constructed around a predefined (i.e. "canned")
    /// IPv6 NameChangeRequest. The request has both forward and reverse DNS
    /// changes requested.  Based upon the change mask, the transaction
    /// will have either the forward, reverse, or both domains populated.
    ///
    /// @param change_mask determines which change directions are requested
    NameRemoveStubPtr makeTransaction6(int change_mask) {
        // Creates IPv6 remove request, forward, and reverse domains.
        setupForIPv6Transaction(dhcp_ddns::CHG_REMOVE, change_mask);

        // Now create the test transaction as would occur in update manager.
        return (NameRemoveStubPtr(new NameRemoveStub(io_service_, ncr_,
                                                     forward_domain_,
                                                     reverse_domain_,
                                                     cfg_mgr_)));
    }

    /// @brief Create a test transaction at a known point in the state model.
    ///
    /// Method prepares a new test transaction and sets its state and next
    /// event values to those given.  This makes the transaction appear to
    /// be at that point in the state model without having to transition it
    /// through prerequisite states.   It also provides the ability to set
    /// which change directions are requested: forward change only, reverse
    /// change only, or both.
    ///
    /// @param state value to set as the current state
    /// @param event value to post as the next event
    /// @param change_mask determines which change directions are requested
    /// @param family selects between an IPv4 (AF_INET) and IPv6 (AF_INET6)
    /// transaction.
    NameRemoveStubPtr prepHandlerTest(unsigned int state, unsigned int event,
                                      unsigned int change_mask
                                      = FWD_AND_REV_CHG,
                                      short family = AF_INET) {
        NameRemoveStubPtr name_remove = (family == AF_INET ?
                                         makeTransaction4(change_mask) :
                                         makeTransaction6(change_mask));
        name_remove->initDictionaries();
        name_remove->postNextEvent(event);
        name_remove->setState(state);
        return (name_remove);
    }

};

/// @brief Tests NameRemoveTransaction construction.
/// This test verifies that:
/// 1. Construction with invalid type of request
/// 2. Valid construction functions properly
TEST(NameRemoveTransaction, construction) {
    asiolink::IOServicePtr io_service(new isc::asiolink::IOService());
    D2CfgMgrPtr cfg_mgr(new D2CfgMgr());

    const char* msg_str =
        "{"
        " \"change-type\" : 0 , "
        " \"forward-change\" : true , "
        " \"reverse-change\" : true , "
        " \"fqdn\" : \"example.com.\" , "
        " \"ip-address\" : \"192.168.2.1\" , "
        " \"dhcid\" : \"0102030405060708\" , "
        " \"lease-expires-on\" : \"20130121132405\" , "
        " \"lease-length\" : 1300 "
        "}";

    dhcp_ddns::NameChangeRequestPtr ncr;
    DnsServerInfoStoragePtr servers;
    DdnsDomainPtr forward_domain;
    DdnsDomainPtr reverse_domain;
    DdnsDomainPtr empty_domain;

    ASSERT_NO_THROW(ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str));
    ASSERT_NO_THROW(forward_domain.reset(new DdnsDomain("*", servers)));
    ASSERT_NO_THROW(reverse_domain.reset(new DdnsDomain("*", servers)));

    // Verify that construction with wrong change type fails.
    EXPECT_THROW(NameRemoveTransaction(io_service, ncr,
                                       forward_domain, reverse_domain, cfg_mgr),
                                       NameRemoveTransactionError);

    // Verify that a valid construction attempt works.
    ncr->setChangeType(isc::dhcp_ddns::CHG_REMOVE);
    EXPECT_NO_THROW(NameRemoveTransaction(io_service, ncr,
                                          forward_domain, reverse_domain,
                                          cfg_mgr));
}

/// @brief Tests event and state dictionary construction and verification.
TEST_F(NameRemoveTransactionTest, dictionaryCheck) {
    NameRemoveStubPtr name_remove;
    ASSERT_NO_THROW(name_remove = makeTransaction4(FWD_AND_REV_CHG));
    // Verify that the event and state dictionary validation fails prior
    // dictionary construction.
    ASSERT_THROW(name_remove->verifyEvents(), StateModelError);
    ASSERT_THROW(name_remove->verifyStates(), StateModelError);

    // Construct both dictionaries.
    ASSERT_NO_THROW(name_remove->defineEvents());
    ASSERT_NO_THROW(name_remove->defineStates());

    // Verify both event and state dictionaries now pass validation.
    ASSERT_NO_THROW(name_remove->verifyEvents());
    ASSERT_NO_THROW(name_remove->verifyStates());
}


/// @brief Tests construction of a DNS update request for removing forward
/// DNS address RRs.
TEST_F(NameRemoveTransactionTest, buildRemoveFwdAddressRequest) {
    // Create a IPv4 forward add transaction.
    // Verify the request builds without error.
    // and then verify the request contents.
    NameRemoveStubPtr name_remove;
    ASSERT_NO_THROW(name_remove = makeTransaction4(FORWARD_CHG));
    ASSERT_NO_THROW(name_remove->buildRemoveFwdAddressRequest());
    checkRemoveFwdAddressRequest(*name_remove);

    // Create a IPv6 forward add transaction.
    // Verify the request builds without error.
    // and then verify the request contents.
    ASSERT_NO_THROW(name_remove = makeTransaction6(FORWARD_CHG));
    ASSERT_NO_THROW(name_remove->buildRemoveFwdAddressRequest());
    checkRemoveFwdAddressRequest(*name_remove);
}

/// @brief Tests construction of a DNS update request for removing forward
/// dns RR entries.
TEST_F(NameRemoveTransactionTest, buildRemoveFwdRRsRequest) {
    // Create a IPv4 forward replace transaction.
    // Verify the request builds without error.
    // and then verify the request contents.
    NameRemoveStubPtr name_remove;
    ASSERT_NO_THROW(name_remove = makeTransaction4(FORWARD_CHG));
    ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest());
    checkRemoveFwdRRsRequest(*name_remove);

    // Create a IPv6 forward replace transaction.
    // Verify the request builds without error.
    // and then verify the request contents.
    ASSERT_NO_THROW(name_remove = makeTransaction6(FORWARD_CHG));
    ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest());
    checkRemoveFwdRRsRequest(*name_remove);
}

/// @brief Tests the construction of a DNS update request for removing a
/// reverse dns entry.
TEST_F(NameRemoveTransactionTest, buildRemoveRevPtrsRequest) {
    // Create a IPv4 reverse replace transaction.
    // Verify the request builds without error.
    // and then verify the request contents.
    NameRemoveStubPtr name_remove;
    ASSERT_NO_THROW(name_remove = makeTransaction4(REVERSE_CHG));
    ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest());
    checkRemoveRevPtrsRequest(*name_remove);

    // Create a IPv6 reverse replace transaction.
    // Verify the request builds without error.
    // and then verify the request contents.
    ASSERT_NO_THROW(name_remove = makeTransaction6(REVERSE_CHG));
    ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest());
    checkRemoveRevPtrsRequest(*name_remove);
}

// Tests the readyHandler functionality.
// It verifies behavior for the following scenarios:
//
// 1. Posted event is START_EVT and request includes only a forward change
// 2. Posted event is START_EVT and request includes both a forward and a
// reverse change
// 3. Posted event is START_EVT and request includes only a reverse change
// 4. Posted event is invalid
//
TEST_F(NameRemoveTransactionTest, readyHandler) {
    NameRemoveStubPtr name_remove;

    // Create a transaction which includes only a forward change.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::READY_ST,
                                    StateModel::START_EVT, FORWARD_CHG));
    // Run readyHandler.
    EXPECT_NO_THROW(name_remove->readyHandler());

    // Verify that a request requiring only a forward change, transitions to
    // selecting a forward server.
    EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
              name_remove->getNextEvent());

    // Create a transaction which includes both a forward and a reverse change.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::READY_ST,
                                    StateModel::START_EVT, FWD_AND_REV_CHG));
    // Run readyHandler.
    EXPECT_NO_THROW(name_remove->readyHandler());

    // Verify that a request requiring both forward and reverse, starts with
    // the forward change by transitioning to selecting a forward server.
    EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
              name_remove->getNextEvent());

    // Create and prep a reverse only transaction.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::READY_ST,
                                    StateModel::START_EVT, REVERSE_CHG));
    // Run readyHandler.
    EXPECT_NO_THROW(name_remove->readyHandler());

    // Verify that a request requiring only a reverse change, transitions to
    // selecting a reverse server.
    EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
              name_remove->getNextEvent());

    // Create and prep transaction, poised to run the handler but with an
    // invalid event.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::READY_ST,
                                    StateModel::NOP_EVT));

    // Running the readyHandler should throw.
    EXPECT_THROW(name_remove->readyHandler(), NameRemoveTransactionError);
}


// Tests the selectingFwdServerHandler functionality.
// It verifies behavior for the following scenarios:
//
// 1. Posted event is SELECT_SERVER_EVT
// 2. Posted event is SERVER_IO_ERROR_EVT
// 3. Posted event is invalid
//
TEST_F(NameRemoveTransactionTest, selectingFwdServerHandler) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::
                                    SELECTING_FWD_SERVER_ST,
                                    NameChangeTransaction::SELECT_SERVER_EVT));

    // Call selectingFwdServerHandler enough times to select all of the
    // servers in it's current domain.  The first time, it will be with
    // next event of SELECT_SERVER_EVT.  Thereafter it will be with a next
    // event of SERVER_IO_ERROR_EVT.
    int num_servers = name_remove->getForwardDomain()->getServers()->size();
    for (int i = 0; i < num_servers; ++i) {
        // Run selectingFwdServerHandler.
        ASSERT_NO_THROW(name_remove->selectingFwdServerHandler())
                        << " num_servers: " << num_servers
                        << " selections: " << i;

        // Verify that a server was selected.
        ASSERT_TRUE(name_remove->getCurrentServer())
                    << " num_servers: " << num_servers << " selections: " << i;

        // Verify that we transitioned correctly.
        ASSERT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
                  name_remove->getCurrState())
                  << " num_servers: " << num_servers << " selections: " << i;
        ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
                  name_remove->getNextEvent())
                  << " num_servers: " << num_servers << " selections: " << i;

        // Post a server IO error event.  This simulates an IO error occurring
        // and a need to select the new server.
        ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
                                                   SERVER_IO_ERROR_EVT))
                        << " num_servers: " << num_servers
                        << " selections: " << i;
    }

    // We should have exhausted the list of servers. Processing another
    // SERVER_IO_ERROR_EVT should transition us to failure.
    EXPECT_NO_THROW(name_remove->selectingFwdServerHandler());
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
              name_remove->getNextEvent());

    // Create and prep transaction, poised to run the handler but with an
    // invalid event.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::
                                    SELECTING_FWD_SERVER_ST,
                                    StateModel::NOP_EVT));

    // Running the handler should throw.
    EXPECT_THROW(name_remove->selectingFwdServerHandler(),
                 NameRemoveTransactionError);
}

// ************************ addingFwdAddrHandler Tests *****************

// Tests that removingFwdAddrsHandler rejects invalid events.
TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_InvalidEvent) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler but with
    // an invalid event.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    StateModel::NOP_EVT));

    // Running the handler should throw.
    EXPECT_THROW(name_remove->removingFwdAddrsHandler(),
                 NameRemoveTransactionError);
}


// Tests addingFwdAddrsHandler with the following scenario:
//
//  The request includes only a forward change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request is sent without error.
//  A server response is received which indicates successful update
//
TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_FwdOnlyOK) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, FORWARD_CHG));

    // Should not be an update message yet.
    D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
    ASSERT_FALSE(update_msg);

    // At this point completion flags should be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Run removingFwdAddrsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

    // Verify that an update message was constructed properly.
    checkRemoveFwdAddressRequest(*name_remove);

    // Verify that we are still in this state and next event is NOP_EVT.
    // This indicates we "sent" the message and are waiting for IO completion.
    EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::NOP_EVT,
              name_remove->getNextEvent());

    // Simulate receiving a successful update response.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());

    // Run removingFwdAddrsHandler again to process the response.
    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

    // Completion flags should both still be false, as we are only partly
    // done with forward updates.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Since we succeeded, we should now attempt to remove any remaining
    // forward RRs.
    // Verify that we transitioned correctly.
    EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameRemoveTransaction::UPDATE_OK_EVT,
              name_remove->getNextEvent());
}

// Tests addingFwdAddrsHandler with the following scenario:
//
//  The request includes only a forward change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request is sent without error.
//  A server response is received which indicates FQDN is not in use.
//
TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_FqdnNotInUse) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, FORWARD_CHG));

    // Run removingFwdAddrsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

    // Simulate receiving a RRSET does not exist.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXRRSET());

    // Run removingFwdAddrsHandler again to process the response.
    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

    // Completion flags should both still be false, as we are only partly
    // done with forward updates.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // There was no address RR to remove, but we will still make sure there
    // are no other RRs for this FQDN.
    // Verify that we transitioned correctly.
    EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameRemoveTransaction::UPDATE_OK_EVT,
              name_remove->getNextEvent());
}


// Tests removingFwdAddrsHandler with the following scenario:
//
//  The request includes a forward and reverse change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request is sent without error.
//  A server response is received which indicates the update was rejected.
//
TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_OtherRcode) {
    NameRemoveStubPtr name_remove;

    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT));

    // Select a server to satisfy log statements.
    ASSERT_TRUE(name_remove->selectFwdServer());

    // Run removingFwdAddrsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

    // Simulate receiving server rejection response. Per RFC, anything other
    // than no error or FQDN not in use is failure. Arbitrarily choosing
    // refused.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());

    // Run removingFwdAddrsHandler again to process the response.
    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

    // Completion flags should still be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // We should have failed the transaction. Verify that we transitioned
    // correctly.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
              name_remove->getNextEvent());
}


// Tests removingFwdAddrsHandler with the following scenario:
//
//  The request includes a forward and reverse change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
//
TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_Timeout) {
    NameRemoveStubPtr name_remove;

    // Create and prep a transaction, poised to run the handler.
    // The log message issued when this test succeeds, displays the
    // selected server, so we need to select a server before running this
    // test.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT));

    // Select a server to satisfy log statements.
    ASSERT_TRUE(name_remove->selectFwdServer());

    // Verify that we can make maximum number of update attempts permitted
    // and then transition to selecting a new server.
    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
    for (int i = 1; i <= max_tries; ++i) {
        const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();

        // Run removingFwdAddrsHandler to send the request.
        EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

        const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
        if (i == 1) {
            // First time around we should build the message.
            EXPECT_FALSE(prev_msg);
            EXPECT_TRUE(curr_msg);
        } else {
            // Subsequent passes should reuse the request. We are only
            // looking to check that we have not replaced the pointer value
            // with a new pointer.  This tests the on_entry() logic which
            // clears the request ONLY upon initial entry into the state.
            EXPECT_TRUE(prev_msg == curr_msg);
        }

        // Simulate a server IO timeout.
        name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);

        // Run removingFwdAddrsHandler again to process the response.
        EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

        // Completion flags should be false.
        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
        EXPECT_FALSE(name_remove->getReverseChangeCompleted());

        if (i < max_tries) {
            // We should be ready to try again.
            EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
                    name_remove->getNextEvent());
        } else {
            // Server retries should be exhausted, time for a new server.
            EXPECT_EQ(NameRemoveTransaction::SELECTING_FWD_SERVER_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
                    name_remove->getNextEvent());
        }
    }
}

// Tests removingFwdAddrsHandler with the following scenario:
//
//  The request includes a forward and reverse change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request is sent but a corrupt response is received, this occurs
//  MAX_UPDATE_TRIES_PER_SERVER times.
//
TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_InvalidResponse) {
    NameRemoveStubPtr name_remove;

    // Create and prep a transaction, poised to run the handler.
    // The log message issued when this test succeeds, displays the
    // selected server, so we need to select a server before running this
    // test.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT));

    // Select a server to satisfy log statements.
    ASSERT_TRUE(name_remove->selectFwdServer());

    // Verify that we can make maximum number of update attempts permitted
    // and then transition to selecting a new server.
    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
    for (int i = 1; i <= max_tries; ++i) {
        // Run removingFwdAddrsHandler to construct send the request.
        EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

        // Simulate a corrupt server response.
        name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);

        // Run removingFwdAddrsHandler again to process the response.
        EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

        // Completion flags should be false.
        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
        EXPECT_FALSE(name_remove->getReverseChangeCompleted());

        if (i < max_tries) {
            // We should be ready to try again.
            EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
                    name_remove->getNextEvent());
        } else {
            // Server retries should be exhausted, time for a new server.
            EXPECT_EQ(NameRemoveTransaction::SELECTING_FWD_SERVER_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
                    name_remove->getNextEvent());
        }
    }

}

// ************************ removingFwdRRsHandler Tests *****************

// Tests that removingFwdRRsHandler rejects invalid events.
TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_InvalidEvent) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler but with
    // an invalid event.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_RRS_ST,
                                    StateModel::NOP_EVT));

    // Running the handler should throw.
    EXPECT_THROW(name_remove->removingFwdRRsHandler(),
                 NameRemoveTransactionError);
}

// Tests removingFwdRRsHandler with the following scenario:
//
//  The request includes only a forward change.
//  Initial posted event is UPDATE_OK_EVT.
//  The update request is sent without error.
//  A server response is received which indicates successful update.
//
TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    UPDATE_OK_EVT, FORWARD_CHG));

    // Should not be an update message yet.
    D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
    ASSERT_FALSE(update_msg);

    // At this point completion flags should be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Run removingFwdRRsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Verify that an update message was constructed properly.
    checkRemoveFwdRRsRequest(*name_remove);

    // Verify that we are still in this state and next event is NOP_EVT.
    // This indicates we "sent" the message and are waiting for IO completion.
    EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::NOP_EVT,
              name_remove->getNextEvent());

    // Simulate receiving a successful update response.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());

    // Run removingFwdRRsHandler again to process the response.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Forward completion should be true, reverse should be false.
    EXPECT_TRUE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Since it is a forward only change, we should be done.
    // Verify that we transitioned correctly.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
              name_remove->getNextEvent());
}

// Tests removingFwdRRsHandler with the following scenario:
//
//  The request includes only a forward change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request is sent without error.
//  A server response is received which indicates successful update.
//
TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK2) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, FORWARD_CHG));

    // Run removingFwdRRsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Simulate receiving a successful update response.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());

    // Run removingFwdRRsHandler again to process the response.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Forward completion should be true, reverse should be false.
    EXPECT_TRUE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Since it is a forward only change, we should be done.
    // Verify that we transitioned correctly.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
              name_remove->getNextEvent());
}


// Tests removingFwdRRsHandler with the following scenario:
//
//  The request includes a forward and reverse change.
//  Initial posted event is UPDATE_OK_EVT.
//  The update request is sent without error.
//  A server response is received which indicates successful update.
//
TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdAndRevOK) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    UPDATE_OK_EVT, FWD_AND_REV_CHG));

    // Run removingFwdRRsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Simulate receiving a successful update response.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());

    // Run removingFwdRRsHandler again  to process the response.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Forward change completion should be true, reverse flag should be false.
    EXPECT_TRUE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Since the request also includes a reverse change we should
    // be poised to start it. Verify that we transitioned correctly.
    EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
              name_remove->getNextEvent());
}

// Tests removingFwdAddrsHandler with the following scenario:
//
//  The request includes a forward and reverse change.
//  Initial posted event is UPDATE_OK_EVT.
//  The update request is sent without error.
//  A server response is received which indicates the FQDN is NOT in use.
//
TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FqdnNotInUse) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    UPDATE_OK_EVT, FORWARD_CHG));

    // Run removingFwdRRsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Simulate receiving a RRSET does not exist response.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXRRSET());

    // Run removingFwdRRsHandler again to process the response.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Forward completion flag should be true, reverse should still be false.
    EXPECT_TRUE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // The FQDN is no longer in use, RFC is unclear about this,
    // but we will treat this as success.
    // Since it is a forward only change, we should be done.
    // Verify that we transitioned correctly.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
              name_remove->getNextEvent());
}

// Tests removingFwdRRsHandler with the following scenario:
//
//  The request includes a forward and reverse change.
//  The update request is sent without error.
//  A server response is received which indicates the update was rejected.
//
TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_OtherRcode) {
    NameRemoveStubPtr name_remove;
    // Create the transaction.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    UPDATE_OK_EVT, FWD_AND_REV_CHG));

    // Select a server to satisfy log statements.
    ASSERT_TRUE(name_remove->selectFwdServer());

    // Run removingFwdRRsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Simulate receiving server rejection response. Per RFC, anything other
    // than no error is failure (we are also treating FQDN not in use is
    // success). Arbitrarily choosing refused.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());

    // Run removingFwdRRsHandler again to process the response.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Completion flags should still be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // We should have failed the transaction. Verify that we transitioned
    // correctly.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
              name_remove->getNextEvent());
}

// Tests removingFwdRRsHandler with the following scenario:
//
//  The request includes a forward and reverse change.
//  Initial posted event is UPDATE_OK_EVT.
//  The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
//
TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_Timeout) {
    NameRemoveStubPtr name_remove;

    // Create the transaction.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_RRS_ST,
                                    NameChangeTransaction::
                                    UPDATE_OK_EVT, FWD_AND_REV_CHG));

    // Select a server to satisfy log statements.
    ASSERT_TRUE(name_remove->selectFwdServer());

    // Verify that we can make maximum number of update attempts permitted
    // and then transition to selecting a new server.
    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
    for (int i = 1; i <= max_tries; ++i) {
        const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();

        // Run removingFwdRRsHandler to send the request.
        EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

        const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
        if (i == 1) {
            // First time around we should build the message.
            EXPECT_FALSE(prev_msg);
            EXPECT_TRUE(curr_msg);
        } else {
            // Subsequent passes should reuse the request. We are only
            // looking to check that we have not replaced the pointer value
            // with a new pointer.  This tests the on_entry() logic which
            // clears the request ONLY upon initial entry into the state.
            EXPECT_TRUE(prev_msg == curr_msg);
        }

        // Simulate a server IO timeout.
        name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);

        // Run removingFwdRRsHandler again to process the response.
        EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

        // Completion flags should be false.
        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
        EXPECT_FALSE(name_remove->getReverseChangeCompleted());

        if (i < max_tries) {
            // We should be ready to try again.
            EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
                    name_remove->getNextEvent());
        } else {
            // Server retries should be exhausted.
            // We should abandon the transaction.
            EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
                    name_remove->getNextEvent());
        }
    }
}

// Tests removingFwdRRsHandler with the following scenario:
//
//  The request includes a forward and reverse change.
//  Initial posted event is UPDATE_OK_EVT.
//  The update request is sent but a corrupt response is received, this occurs
//  MAX_UPDATE_TRIES_PER_SERVER times.
//
TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_InvalidResponse) {
    NameRemoveStubPtr name_remove;

    // Create the transaction.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_RRS_ST,
                                    NameChangeTransaction::
                                    UPDATE_OK_EVT, FWD_AND_REV_CHG));

    // Select a server to satisfy log statements.
    ASSERT_TRUE(name_remove->selectFwdServer());

    // Verify that we can make maximum number of update attempts permitted
    // and then transition to selecting a new server.
    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
    for (int i = 1; i <= max_tries; ++i) {
        const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();

        // Run removingFwdRRsHandler to send the request.
        EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

        const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
        if (i == 1) {
            // First time around we should build the message.
            EXPECT_FALSE(prev_msg);
            EXPECT_TRUE(curr_msg);
        } else {
            // Subsequent passes should reuse the request. We are only
            // looking to check that we have not replaced the pointer value
            // with a new pointer.  This tests the on_entry() logic which
            // clears the request ONLY upon initial entry into the state.
            EXPECT_TRUE(prev_msg == curr_msg);
        }

        // Simulate a corrupt server response.
        name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);

        // Run removingFwdRRsHandler again to process the response.
        EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

        // Completion flags should be false.
        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
        EXPECT_FALSE(name_remove->getReverseChangeCompleted());

        if (i < max_tries) {
            // We should be ready to try again.
            EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
                    name_remove->getNextEvent());
        } else {
            // Server retries should be exhausted.
            // We should abandon the transaction.
            EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
                    name_remove->getNextEvent());
        }
    }
}


// Tests the selectingRevServerHandler functionality.
// It verifies behavior for the following scenarios:
//
// 1. Posted event is SELECT_SERVER_EVT
// 2. Posted event is SERVER_IO_ERROR_EVT
// 3. Posted event is invalid
//
TEST_F(NameRemoveTransactionTest, selectingRevServerHandler) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::
                                    SELECTING_REV_SERVER_ST,
                                    NameChangeTransaction::SELECT_SERVER_EVT));

    // Call selectingRevServerHandler enough times to select all of the
    // servers in it's current domain.  The first time, it will be with
    // next event of SELECT_SERVER_EVT.  Thereafter it will be with a next
    // event of SERVER_IO_ERROR_EVT.
    int num_servers = name_remove->getReverseDomain()->getServers()->size();
    for (int i = 0; i < num_servers; ++i) {
        // Run selectingRevServerHandler.
        ASSERT_NO_THROW(name_remove->selectingRevServerHandler())
                        << " num_servers: " << num_servers
                        << " selections: " << i;

        // Verify that a server was selected.
        ASSERT_TRUE(name_remove->getCurrentServer())
                    << " num_servers: " << num_servers
                    << " selections: " << i;

        // Verify that we transitioned correctly.
        ASSERT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
                  name_remove->getCurrState())
                  << " num_servers: " << num_servers << " selections: " << i;
        ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
                  name_remove->getNextEvent())
                  << " num_servers: " << num_servers << " selections: " << i;

        // Post a server IO error event.  This simulates an IO error occurring
        // and a need to select the new server.
        ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
                                                SERVER_IO_ERROR_EVT))
                        << " num_servers: " << num_servers
                        << " selections: " << i;
    }

    // We should have exhausted the list of servers. Processing another
    // SERVER_IO_ERROR_EVT should transition us to failure.
    EXPECT_NO_THROW(name_remove->selectingRevServerHandler());
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
              name_remove->getNextEvent());

    // Create and prep transaction, poised to run the handler but with an
    // invalid event.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::
                                    SELECTING_REV_SERVER_ST,
                                    StateModel::NOP_EVT));

    // Running the handler should throw.
    EXPECT_THROW(name_remove->selectingRevServerHandler(),
                 NameRemoveTransactionError);
}

//************************** removingRevPtrsHandler tests *****************

// Tests that removingRevPtrsHandler rejects invalid events.
TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_InvalidEvent) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler but with
    // an invalid event.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_REV_PTRS_ST,
                                    StateModel::NOP_EVT));

    // Running the handler should throw.
    EXPECT_THROW(name_remove->removingRevPtrsHandler(),
                 NameRemoveTransactionError);
}

// Tests removingRevPtrsHandler with the following scenario:
//
//  The request includes only a reverse change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request is sent without error.
//  A server response is received which indicates successful update.
//
TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_RevOnlyOK) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_REV_PTRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, REVERSE_CHG));

    // Should not be an update message yet.
    D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
    ASSERT_FALSE(update_msg);

    // At this point completion flags should be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Run removingRevPtrsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

    // Verify that an update message was constructed properly.
    checkRemoveRevPtrsRequest(*name_remove);

    // Verify that we are still in this state and next event is NOP_EVT.
    // This indicates we "sent" the message and are waiting for IO completion.
    EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
              name_remove->getCurrState());
    EXPECT_EQ(StateModel::NOP_EVT,
              name_remove->getNextEvent());

    // Simulate receiving a successful update response.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());

    // Run removingRevPtrsHandler again to process the response.
    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

    // Forward completion should be false, reverse should be true.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_TRUE(name_remove->getReverseChangeCompleted());

    // Since it is a reverse change, we should be done.
    // Verify that we transitioned correctly.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
              name_remove->getNextEvent());
}

// Tests removingRevPtrsHandler with the following scenario:
//
//  The request includes only a reverse change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request is sent without error.
//  A server response is received which indicates FQDN is NOT in use.
//
TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_FqdnNotInUse) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_REV_PTRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, REVERSE_CHG));

    // Should not be an update message yet.
    D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
    ASSERT_FALSE(update_msg);

    // At this point completion flags should be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Run removingRevPtrsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

    // Verify that an update message was constructed properly.
    checkRemoveRevPtrsRequest(*name_remove);

    // Verify that we are still in this state and next event is NOP_EVT.
    // This indicates we "sent" the message and are waiting for IO completion.
    EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
              name_remove->getCurrState());
    EXPECT_EQ(StateModel::NOP_EVT,
              name_remove->getNextEvent());

    // Simulate receiving a RRSET does not exist.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXRRSET());

    // Run removingRevPtrsHandler again to process the response.
    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

    // Forward completion should be false, reverse should be true.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_TRUE(name_remove->getReverseChangeCompleted());

    // Since it is a reverse change, we should be done.
    // Verify that we transitioned correctly.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
              name_remove->getNextEvent());
}

// Tests removingRevPtrsHandler with the following scenario:
//
//  The request includes only a reverse change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request is sent without error.
//  A server response is received which indicates the update was rejected.
//
TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_OtherRcode) {
    NameRemoveStubPtr name_remove;
    // Create the transaction.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_REV_PTRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, REVERSE_CHG));

    // Select a server to satisfy log statements.
    ASSERT_TRUE(name_remove->selectRevServer());

    // Run removingRevPtrsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

    // Simulate receiving server rejection response. Per RFC, anything other
    // than no error is failure.  Arbitrarily choosing refused.
    name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());

    // Run removingRevPtrsHandler again to process the response.
    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

    // Completion flags should still be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // We should have failed the transaction. Verify that we transitioned
    // correctly.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
              name_remove->getNextEvent());
}

// Tests removingRevPtrsHandler with the following scenario:
//
//  The request includes only a reverse change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
//
TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_Timeout) {
    NameRemoveStubPtr name_remove;
    // Create the transaction.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_REV_PTRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, REVERSE_CHG));

    // Select a server to satisfy log statements.
    ASSERT_TRUE(name_remove->selectRevServer());

    // Verify that we can make maximum number of update attempts permitted
    // and then transition to selecting a new server.
    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
    for (int i = 1; i <= max_tries; ++i) {
        const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();

        // Run removingRevPtrsHandler to send the request.
        EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

        const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
        if (i == 1) {
            // First time around we should build the message.
            EXPECT_FALSE(prev_msg);
            EXPECT_TRUE(curr_msg);
        } else {
            // Subsequent passes should reuse the request. We are only
            // looking to check that we have not replaced the pointer value
            // with a new pointer.  This tests the on_entry() logic which
            // clears the request ONLY upon initial entry into the state.
            EXPECT_TRUE(prev_msg == curr_msg);
        }

        // Simulate a server IO timeout.
        name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);

        // Run removingRevPtrsHandler again to process the response.
        EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

        // Completion flags should be false.
        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
        EXPECT_FALSE(name_remove->getReverseChangeCompleted());

        if (i < max_tries) {
            // We should be ready to try again.
            EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
                      name_remove->getNextEvent());
        } else {
            // Server retries should be exhausted, time for a new server.
            EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
                      name_remove->getNextEvent());
        }
    }
}


// Tests removingRevPtrsHandler with the following scenario:
//
//  The request includes only a reverse change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The update request is sent but a corrupt response is received, this occurs
//  MAX_UPDATE_TRIES_PER_SERVER times.
//
TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_CorruptResponse) {
    NameRemoveStubPtr name_remove;
    // Create the transaction.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_REV_PTRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, REVERSE_CHG));

    // Select a server to satisfy log statements.
    ASSERT_TRUE(name_remove->selectRevServer());

    // Verify that we can make maximum number of update attempts permitted
    // and then transition to selecting a new server.
    int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
    for (int i = 1; i <= max_tries; ++i) {
        const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();

        // Run removingRevPtrsHandler to send the request.
        EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

        const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
        if (i == 1) {
            // First time around we should build the message.
            EXPECT_FALSE(prev_msg);
            EXPECT_TRUE(curr_msg);
        } else {
            // Subsequent passes should reuse the request. We are only
            // looking to check that we have not replaced the pointer value
            // with a new pointer.  This tests the on_entry() logic which
            // clears the request ONLY upon initial entry into the state.
            EXPECT_TRUE(prev_msg == curr_msg);
        }

        // Simulate a server corrupt response.
        name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
        name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);

        // Run removingRevPtrsHandler again to process the response.
        EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

        // Completion flags should be false.
        EXPECT_FALSE(name_remove->getForwardChangeCompleted());
        EXPECT_FALSE(name_remove->getReverseChangeCompleted());

        if (i < max_tries) {
            // We should be ready to try again.
            EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
                      name_remove->getNextEvent());
        } else {
            // Server retries should be exhausted, time for a new server.
            EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
                      name_remove->getCurrState());
            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
                      name_remove->getNextEvent());
        }
    }
}

// Tests the processRemoveOkHandler functionality.
// It verifies behavior for the following scenarios:
//
// 1. Posted event is UPDATE_OK_EVT
// 2. Posted event is invalid
//
TEST_F(NameRemoveTransactionTest, processRemoveOkHandler) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
                                    NameChangeTransaction::UPDATE_OK_EVT));
    // Run processRemoveOkHandler.
    EXPECT_NO_THROW(name_remove->processRemoveOkHandler());

    // Verify that a server was selected.
    EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_remove->getNcrStatus());

    // Verify that the model has ended.
    EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
    EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());


    // Create and prep transaction, poised to run the handler but with an
    // invalid event.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
                                    StateModel::NOP_EVT));
    // Running the handler should throw.
    EXPECT_THROW(name_remove->processRemoveOkHandler(),
                 NameRemoveTransactionError);
}

// Tests the processRemoveFailedHandler functionality.
// It verifies behavior for the following scenarios:
//
// 1. Posted event is UPDATE_FAILED_EVT
// 2. Posted event is invalid
//
TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::
                                    PROCESS_TRANS_FAILED_ST,
                                    NameChangeTransaction::UPDATE_FAILED_EVT));
    // Run processRemoveFailedHandler.
    EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());

    // Verify that a server was selected.
    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());

    // Verify that the model has ended. (Remember, the transaction failed NOT
    // the model.  The model should have ended normally.)
    EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
    EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());


    // Create and prep transaction, poised to run the handler but with an
    // invalid event.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::
                                    PROCESS_TRANS_FAILED_ST,
                                    StateModel::NOP_EVT));
    // Running the handler should throw.
    EXPECT_THROW(name_remove->processRemoveFailedHandler(),
                 NameRemoveTransactionError);
}

// Tests the processRemoveFailedHandler functionality.
// It verifies behavior for posted event of NO_MORE_SERVERS_EVT.
TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler_NoMoreServers) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::
                                    PROCESS_TRANS_FAILED_ST,
                                    NameChangeTransaction::
                                    NO_MORE_SERVERS_EVT));

    // Run processRemoveFailedHandler.
    EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());

    // Verify that a server was selected.
    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());

    // Verify that the model has ended. (Remember, the transaction failed NOT
    // the model.  The model should have ended normally.)
    EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
    EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
}

// Tests the processRemoveFailedHandler functionality.
// It verifies behavior for posted event of SERVER_IO_ERROR_EVT.
TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler_ServerIOError) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameChangeTransaction::
                                    PROCESS_TRANS_FAILED_ST,
                                    NameChangeTransaction::
                                    SERVER_IO_ERROR_EVT));

    // Run processRemoveFailedHandler.
    EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());

    // Verify that a server was selected.
    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());

    // Verify that the model has ended. (Remember, the transaction failed NOT
    // the model.  The model should have ended normally.)
    EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
    EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
}

// Tests removingFwdAddrsHandler with the following scenario:
//
//  The request includes only a forward change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The send update request fails due to an unexpected exception.
//
TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_sendUpdateException) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, FORWARD_CHG));

    name_remove->simulate_send_exception_ = true;

    // Run removingFwdAddrsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());

    // Completion flags should be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Since IO exceptions should be gracefully handled, any that occur
    // are unanticipated, and deemed unrecoverable, so the transaction should
    // be transitioned to failure.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
              name_remove->getNextEvent());
}


// Tests removingFwdRRsHandler with the following scenario:
//
//  The request includes only a forward change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The send update request fails due to an unexpected exception.
//
TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_SendUpdateException) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_RRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, FORWARD_CHG));

    name_remove->simulate_send_exception_ = true;

    // Run removingFwdRRsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Completion flags should be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Since IO exceptions should be gracefully handled, any that occur
    // are unanticipated, and deemed unrecoverable, so the transaction should
    // be transitioned to failure.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
              name_remove->getNextEvent());
}

// Tests removingRevPtrHandler with the following scenario:
//
//  The request includes only a reverse change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The send update request fails due to an unexpected exception.
//
TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_SendUpdateException) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_REV_PTRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, REVERSE_CHG));

    name_remove->simulate_send_exception_ = true;

    // Run removingRevPtrsHandler to construct and send the request.
    EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());

    // Completion flags should be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Since IO exceptions should be gracefully handled, any that occur
    // are unanticipated, and deemed unrecoverable, so the transaction should
    // be transitioned to failure.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
              name_remove->getNextEvent());
}

// Tests removingFwdAddrsHandler with the following scenario:
//
//  The request includes only a forward change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The request build fails due to an unexpected exception.
//
TEST_F(NameRemoveTransactionTest,
       removingFwdAddrsHandler_BuildRequestException) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_ADDRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, FORWARD_CHG));

    // Set the one-shot exception simulation flag.
    name_remove->simulate_build_request_exception_ = true;

    // Run removingFwdAddrsHandler to construct and send the request.
    // This should fail with a build request throw which should be caught
    // in the state handler.
    ASSERT_NO_THROW(name_remove->removingFwdAddrsHandler());

    // Verify we did not attempt to send anything.
    EXPECT_EQ(0, name_remove->getUpdateAttempts());

    // Completion flags should be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Since IO exceptions should be gracefully handled, any that occur
    // are unanticipated, and deemed unrecoverable, so the transaction should
    // be transitioned to failure.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
              name_remove->getNextEvent());
}

// Tests removingFwdRRsHandler with the following scenario:
//
//  The request includes only a forward change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The request build fails due to an unexpected exception.
//
TEST_F(NameRemoveTransactionTest,
       removingFwdRRsHandler_BuildRequestException) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_FWD_RRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, FORWARD_CHG));

    // Set the one-shot exception simulation flag.
    name_remove->simulate_build_request_exception_ = true;

    // Run removingFwdRRsHandler to construct and send the request.
    // This should fail with a build request throw which should be caught
    // in the state handler.
    ASSERT_NO_THROW(name_remove->removingFwdRRsHandler());

    // Verify we did not attempt to send anything.
    EXPECT_EQ(0, name_remove->getUpdateAttempts());

    // Completion flags should be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Since IO exceptions should be gracefully handled, any that occur
    // are unanticipated, and deemed unrecoverable, so the transaction should
    // be transitioned to failure.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
              name_remove->getNextEvent());
}

// Tests removingRevPTRsHandler with the following scenario:
//
//  The request includes only a forward change.
//  Initial posted event is SERVER_SELECTED_EVT.
//  The request build fails due to an unexpected exception.
//
TEST_F(NameRemoveTransactionTest,
       removingRevPTRsHandler_BuildRequestException) {
    NameRemoveStubPtr name_remove;
    // Create and prep a transaction, poised to run the handler.
    ASSERT_NO_THROW(name_remove =
                    prepHandlerTest(NameRemoveTransaction::
                                    REMOVING_REV_PTRS_ST,
                                    NameChangeTransaction::
                                    SERVER_SELECTED_EVT, FORWARD_CHG));

    // Set the one-shot exception simulation flag.
    name_remove->simulate_build_request_exception_ = true;

    // Run removingRevPtrsHandler to construct and send the request.
    // This should fail with a build request throw which should be caught
    // in the state handler.
    ASSERT_NO_THROW(name_remove->removingRevPtrsHandler());

    // Verify we did not attempt to send anything.
    EXPECT_EQ(0, name_remove->getUpdateAttempts());

    // Completion flags should be false.
    EXPECT_FALSE(name_remove->getForwardChangeCompleted());
    EXPECT_FALSE(name_remove->getReverseChangeCompleted());

    // Since IO exceptions should be gracefully handled, any that occur
    // are unanticipated, and deemed unrecoverable, so the transaction should
    // be transitioned to failure.
    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_remove->getCurrState());
    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
              name_remove->getNextEvent());
}

}
