BIND 10 trac3087, updated. e6df9bca72feedd92ec08dc43b53dfec632f228c [3087] Added state handler unit tests for D2::NameAddTransaction

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Nov 13 21:22:02 UTC 2013


The branch, trac3087 has been updated
       via  e6df9bca72feedd92ec08dc43b53dfec632f228c (commit)
      from  4c16336d81f8af698f6791c3f2a637106211e72d (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit e6df9bca72feedd92ec08dc43b53dfec632f228c
Author: Thomas Markwalder <tmark at isc.org>
Date:   Wed Nov 13 16:19:12 2013 -0500

    [3087] Added state handler unit tests for D2::NameAddTransaction
    
    Created unit tests for all of the states implemented by NameAddTransaction
    in b10-dhcp-ddns.  The class lacks only proper construction of DNS update
    requests.

-----------------------------------------------------------------------

Summary of changes:
 src/bin/d2/d2_messages.mes           |    6 +-
 src/bin/d2/nc_add.cc                 |   21 +-
 src/bin/d2/nc_trans.h                |    2 +-
 src/bin/d2/state_model.cc            |   43 +-
 src/bin/d2/state_model.h             |   16 +-
 src/bin/d2/tests/nc_add_unittests.cc | 1237 +++++++++++++++++++++++++++++++++-
 6 files changed, 1279 insertions(+), 46 deletions(-)

-----------------------------------------------------------------------
diff --git a/src/bin/d2/d2_messages.mes b/src/bin/d2/d2_messages.mes
index 1f261c6..211e1eb 100644
--- a/src/bin/d2/d2_messages.mes
+++ b/src/bin/d2/d2_messages.mes
@@ -259,7 +259,7 @@ NameChangeRequest correctly. Some or all of the DNS updates requested as part
 of this update did not succeed. This is a programmatic error and should be
 reported.
 
-% DHCP_DDNS_FORWARD_ADD_REJECTED DNS Server, %1, rejected a DNS update request to add the address mapping for FQDN, %1, with an RCODE: %3
+% DHCP_DDNS_FORWARD_ADD_REJECTED DNS Server, %1, rejected a DNS update request to add the address mapping for FQDN, %2, with an RCODE: %3
 This is an error message issued when an udpate was rejected by the DNS server itwas sent to for the reason given by the RCODE. The rcode values are defined in
 RFC 2136.
 
@@ -277,7 +277,7 @@ This is an error message issued when a unexpected error condition error occurs
 while DHCP_DDNS is carrying out a forward address update.  The request will be
 be aborted.  This is most likely a programmatic issue and should be reported.
 
-% DHCP_DDNS_FORWARD_REPLACE_REJECTED DNS Server, %1, rejected a DNS update request to replace the address mapping for FQDN, %1, with an RCODE: %3
+% DHCP_DDNS_FORWARD_REPLACE_REJECTED DNS Server, %1, rejected a DNS update request to replace the address mapping for FQDN, %2, with an RCODE: %3
 This is an error message issued when an udpate was rejected by the DNS server itwas sent to for the reason given by the RCODE. The rcode values are defined in
 RFC 2136.
 
@@ -295,7 +295,7 @@ This is an error message issued when a unexpected error condition error occurs
 while DHCP_DDNS is carrying out a forward address update.  The request will be
 be aborted.  This is most likely a programmatic issue and should be reported.
 
-% DHCP_DDNS_REVERSE_REPLACE_REJECTED DNS Server, %1, rejected a DNS update request to replace the reverse mapping for FQDN, %1, with an RCODE: %3
+% DHCP_DDNS_REVERSE_REPLACE_REJECTED DNS Server, %1, rejected a DNS update request to replace the reverse mapping for FQDN, %2, with an RCODE: %3
 This is an error message issued when an udpate was rejected by the DNS server itwas sent to for the reason given by the RCODE. The rcode values are defined in
 RFC 2136.
 
diff --git a/src/bin/d2/nc_add.cc b/src/bin/d2/nc_add.cc
index 38a4d33..4f2a3a5 100644
--- a/src/bin/d2/nc_add.cc
+++ b/src/bin/d2/nc_add.cc
@@ -201,8 +201,8 @@ NameAddTransaction::addingFwdAddrsHandler() {
                 // If we get not authorized should we try the next server in
                 // the list? @todo  This needs some discussion perhaps.
                 LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_REJECTED)
-                          .arg(getNcr()->getFqdn())
                           .arg(getCurrentServer()->getIpAddress())
+                          .arg(getNcr()->getFqdn())
                           .arg(rcode.getCode());
                 transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
             }
