nc_trans_unittests.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. // Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <d2/nc_trans.h>
  15. #include <boost/function.hpp>
  16. #include <boost/bind.hpp>
  17. #include <gtest/gtest.h>
  18. using namespace std;
  19. using namespace isc;
  20. using namespace isc::d2;
  21. namespace {
  22. /// @brief Test derivation of NameChangeTransaction for exercising state
  23. /// model mechanics.
  24. ///
  25. /// This class faciliates testing by making non-public methods accessible so
  26. /// they can be invoked directly in test routines. It implements a very
  27. /// rudimentary state model, sufficient to test the state model mechanics
  28. /// supplied by the base class.
  29. class NameChangeStub : public NameChangeTransaction {
  30. public:
  31. // NameChangeStub states
  32. static const int DO_WORK_ST = DERIVED_STATES + 1;
  33. // NameChangeStub events
  34. static const int START_WORK_EVT = DERIVED_EVENTS + 1;
  35. /// @brief Constructor
  36. ///
  37. /// Parameters match those needed by NameChangeTransaction.
  38. NameChangeStub(isc::asiolink::IOService& io_service,
  39. dhcp_ddns::NameChangeRequestPtr& ncr,
  40. DdnsDomainPtr forward_domain,
  41. DdnsDomainPtr reverse_domain)
  42. : NameChangeTransaction(io_service, ncr, forward_domain,
  43. reverse_domain) {
  44. }
  45. /// @brief Destructor
  46. virtual ~NameChangeStub() {
  47. }
  48. /// @brief State handler for the READY_ST.
  49. ///
  50. /// Serves as the starting state handler, it consumes the
  51. /// START_TRANSACTION_EVT "transitioing" to the state, DO_WORK_ST and
  52. /// sets the next event to START_WORK_EVT.
  53. void readyHandler() {
  54. switch(getNextEvent()) {
  55. case START_TRANSACTION_EVT:
  56. setState(DO_WORK_ST);
  57. setNextEvent(START_WORK_EVT);
  58. break;
  59. default:
  60. // its bogus
  61. isc_throw(NameChangeTransactionError, "invalid event: "
  62. << getNextEvent() << " for state: " << getState());
  63. }
  64. }
  65. /// @brief State handler for the DO_WORK_ST.
  66. ///
  67. /// Simulates a state that starts some form of asynchronous work.
  68. /// When next event is START_WROK_EVT it sets the status to pending
  69. /// and signals the state model must "wait" for an event by setting
  70. /// next event to NOP_EVT.
  71. ///
  72. /// When next event is IO_COMPLETED_EVT, it transitions to the state,
  73. /// DONE_ST, and sets the next event to ALL_DONE_EVT.
  74. void doWorkHandler() {
  75. switch(getNextEvent()) {
  76. case START_WORK_EVT:
  77. setNcrStatus(dhcp_ddns::ST_PENDING);
  78. setNextEvent(NOP_EVT);
  79. break;
  80. //case WORK_DONE_EVT:
  81. case IO_COMPLETED_EVT:
  82. setState(DONE_ST);
  83. setNextEvent(ALL_DONE_EVT);
  84. break;
  85. default:
  86. // its bogus
  87. isc_throw(NameChangeTransactionError, "invalid event: "
  88. << getNextEvent() << " for state: " << getState());
  89. }
  90. }
  91. /// @brief State handler for the DONE_ST.
  92. ///
  93. /// This is the last state in the model. Note that it sets the
  94. /// status to completed and next event to NOP_EVT.
  95. void doneHandler() {
  96. switch(getNextEvent()) {
  97. case ALL_DONE_EVT:
  98. setNcrStatus(dhcp_ddns::ST_COMPLETED);
  99. setNextEvent(NOP_EVT);
  100. break;
  101. default:
  102. // its bogus
  103. isc_throw(NameChangeTransactionError, "invalid event: "
  104. << getNextEvent() << " for state: " << getState());
  105. }
  106. }
  107. /// @brief Initializes the state handler map.
  108. void initStateHandlerMap() {
  109. addToMap(READY_ST,
  110. boost::bind(&NameChangeStub::readyHandler, this));
  111. addToMap(DO_WORK_ST,
  112. boost::bind(&NameChangeStub::doWorkHandler, this));
  113. addToMap(DONE_ST,
  114. boost::bind(&NameChangeStub::doneHandler, this));
  115. }
  116. // Expose the protected methods to be tested.
  117. using NameChangeTransaction::addToMap;
  118. using NameChangeTransaction::getStateHandler;
  119. using NameChangeTransaction::initStateHandlerMap;
  120. using NameChangeTransaction::runStateModel;
  121. using NameChangeTransaction::setState;
  122. using NameChangeTransaction::setNextEvent;
  123. };
  124. const int NameChangeStub::DO_WORK_ST;
  125. const int NameChangeStub::START_WORK_EVT;
  126. /// @brief Defines a pointer to a D2UpdateMgr instance.
  127. typedef boost::shared_ptr<NameChangeStub> NameChangeStubPtr;
  128. /// @brief Test fixture for testing NameChangeTransaction
  129. ///
  130. /// Note this class uses NameChangeStub class to exercise non-public
  131. /// aspects of NameChangeTransaction.
  132. class NameChangeTransactionTest : public ::testing::Test {
  133. public:
  134. isc::asiolink::IOService io_service_;
  135. virtual ~NameChangeTransactionTest() {
  136. }
  137. /// @brief Instantiates a NameChangeStub built around a canned
  138. /// NameChangeRequest.
  139. NameChangeStubPtr makeCannedTransaction() {
  140. const char* msg_str =
  141. "{"
  142. " \"change_type\" : 0 , "
  143. " \"forward_change\" : true , "
  144. " \"reverse_change\" : true , "
  145. " \"fqdn\" : \"walah.walah.org.\" , "
  146. " \"ip_address\" : \"192.168.2.1\" , "
  147. " \"dhcid\" : \"0102030405060708\" , "
  148. " \"lease_expires_on\" : \"20130121132405\" , "
  149. " \"lease_length\" : 1300 "
  150. "}";
  151. dhcp_ddns::NameChangeRequestPtr ncr;
  152. DnsServerInfoStoragePtr servers;
  153. DdnsDomainPtr forward_domain;
  154. DdnsDomainPtr reverse_domain;
  155. ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str);
  156. forward_domain.reset(new DdnsDomain("*", "", servers));
  157. reverse_domain.reset(new DdnsDomain("*", "", servers));
  158. return (NameChangeStubPtr(new NameChangeStub(io_service_, ncr,
  159. forward_domain, reverse_domain)));
  160. }
  161. };
  162. /// @brief Tests NameChangeTransaction construction.
  163. /// This test verifies that:
  164. /// 1. Construction with null NameChangeRequest
  165. /// 2. Construction with null forward domain is not allowed when the request
  166. /// requires forward change.
  167. /// 3. Construction with null reverse domain is not allowed when the request
  168. /// requires reverse change.
  169. /// 4. Valid construction functions properly
  170. TEST(NameChangeTransaction, construction) {
  171. isc::asiolink::IOService io_service;
  172. const char* msg_str =
  173. "{"
  174. " \"change_type\" : 0 , "
  175. " \"forward_change\" : true , "
  176. " \"reverse_change\" : true , "
  177. " \"fqdn\" : \"walah.walah.org.\" , "
  178. " \"ip_address\" : \"192.168.2.1\" , "
  179. " \"dhcid\" : \"0102030405060708\" , "
  180. " \"lease_expires_on\" : \"20130121132405\" , "
  181. " \"lease_length\" : 1300 "
  182. "}";
  183. dhcp_ddns::NameChangeRequestPtr ncr;
  184. dhcp_ddns::NameChangeRequestPtr empty_ncr;
  185. DnsServerInfoStoragePtr servers;
  186. DdnsDomainPtr forward_domain;
  187. DdnsDomainPtr reverse_domain;
  188. DdnsDomainPtr empty_domain;
  189. ASSERT_NO_THROW(ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str));
  190. ASSERT_NO_THROW(forward_domain.reset(new DdnsDomain("*", "", servers)));
  191. ASSERT_NO_THROW(reverse_domain.reset(new DdnsDomain("*", "", servers)));
  192. // Verify that construction with an empty NameChangeRequest throws.
  193. EXPECT_THROW(NameChangeTransaction(io_service, empty_ncr,
  194. forward_domain, reverse_domain),
  195. NameChangeTransactionError);
  196. // Verify that construction with an empty forward domain when the
  197. // NameChangeRequest calls for a forward change throws.
  198. EXPECT_THROW(NameChangeTransaction(io_service, ncr,
  199. empty_domain, reverse_domain),
  200. NameChangeTransactionError);
  201. // Verify that construction with an empty reverse domain when the
  202. // NameChangeRequest calls for a reverse change throws.
  203. EXPECT_THROW(NameChangeTransaction(io_service, ncr,
  204. forward_domain, empty_domain),
  205. NameChangeTransactionError);
  206. // Verify that a valid construction attempt works.
  207. EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
  208. forward_domain, reverse_domain));
  209. // Verify that an empty forward domain is allowed when the requests does
  210. // include a forward change.
  211. ncr->setForwardChange(false);
  212. EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
  213. empty_domain, reverse_domain));
  214. // Verify that an empty reverse domain is allowed when the requests does
  215. // include a reverse change.
  216. ncr->setReverseChange(false);
  217. EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
  218. empty_domain, empty_domain));
  219. }
  220. /// @brief Test the basic mechanics of state model execution.
  221. /// It first verifies basic state handle map fucntionality, and then
  222. /// runs the NameChangeStub state model through from start to finish.
  223. TEST_F(NameChangeTransactionTest, stateModelTest) {
  224. NameChangeStubPtr name_change;
  225. ASSERT_NO_THROW(name_change = makeCannedTransaction());
  226. // Verify that getStateHandler will throw when, handler map is empty.
  227. EXPECT_THROW(name_change->getStateHandler(NameChangeTransaction::READY_ST),
  228. NameChangeTransactionError);
  229. // Verify that we can add a handler to the map.
  230. ASSERT_NO_THROW(name_change->addToMap(NameChangeTransaction::READY_ST,
  231. boost::bind(&NameChangeStub::readyHandler,
  232. name_change.get())));
  233. // Verify that we can find the handler by its state.
  234. EXPECT_NO_THROW(name_change->getStateHandler(NameChangeTransaction::
  235. READY_ST));
  236. // Verify that we cannot add a duplicate.
  237. EXPECT_THROW(name_change->addToMap(NameChangeTransaction::READY_ST,
  238. boost::bind(&NameChangeStub::readyHandler,
  239. name_change.get())),
  240. NameChangeTransactionError);
  241. // Verify that we can still find the handler by its state.
  242. EXPECT_NO_THROW(name_change->getStateHandler(NameChangeTransaction::
  243. READY_ST));
  244. // Get a fresh transaction.
  245. ASSERT_NO_THROW(name_change = makeCannedTransaction());
  246. // Manually call checkHandlerMap to ensure our test map populates.
  247. // This is the method startTranscation invokes.
  248. ASSERT_NO_THROW(name_change->initStateHandlerMap());
  249. // Verify that we can find all the handlers by their state.
  250. EXPECT_NO_THROW(name_change->getStateHandler(NameChangeTransaction::
  251. READY_ST));
  252. EXPECT_NO_THROW(name_change->getStateHandler(NameChangeStub::DO_WORK_ST));
  253. EXPECT_NO_THROW(name_change->getStateHandler(NameChangeTransaction::
  254. DONE_ST));
  255. // Default value for state is NEW_ST. Attempting to run the model
  256. // with an invalid state will result in status of ST_FAILED.
  257. ASSERT_EQ(NameChangeTransaction::NEW_ST, name_change->getState());
  258. EXPECT_NO_THROW(name_change->runStateModel(NameChangeTransaction::
  259. START_TRANSACTION_EVT));
  260. EXPECT_EQ(dhcp_ddns::ST_FAILED, name_change->getNcrStatus());
  261. // Get a fresh transaction.
  262. ASSERT_NO_THROW(name_change = makeCannedTransaction());
  263. // Launch the transaction properly by calling startTranscation.
  264. // Verify that this transitions through to state of DO_WORK_ST,
  265. // last event is START_WORK_EVT, next event is NOP_EVT, and
  266. // NCR status is ST_PENDING.
  267. ASSERT_NO_THROW(name_change->startTransaction());
  268. EXPECT_EQ(NameChangeStub::DO_WORK_ST, name_change->getState());
  269. EXPECT_EQ(NameChangeStub::START_WORK_EVT, name_change->getLastEvent());
  270. EXPECT_EQ(NameChangeTransaction::NOP_EVT, name_change->getNextEvent());
  271. EXPECT_EQ(dhcp_ddns::ST_PENDING, name_change->getNcrStatus());
  272. // Simulate completion of DNSClient exchange by invoking the callback.
  273. EXPECT_NO_THROW((*name_change)(DNSClient::SUCCESS));
  274. // Verify that the state model has progressed through to completion:
  275. // it is in the DONE_ST, the status is ST_COMPLETED, and the next event
  276. // is NOP_EVT.
  277. EXPECT_EQ(NameChangeTransaction::DONE_ST, name_change->getState());
  278. EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_change->getNcrStatus());
  279. EXPECT_EQ(NameChangeTransaction::NOP_EVT, name_change->getNextEvent());
  280. }
  281. }