@@ -301,8 +301,8 @@ NameAddTransaction::replacingFwdAddrsHandler() {
                 // If we get not authorized should try the next server in
                 // the list? @todo  This needs some discussion perhaps.
                 LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REPLACE_REJECTED)
-                          .arg(getNcr()->getFqdn())
                           .arg(getCurrentServer()->getIpAddress())
+                          .arg(getNcr()->getFqdn())
                           .arg(rcode.getCode());
                 transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
             }
@@ -422,15 +422,12 @@ NameAddTransaction::replacingRevPtrsHandler() {
                 // If we get not authorized should try the next server in
                 // the list? @todo  This needs some discussion perhaps.
                 LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REPLACE_REJECTED)
-                          .arg(getNcr()->getFqdn())
                           .arg(getCurrentServer()->getIpAddress())
+                          .arg(getNcr()->getFqdn())
                           .arg(rcode.getCode());
                 transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
             }
 
-            // @todo clearing request seems clunky here
-            // safest thing to do is to always rebuild it
-            clearDnsUpdateRequest();
             break;
         }
 
@@ -514,17 +511,23 @@ NameAddTransaction::processAddFailedHandler() {
 
 void
 NameAddTransaction::buildAddFwdAddressRequest() {
-    isc_throw(NotImplemented, "buildAddFwdAddressRequest");
+    // @todo For now construct a blank outbound message.
+    D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND));
+    setDnsUpdateRequest(msg);
 }
 
 void
 NameAddTransaction::buildReplaceFwdAddressRequest() {
-    isc_throw(NotImplemented, "buildReplaceFwdAddressRequest");
+    // @todo For now construct a blank outbound message.
+    D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND));
+    setDnsUpdateRequest(msg);
 }
 
 void
 NameAddTransaction::buildReplaceRevPtrsRequest() {
-    isc_throw(NotImplemented, "buildReplaceRevPtrsRequest");
+    // @todo For now construct a blank outbound message.
+    D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND));
+    setDnsUpdateRequest(msg);
 }
 
 } // namespace isc::d2
diff --git a/src/bin/d2/nc_trans.h b/src/bin/d2/nc_trans.h
index d4d22e5..fc19569 100644
--- a/src/bin/d2/nc_trans.h
+++ b/src/bin/d2/nc_trans.h
@@ -198,7 +198,7 @@ public:
 
 protected:
     /// @todo
-    void sendUpdate(bool use_tsig_ = false);
+    virtual void sendUpdate(bool use_tsig_ = false);
 
     /// @brief Adds events defined by NameChangeTransaction to the event set.
     ///
diff --git a/src/bin/d2/state_model.cc b/src/bin/d2/state_model.cc
index 6786e43..c5a0209 100644
--- a/src/bin/d2/state_model.cc
+++ b/src/bin/d2/state_model.cc
@@ -92,27 +92,13 @@ StateModel::~StateModel(){
 
 void
 StateModel::startModel(const int start_state) {
-    // First let's build and verify the dictionary of events.
-    try {
-        defineEvents();
-        verifyEvents();
-    } catch (const std::exception& ex) {
-        isc_throw(StateModelError, "Event set is invalid: " << ex.what());
-    }
-
-    // Next let's build and verify the dictionary of states.
-    try {
-        defineStates();
-        verifyStates();
-    } catch (const std::exception& ex) {
-        isc_throw(StateModelError, "State set is invalid: " << ex.what());
-    }
-
-    // Record that we are good to go.
-    dictionaries_initted_ = true;
+    // Intialize dictionaries of events and states.
+    initDictionaries();
 
     // Set the current state to starting state and enter the run loop.
     setState(start_state);
+
+    // Start running the model.
     runModel(START_EVT);
 }
 
@@ -149,6 +135,27 @@ void
 StateModel::nopStateHandler() {
 }
 
+void
+StateModel::initDictionaries() {
+    // First let's build and verify the dictionary of events.
+    try {
+        defineEvents();
+        verifyEvents();
+    } catch (const std::exception& ex) {
+        isc_throw(StateModelError, "Event set is invalid: " << ex.what());
+    }
+
+    // Next let's build and verify the dictionary of states.
+    try {
+        defineStates();
+        verifyStates();
+    } catch (const std::exception& ex) {
+        isc_throw(StateModelError, "State set is invalid: " << ex.what());
+    }
+
+    // Record that we are good to go.
+    dictionaries_initted_ = true;
+}
 
 void
 StateModel::defineEvent(unsigned int event_value, const std::string& label) {
diff --git a/src/bin/d2/state_model.h b/src/bin/d2/state_model.h
index 9a7a1c9..b7320eb 100644
--- a/src/bin/d2/state_model.h
+++ b/src/bin/d2/state_model.h
@@ -275,10 +275,9 @@ public:
 
     /// @brief Begins execution of the model.
     ///
-    /// This method invokes the define and verify methods for both events and
-    /// states to initialize their respective dictionaries. It then starts
-    /// the model execution setting the current state to the given start state,
-    /// and the event to START_EVT.
+    /// This method invokes initDictionaries method to initialize the event
+    /// and state dictionaries and then starts the model execution setting 
+    /// the current state to the given start state, and the event to START_EVT.
     ///
     /// @param start_state is the state in which to begin execution.
     ///
@@ -323,6 +322,15 @@ public:
     void nopStateHandler();
 
 protected:
+    /// @brief Initializes the event and state dictionaries.
+    ///
+    /// This method invokes the define and verify methods for both events and
+    /// states to initialize their respective dictionaries. 
+    ///
+    /// @throw StateModelError or others indirectly, as this method calls
+    /// dictionary define and verify methods.
+    void initDictionaries();
+
     /// @brief Populates the set of events.
     ///
     /// This method is used to construct the set of valid events. Each class
diff --git a/src/bin/d2/tests/nc_add_unittests.cc b/src/bin/d2/tests/nc_add_unittests.cc
index 572a13a..df42a45 100644
--- a/src/bin/d2/tests/nc_add_unittests.cc
+++ b/src/bin/d2/tests/nc_add_unittests.cc
@@ -37,11 +37,61 @@ public:
 
     virtual ~NameAddStub() {
     }
+    
+    /// @brief Simulates sending update requests to the DNS server
+    /// Allows state handlers which conduct IO to be tested without a server.
+    virtual void sendUpdate(bool /* use_tsig_ = false */) {
+        setUpdateAttempts(getUpdateAttempts() + 1);
+        postNextEvent(StateModel::NOP_EVT);
+    }
+
+    void fakeResponse(const DNSClient::Status& status, 
+                      const dns::Rcode& rcode) {
+        D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND));
+        setDnsUpdateStatus(status);
+        msg->setRcode(rcode);
+        setDnsUpdateResponse(msg);
+        postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+    }
+
+    bool selectFwdServer() {
+        if (getForwardDomain()) {
+            initServerSelection(getForwardDomain());
+            selectNextServer();
+            return (getCurrentServer());
+        }
+
+        return (false);
+    }
+
+    bool selectRevServer() {
+        if (getReverseDomain()) {
+            initServerSelection(getReverseDomain());
+            selectNextServer();
+            return (getCurrentServer());
+        }
+
+        return (false);
+    }
+
 
+    using StateModel::postNextEvent;
+    using StateModel::setState;
+    using StateModel::initDictionaries;
     using NameAddTransaction::defineEvents;
     using NameAddTransaction::verifyEvents;
     using NameAddTransaction::defineStates;
     using NameAddTransaction::verifyStates;
+    using NameAddTransaction::readyHandler;
+    using NameAddTransaction::selectingFwdServerHandler;
+    using NameAddTransaction::getCurrentServer;
+    using NameAddTransaction::addingFwdAddrsHandler;
+    using NameAddTransaction::setDnsUpdateStatus;
+    using NameAddTransaction::replacingFwdAddrsHandler;
+    using NameAddTransaction::selectingRevServerHandler;
+    using NameAddTransaction::replacingRevPtrsHandler;
+    using NameAddTransaction::processAddOkHandler;
+    using NameAddTransaction::processAddFailedHandler;
 };
 
 typedef boost::shared_ptr<NameAddStub> NameAddStubPtr;
@@ -59,12 +109,16 @@ public:
     NameAddTransactionTest() : io_service_(new isc::asiolink::IOService()) {
     }
 
+    static const unsigned int FORWARD_CHG = 0x01;
+    static const unsigned int REVERSE_CHG = 0x02;
+    static const unsigned int FWD_AND_REV_CHG = REVERSE_CHG | FORWARD_CHG;
+
     virtual ~NameAddTransactionTest() {
     }
 
     /// @brief Instantiates a NameAddTransaction built around a canned
     /// NameChangeRequest.
-    NameAddStubPtr makeCannedTransaction() {
+    NameAddStubPtr makeCannedTransaction(int change_mask=FWD_AND_REV_CHG) {
         const char* msg_str =
             "{"
             " \"change_type\" : 0 , "
@@ -84,21 +138,40 @@ public:
 
         ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str);
 
-        // make forward server list
-        server.reset(new DnsServerInfo("forward.example.com",
+        if (!(change_mask & FORWARD_CHG)) {
+            ncr->setForwardChange(false);
+            forward_domain_.reset();
+        } else {
+            // make forward server list
+            server.reset(new DnsServerInfo("forward.example.com",
                                        isc::asiolink::IOAddress("1.1.1.1")));
-        servers->push_back(server);
-        forward_domain_.reset(new DdnsDomain("*", "", servers));
+            servers->push_back(server);
+            forward_domain_.reset(new DdnsDomain("*", "", servers));
+        }
 
-        // make reverse server list
-        servers->clear();
-        server.reset(new DnsServerInfo("reverse.example.com",
+        if (!(change_mask & REVERSE_CHG)) {
+            ncr->setReverseChange(false);
+            reverse_domain_.reset();
+        } else {
+            // make reverse server list
+            servers->clear();
+            server.reset(new DnsServerInfo("reverse.example.com",
                                        isc::asiolink::IOAddress("2.2.2.2")));
-        servers->push_back(server);
-        reverse_domain_.reset(new DdnsDomain("*", "", servers));
+            servers->push_back(server);
+            reverse_domain_.reset(new DdnsDomain("*", "", servers));
+        }
+
         return (NameAddStubPtr(new NameAddStub(io_service_, ncr,
                                       forward_domain_, reverse_domain_)));
+    }
 
+    NameAddStubPtr prepHandlerTest(unsigned int state, unsigned int event,
+                                   unsigned int change_mask = FWD_AND_REV_CHG) {
+        NameAddStubPtr name_add = makeCannedTransaction(change_mask);
+        name_add->initDictionaries();
+        name_add->postNextEvent(event);
+        name_add->setState(state);
+        return (name_add);
     }
 
 };
@@ -147,7 +220,6 @@ TEST(NameAddTransaction, construction) {
 TEST_F(NameAddTransactionTest, dictionaryCheck) {
     NameAddStubPtr name_add;
     ASSERT_NO_THROW(name_add = makeCannedTransaction());
-
     // Verify that the event and state dictionary validation fails prior
     // dictionary construction.
     ASSERT_THROW(name_add->verifyEvents(), StateModelError);
@@ -162,4 +234,1147 @@ TEST_F(NameAddTransactionTest, dictionaryCheck) {
     ASSERT_NO_THROW(name_add->verifyStates());
 }
 
+// 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 
+// 3. Posted event is invalid
+//
+TEST_F(NameAddTransactionTest, readyHandler) {
+    NameAddStubPtr name_add;
+
+    // Create a transaction which includes only a forward change.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::READY_ST, 
+                                    StateModel::START_EVT, FORWARD_CHG));
+    // Run readyHandler. 
+    EXPECT_NO_THROW(name_add->readyHandler());
+
+    // Verify that a request requiring only a forward change, transitions to 
+    // selecting a forward server.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, 
+              name_add->getNextEvent());
+
+
+    // Create a transaction which includes both a forward and a reverse change.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::READY_ST, 
+                                    StateModel::START_EVT, FORWARD_CHG));
+    // Run readyHandler. 
+    EXPECT_NO_THROW(name_add->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_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, 
+              name_add->getNextEvent());
+
+
+    // Create and prep a reverse only transaction.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::READY_ST, 
+                                    StateModel::START_EVT, REVERSE_CHG));
+    // Run readyHandler. 
+    EXPECT_NO_THROW(name_add->readyHandler());
+
+    // Verify that a request requiring only a reverse change, transitions to 
+    // selecting a reverse server.
+    EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, 
+              name_add->getNextEvent());
+
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::READY_ST, 
+                                    StateModel::NOP_EVT));
+
+    // Running the readyHandler should throw. 
+    EXPECT_THROW(name_add->readyHandler(), NameAddTransactionError);
+}
+
+// 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(NameAddTransactionTest, selectingFwdServerHandler) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_FWD_SERVER_ST, 
+                                    NameChangeTransaction::SELECT_SERVER_EVT));
+    
+    // Run selectingFwdServerHandler. 
+    EXPECT_NO_THROW(name_add->selectingFwdServerHandler());
+
+    // Verify that a server was selected. 
+    EXPECT_TRUE(name_add->getCurrentServer());
+
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, 
+              name_add->getNextEvent());
+
+    // Post a server IO error event.  This simulates an IO error occuring
+    // and a need to select the a new server.
+    ASSERT_NO_THROW(name_add->postNextEvent(NameChangeTransaction::
+                                            SERVER_IO_ERROR_EVT));
+
+    // Run selectingFwdServerHandler. 
+    EXPECT_NO_THROW(name_add->selectingFwdServerHandler());
+
+    // Test domain only has 1 server, so we should have exhausted server
+    // list. Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT, 
+              name_add->getNextEvent());
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_FWD_SERVER_ST, 
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw. 
+    EXPECT_THROW(name_add->selectingFwdServerHandler(), 
+                 NameAddTransactionError);
+}
+
+// ************************ addingFwdAddrHandler Tests *****************
+
+// Tests that addingFwdAddrsHandler rejects invalid events.
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_invalid_event) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler but with
+    // an invalid event.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+                                    NameChangeTransaction::
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw. 
+    EXPECT_THROW(name_add->addingFwdAddrsHandler(), 
+                 NameAddTransactionError);
+}
+
+// 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(NameAddTransactionTest, addingFwdAddrsHandler_fwd_only_add_Ok) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+  
+    // Should not be an update message yet. 
+    D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest();
+    EXPECT_TRUE(!update_msg);
+
+    // At this point completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Run addingFwdAddrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Verify that an update message was constructed.
+    update_msg = name_add->getDnsUpdateRequest();
+    EXPECT_TRUE(update_msg);
+
+    // 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(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NOP_EVT, 
+              name_add->getNextEvent());
+
+    // Simulate receiving a succussful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run addingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Forward completion should be true, reverse should be false.
+    EXPECT_TRUE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->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_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, 
+              name_add->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler 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 successful update.
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_fwd_and_rev_add_Ok) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FWD_AND_REV_CHG));
+  
+    // Run addingFwdAddrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Simulate receiving a succussful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run addingFwdAddrsHandler again  to process the response.
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Forward change completion should be true, reverse flag should be false.
+    EXPECT_TRUE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->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_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, 
+              name_add->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler 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 FQDN is in use.
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_fqdn_in_use) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+  
+    // Run addingFwdAddrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Simulate receiving a FQDN in use response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::YXDOMAIN());
+
+    // Run addingFwdAddrsHandler again to process the response. 
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since the FQDN is in use, per the RFC we must attempt to replace it.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameAddTransaction::FQDN_IN_USE_EVT, 
+              name_add->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler 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(NameAddTransactionTest, addingFwdAddrsHandler_other_rcode) {
+    NameAddStubPtr name_add;
+
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectFwdServer());
+
+    // Run addingFwdAddrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Simulate receiving server rejection response. Per RFC, anything other
+    // than no error or FQDN in use is failure.  Arbitrarily choosing refused.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+    // Run addingFwdAddrsHandler again to process the response. 
+    EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // We should have failed the transaction. Verifiy that we transitioned 
+    // correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, 
+              name_add->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler 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(NameAddTransactionTest, addingFwdAddrsHandler_time_out) {
+    NameAddStubPtr name_add;
+
+    // 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_add = 
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->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_add->getDnsUpdateRequest();
+
+        // Run addingFwdAddrsHandler to send the request. 
+        EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time out we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server IO timeout. 
+        name_add->setDnsUpdateStatus(DNSClient::TIMEOUT);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run addingFwdAddrsHandler again to process the response. 
+        EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, 
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, 
+                    name_add->getNextEvent());
+        }
+    }
+}
+
+// Tests addingFwdAddrsHandler 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(NameAddTransactionTest, addingFwdAddrsHandler_invalid_response) {
+    NameAddStubPtr name_add;
+
+    // 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_add = 
+                    prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->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 addingFwdAddrsHandler to construct send the request. 
+        EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+        // Simulate a server IO timeout. 
+        name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run addingFwdAddrsHandler again to process the response. 
+        EXPECT_NO_THROW(name_add->addingFwdAddrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, 
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, 
+                    name_add->getNextEvent());
+        }
+    }
+
+}
+
+// ************************ replacingFwdAddrHandler Tests *****************
+
+// Tests that replacingFwdAddrsHandler rejects invalid events.
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_invalid_event) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler but with
+    // an invalid event.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+                                    NameChangeTransaction::
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw. 
+    EXPECT_THROW(name_add->replacingFwdAddrsHandler(), 
+                 NameAddTransactionError);
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario: 
+//
+//  The request includes only a forward change.
+//  Initial posted event is FQDN_IN_USE_EVT.  
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_fwd_only_add_Ok) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FORWARD_CHG));
+  
+    // Should not be an update message yet. 
+    D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest();
+    EXPECT_TRUE(!update_msg);
+
+    // At this point completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Run replacingFwdAddrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Verify that an update message was constructed.
+    update_msg = name_add->getDnsUpdateRequest();
+    EXPECT_TRUE(update_msg);
+
+    // 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(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NOP_EVT, 
+              name_add->getNextEvent());
+
+    // Simulate receiving a succussful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run replacingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Forward completion should be true, reverse should be false.
+    EXPECT_TRUE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->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_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, 
+              name_add->getNextEvent());
+}
+
+// Tests replacingFwdAddrsHandler 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(NameAddTransactionTest, replacingFwdAddrsHandler_fwd_only_add_Ok_2) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+                                    NameChangeTransaction::
+                                    SERVER_SELECTED_EVT, FORWARD_CHG));
+  
+    // Run replacingFwdAddrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Simulate receiving a succussful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run replacingFwdAddrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Forward completion should be true, reverse should be false.
+    EXPECT_TRUE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->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_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, 
+              name_add->getNextEvent());
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario: 
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is FQDN_IN_USE_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates successful update.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_fwd_and_rev_add_Ok) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FWD_AND_REV_CHG));
+  
+    // Run replacingFwdAddrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Simulate receiving a succussful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run replacingFwdAddrsHandler again  to process the response.
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Forward change completion should be true, reverse flag should be false.
+    EXPECT_TRUE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->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_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, 
+              name_add->getNextEvent());
+}
+
+
+// Tests addingFwdAddrsHandler with the following scenario: 
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is FQDN_IN_USE_EVT.
+//  The update request is sent without error.
+//  A server response is received which indicates the FQDN is NOT in use.
+//
+TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_fqdn_not_in_use) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FWD_AND_REV_CHG));
+  
+    // Run replacingFwdAddrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Simulate receiving a FQDN not in use response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+    // Run replacingFwdAddrsHandler again to process the response. 
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Since the FQDN is no longer in use, per the RFC, try to add it.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, 
+              name_add->getNextEvent());
+}
+
+
+// Tests replacingFwdAddrsHandler 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(NameAddTransactionTest, replacingFwdAddrsHandler_other_rcode) {
+    NameAddStubPtr name_add;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FWD_AND_REV_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectFwdServer());
+
+    // Run replacingFwdAddrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Simulate receiving server rejection response. Per RFC, anything other
+    // than no error or FQDN in use is failure.  Arbitrarily choosing refused.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+    // Run replacingFwdAddrsHandler again to process the response. 
+    EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // We should have failed the transaction. Verifiy that we transitioned 
+    // correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, 
+              name_add->getNextEvent());
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario: 
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is FQDN_IN_USE_EVT.
+//  The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_time_out) {
+    NameAddStubPtr name_add;
+
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FWD_AND_REV_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->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_add->getDnsUpdateRequest();
+
+        // Run replacingFwdAddrsHandler to send the request. 
+        EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time out we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server IO timeout. 
+        name_add->setDnsUpdateStatus(DNSClient::TIMEOUT);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run replacingFwdAddrsHandler again to process the response. 
+        EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, 
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, 
+                    name_add->getNextEvent());
+        }
+    }
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario: 
+//
+//  The request includes a forward and reverse change.
+//  Initial posted event is FQDN_IN_USE_EVT.
+//  The update request is sent but a corrupt response is received, this occurs
+//  MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_corrupt_response) {
+    NameAddStubPtr name_add;
+
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+                                    NameAddTransaction::
+                                    FQDN_IN_USE_EVT, FWD_AND_REV_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->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_add->getDnsUpdateRequest();
+
+        // Run replacingFwdAddrsHandler to send the request. 
+        EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time out we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server corrupt response. 
+        name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run replacingFwdAddrsHandler again to process the response. 
+        EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, 
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, 
+                    name_add->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(NameAddTransactionTest, selectingRevServerHandler) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_REV_SERVER_ST, 
+                                    NameChangeTransaction::SELECT_SERVER_EVT));
+    
+    // Run selectingRevServerHandler. 
+    EXPECT_NO_THROW(name_add->selectingRevServerHandler());
+
+    // Verify that a server was selected. 
+    EXPECT_TRUE(name_add->getCurrentServer());
+
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, 
+              name_add->getNextEvent());
+
+    // Post a server IO error event.  This simulates an IO error occuring
+    // and a need to select the a new server.
+    ASSERT_NO_THROW(name_add->postNextEvent(NameChangeTransaction::
+                                            SERVER_IO_ERROR_EVT));
+
+    // Run selectingRevServerHandler. 
+    EXPECT_NO_THROW(name_add->selectingRevServerHandler());
+
+    // Test domain only has 1 server, so we should have exhausted server
+    // list. Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT, 
+              name_add->getNextEvent());
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::
+                                    SELECTING_REV_SERVER_ST, 
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw. 
+    EXPECT_THROW(name_add->selectingRevServerHandler(), 
+                 NameAddTransactionError);
+}
+
+//************************** replacingRevPtrsHandler tests *****************
+
+// Tests that replacingRevPtrsHandler rejects invalid events.
+TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_invalid_event) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler but with
+    // an invalid event.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, 
+                                    NameChangeTransaction::
+                                    StateModel::NOP_EVT));
+
+    // Running the handler should throw. 
+    EXPECT_THROW(name_add->replacingRevPtrsHandler(), 
+                 NameAddTransactionError);
+}
+
+// Tests replacingRevPtrsHandler 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(NameAddTransactionTest, replacingRevPtrsHandler_fwd_only_add_Ok) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, 
+                                    NameAddTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+  
+    // Should not be an update message yet. 
+    D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest();
+    EXPECT_TRUE(!update_msg);
+
+    // At this point completion flags should be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // Run replacingRevPtrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+    // Verify that an update message was constructed.
+    update_msg = name_add->getDnsUpdateRequest();
+    EXPECT_TRUE(update_msg);
+
+    // 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(NameAddTransaction::REPLACING_REV_PTRS_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::NOP_EVT, 
+              name_add->getNextEvent());
+
+    // Simulate receiving a succussful update response.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+    // Run replacingRevPtrsHandler again to process the response.
+    EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+    // Forward completion should be false, reverse should be true.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_TRUE(name_add->getReverseChangeCompleted());
+
+    // Since it is a reverse change, we should be done.
+    // Verify that we transitioned correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, 
+              name_add->getNextEvent());
+}
+
+// Tests replacingRevPtrsHandler 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(NameAddTransactionTest, replacingRevPtrsHandler_other_rcode) {
+    NameAddStubPtr name_add;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, 
+                                    NameAddTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->selectRevServer());
+
+    // Run replacingRevPtrsHandler to construct and send the request. 
+    EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+    // Simulate receiving server rejection response. Per RFC, anything other
+    // than no error is failure.  Arbitrarily choosing refused.
+    name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+    // Run replacingRevPtrsHandler again to process the response. 
+    //EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+    (name_add->replacingRevPtrsHandler());
+
+    // Completion flags should still be false.
+    EXPECT_FALSE(name_add->getForwardChangeCompleted());
+    EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+    // We should have failed the transaction. Verifiy that we transitioned 
+    // correctly.
+    EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, 
+              name_add->getCurrState());
+    EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, 
+              name_add->getNextEvent());
+}
+
+// Tests replacingRevPtrsHandler 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(NameAddTransactionTest, replacingRevPtrsHandler_time_out) {
+    NameAddStubPtr name_add;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, 
+                                    NameAddTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->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_add->getDnsUpdateRequest();
+
+        // Run replacingRevPtrsHandler to send the request. 
+        EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time out we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server IO timeout. 
+        name_add->setDnsUpdateStatus(DNSClient::TIMEOUT);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run replacingRevPtrsHandler again to process the response. 
+        EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, 
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_REV_SERVER_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, 
+                    name_add->getNextEvent());
+        }
+    }
+}
+
+// Tests replacingRevPtrsHandler 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(NameAddTransactionTest, replacingRevPtrsHandler_corrupt_response) {
+    NameAddStubPtr name_add;
+    // Create the transaction.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, 
+                                    NameAddTransaction::
+                                    SERVER_SELECTED_EVT, REVERSE_CHG));
+
+    // Select a server to satisfy log statements.
+    ASSERT_TRUE(name_add->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_add->getDnsUpdateRequest();
+
+        // Run replacingRevPtrsHandler to send the request. 
+        EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+        const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+        if (i == 1) {
+            // First time out we should build the message.
+            EXPECT_FALSE(prev_msg);
+            EXPECT_TRUE(curr_msg);
+        } else {
+            // Subsequent passes should reuse the request.
+            EXPECT_TRUE(prev_msg == curr_msg);
+        }
+
+        // Simulate a server corrupt response. 
+        name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+        name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+        // Run replacingRevPtrsHandler again to process the response. 
+        EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+        // Completion flags should be false.
+        EXPECT_FALSE(name_add->getForwardChangeCompleted());
+        EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+        if (i < max_tries) {
+            // We should be ready to try again.
+            EXPECT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, 
+                    name_add->getNextEvent());
+        } else {
+            // Server retries should be exhausted, time for a new server.
+            EXPECT_EQ(NameAddTransaction::SELECTING_REV_SERVER_ST, 
+                      name_add->getCurrState());
+            EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, 
+                    name_add->getNextEvent());
+        }
+    }
+}
+
+// Tests the processAddOkHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_OK_EVT 
+// 2. Posted event is invalid
+//
+TEST_F(NameAddTransactionTest, processAddOkHandler) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+                                    NameChangeTransaction::UPDATE_OK_EVT));
+    // Run processAddOkHandler. 
+    EXPECT_NO_THROW(name_add->processAddOkHandler());
+
+    // Verify that a server was selected. 
+    EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_add->getNcrStatus());
+
+    // Verify that the model has ended.
+    EXPECT_EQ(StateModel::END_ST, name_add->getCurrState());
+    EXPECT_EQ(StateModel::END_EVT, name_add->getNextEvent());
+
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST, 
+                                    StateModel::NOP_EVT));
+    // Running the handler should throw. 
+    EXPECT_THROW(name_add->processAddOkHandler(), NameAddTransactionError);
+}
+
+// Tests the processAddFailedHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_FAILED_EVT 
+// 2. Posted event is invalid
+//
+TEST_F(NameAddTransactionTest, processAddFailedHandler) {
+    NameAddStubPtr name_add;
+    // Create and prep a transaction, poised to run the handler.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::
+                                    PROCESS_TRANS_FAILED_ST,
+                                    NameChangeTransaction::UPDATE_FAILED_EVT));
+    // Run processAddFailedHandler. 
+    EXPECT_NO_THROW(name_add->processAddFailedHandler());
+
+    // Verify that a server was selected. 
+    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_add->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_add->getCurrState());
+    EXPECT_EQ(StateModel::END_EVT, name_add->getNextEvent());
+
+
+    // Create and prep transaction, poised to run the handler but with an
+    // invalid event.
+    ASSERT_NO_THROW(name_add = 
+                    prepHandlerTest(NameChangeTransaction::
+                                    PROCESS_TRANS_FAILED_ST, 
+                                    StateModel::NOP_EVT));
+    // Running the handler should throw. 
+    EXPECT_THROW(name_add->processAddFailedHandler(), NameAddTransactionError);
+}
+
+
 }



More information about the bind10-changes mailing list