datasrc_clients_builder_unittest.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. // Copyright (C) 2012 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 <util/unittests/check_valgrind.h>
  15. #include <dns/name.h>
  16. #include <dns/rrclass.h>
  17. #include <cc/data.h>
  18. #include <datasrc/client.h>
  19. #include <datasrc/factory.h>
  20. #include <auth/datasrc_clients_mgr.h>
  21. #include <auth/datasrc_config.h>
  22. #include <testutils/dnsmessage_test.h>
  23. #include "test_datasrc_clients_mgr.h"
  24. #include "datasrc_util.h"
  25. #include <gtest/gtest.h>
  26. #include <boost/function.hpp>
  27. #include <cstdlib>
  28. #include <string>
  29. #include <sstream>
  30. using isc::data::ConstElementPtr;
  31. using namespace isc::dns;
  32. using namespace isc::data;
  33. using namespace isc::datasrc;
  34. using namespace isc::auth::datasrc_clientmgr_internal;
  35. using namespace isc::auth::unittest;
  36. using namespace isc::testutils;
  37. namespace {
  38. class DataSrcClientsBuilderTest : public ::testing::Test {
  39. protected:
  40. DataSrcClientsBuilderTest() :
  41. clients_map(new std::map<RRClass,
  42. boost::shared_ptr<ConfigurableClientList> >),
  43. builder(&command_queue, &cond, &queue_mutex, &clients_map, &map_mutex),
  44. cond(command_queue, delayed_command_queue), rrclass(RRClass::IN()),
  45. shutdown_cmd(SHUTDOWN, ConstElementPtr()),
  46. noop_cmd(NOOP, ConstElementPtr())
  47. {}
  48. void configureZones(); // used for loadzone related tests
  49. ClientListMapPtr clients_map; // configured clients
  50. std::list<Command> command_queue; // test command queue
  51. std::list<Command> delayed_command_queue; // commands available after wait
  52. TestDataSrcClientsBuilder builder;
  53. TestCondVar cond;
  54. TestMutex queue_mutex;
  55. TestMutex map_mutex;
  56. const RRClass rrclass;
  57. const Command shutdown_cmd;
  58. const Command noop_cmd;
  59. };
  60. TEST_F(DataSrcClientsBuilderTest, runSingleCommand) {
  61. // A simplest case, just to check the basic behavior.
  62. command_queue.push_back(shutdown_cmd);
  63. builder.run();
  64. EXPECT_TRUE(command_queue.empty());
  65. EXPECT_EQ(0, cond.wait_count); // no wait because command queue is not empty
  66. EXPECT_EQ(1, queue_mutex.lock_count);
  67. EXPECT_EQ(1, queue_mutex.unlock_count);
  68. }
  69. TEST_F(DataSrcClientsBuilderTest, runMultiCommands) {
  70. // Two NOOP commands followed by SHUTDOWN. We should see two doNoop()
  71. // calls.
  72. command_queue.push_back(noop_cmd);
  73. command_queue.push_back(noop_cmd);
  74. command_queue.push_back(shutdown_cmd);
  75. builder.run();
  76. EXPECT_TRUE(command_queue.empty());
  77. EXPECT_EQ(1, queue_mutex.lock_count);
  78. EXPECT_EQ(1, queue_mutex.unlock_count);
  79. EXPECT_EQ(2, queue_mutex.noop_count);
  80. }
  81. TEST_F(DataSrcClientsBuilderTest, exception) {
  82. // Let the noop command handler throw exceptions and see if we can see
  83. // them. Right now, we simply abort to prevent the system from running
  84. // with half-broken state. Eventually we should introduce a better
  85. // error handling.
  86. if (!isc::util::unittests::runningOnValgrind()) {
  87. command_queue.push_back(noop_cmd);
  88. queue_mutex.throw_from_noop = TestMutex::EXCLASS;
  89. EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
  90. command_queue.push_back(noop_cmd);
  91. queue_mutex.throw_from_noop = TestMutex::INTEGER;
  92. EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
  93. }
  94. command_queue.push_back(noop_cmd);
  95. command_queue.push_back(shutdown_cmd); // we need to stop the loop
  96. queue_mutex.throw_from_noop = TestMutex::INTERNAL;
  97. builder.run();
  98. }
  99. TEST_F(DataSrcClientsBuilderTest, condWait) {
  100. // command_queue is originally empty, so it will require waiting on
  101. // condvar. specialized wait() will make the delayed command available.
  102. delayed_command_queue.push_back(shutdown_cmd);
  103. builder.run();
  104. // There should be one call to wait()
  105. EXPECT_EQ(1, cond.wait_count);
  106. // wait() effectively involves one more set of lock/unlock, so we have
  107. // two in total
  108. EXPECT_EQ(2, queue_mutex.lock_count);
  109. EXPECT_EQ(2, queue_mutex.unlock_count);
  110. }
  111. TEST_F(DataSrcClientsBuilderTest, reconfigure) {
  112. // Full testing of different configurations is not here, but we
  113. // do check a few cases of correct and erroneous input, to verify
  114. // the error handling
  115. // A command structure we'll modify to send different commands
  116. Command reconfig_cmd(RECONFIGURE, ConstElementPtr());
  117. // Initially, no clients should be there
  118. EXPECT_TRUE(clients_map->empty());
  119. // A config that doesn't do much except be accepted
  120. ConstElementPtr good_config = Element::fromJSON(
  121. "{"
  122. "\"IN\": [{"
  123. " \"type\": \"MasterFiles\","
  124. " \"params\": {},"
  125. " \"cache-enable\": true"
  126. "}]"
  127. "}"
  128. );
  129. // A configuration that is 'correct' in the top-level, but contains
  130. // bad data for the type it specifies
  131. ConstElementPtr bad_config = Element::fromJSON(
  132. "{"
  133. "\"IN\": [{"
  134. " \"type\": \"MasterFiles\","
  135. " \"params\": { \"foo\": [ 1, 2, 3, 4 ]},"
  136. " \"cache-enable\": true"
  137. "}]"
  138. "}"
  139. );
  140. reconfig_cmd.second = good_config;
  141. EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
  142. EXPECT_EQ(1, clients_map->size());
  143. EXPECT_EQ(1, map_mutex.lock_count);
  144. // Store the nonempty clients map we now have
  145. ClientListMapPtr working_config_clients(clients_map);
  146. // If a 'bad' command argument got here, the config validation should
  147. // have failed already, but still, the handler should return true,
  148. // and the clients_map should not be updated.
  149. reconfig_cmd.second = Element::create("{ \"foo\": \"bar\" }");
  150. EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
  151. EXPECT_EQ(working_config_clients, clients_map);
  152. // Building failed, so map mutex should not have been locked again
  153. EXPECT_EQ(1, map_mutex.lock_count);
  154. // The same for a configuration that has bad data for the type it
  155. // specifies
  156. reconfig_cmd.second = bad_config;
  157. builder.handleCommand(reconfig_cmd);
  158. EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
  159. EXPECT_EQ(working_config_clients, clients_map);
  160. // Building failed, so map mutex should not have been locked again
  161. EXPECT_EQ(1, map_mutex.lock_count);
  162. // The same goes for an empty parameter (it should at least be
  163. // an empty map)
  164. reconfig_cmd.second = ConstElementPtr();
  165. EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
  166. EXPECT_EQ(working_config_clients, clients_map);
  167. EXPECT_EQ(1, map_mutex.lock_count);
  168. // Reconfigure again with the same good clients, the result should
  169. // be a different map than the original, but not an empty one.
  170. reconfig_cmd.second = good_config;
  171. EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
  172. EXPECT_NE(working_config_clients, clients_map);
  173. EXPECT_EQ(1, clients_map->size());
  174. EXPECT_EQ(2, map_mutex.lock_count);
  175. // And finally, try an empty config to disable all datasource clients
  176. reconfig_cmd.second = Element::createMap();
  177. EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
  178. EXPECT_EQ(0, clients_map->size());
  179. EXPECT_EQ(3, map_mutex.lock_count);
  180. // Also check if it has been cleanly unlocked every time
  181. EXPECT_EQ(3, map_mutex.unlock_count);
  182. }
  183. TEST_F(DataSrcClientsBuilderTest, shutdown) {
  184. EXPECT_FALSE(builder.handleCommand(shutdown_cmd));
  185. }
  186. TEST_F(DataSrcClientsBuilderTest, badCommand) {
  187. // out-of-range command ID
  188. EXPECT_THROW(builder.handleCommand(Command(NUM_COMMANDS,
  189. ConstElementPtr())),
  190. isc::Unexpected);
  191. }
  192. // A helper function commonly used for the "loadzone" command tests.
  193. // It configures the given data source client lists with a memory data source
  194. // containing two zones, and checks the zones are correctly loaded.
  195. void
  196. zoneChecks(ClientListMapPtr clients_map, RRClass rrclass) {
  197. EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
  198. find(Name("ns.test1.example")).finder_->
  199. find(Name("ns.test1.example"), RRType::A())->code);
  200. EXPECT_EQ(ZoneFinder::NXRRSET, clients_map->find(rrclass)->second->
  201. find(Name("ns.test1.example")).finder_->
  202. find(Name("ns.test1.example"), RRType::AAAA())->code);
  203. EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
  204. find(Name("ns.test2.example")).finder_->
  205. find(Name("ns.test2.example"), RRType::A())->code);
  206. EXPECT_EQ(ZoneFinder::NXRRSET, clients_map->find(rrclass)->second->
  207. find(Name("ns.test2.example")).finder_->
  208. find(Name("ns.test2.example"), RRType::AAAA())->code);
  209. }
  210. // Another helper that checks after completing loadzone command.
  211. void
  212. newZoneChecks(ClientListMapPtr clients_map, RRClass rrclass) {
  213. EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
  214. find(Name("ns.test1.example")).finder_->
  215. find(Name("ns.test1.example"), RRType::A())->code);
  216. // now test1.example should have ns/AAAA
  217. EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
  218. find(Name("ns.test1.example")).finder_->
  219. find(Name("ns.test1.example"), RRType::AAAA())->code);
  220. // test2.example shouldn't change
  221. EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
  222. find(Name("ns.test2.example")).finder_->
  223. find(Name("ns.test2.example"), RRType::A())->code);
  224. EXPECT_EQ(ZoneFinder::NXRRSET,
  225. clients_map->find(rrclass)->second->
  226. find(Name("ns.test2.example")).finder_->
  227. find(Name("ns.test2.example"), RRType::AAAA())->code);
  228. }
  229. void
  230. DataSrcClientsBuilderTest::configureZones() {
  231. ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in "
  232. TEST_DATA_BUILDDIR "/test1.zone.copied"));
  233. ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_DATA_DIR "/test2.zone.in "
  234. TEST_DATA_BUILDDIR "/test2.zone.copied"));
  235. const ConstElementPtr config(
  236. Element::fromJSON(
  237. "{"
  238. "\"IN\": [{"
  239. " \"type\": \"MasterFiles\","
  240. " \"params\": {"
  241. " \"test1.example\": \"" +
  242. std::string(TEST_DATA_BUILDDIR "/test1.zone.copied") + "\","
  243. " \"test2.example\": \"" +
  244. std::string(TEST_DATA_BUILDDIR "/test2.zone.copied") + "\""
  245. " },"
  246. " \"cache-enable\": true"
  247. "}]}"));
  248. clients_map = configureDataSource(config);
  249. zoneChecks(clients_map, rrclass);
  250. }
  251. TEST_F(DataSrcClientsBuilderTest, loadZone) {
  252. // pre test condition checks
  253. EXPECT_EQ(0, map_mutex.lock_count);
  254. EXPECT_EQ(0, map_mutex.unlock_count);
  255. configureZones();
  256. EXPECT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
  257. "/test1-new.zone.in "
  258. TEST_DATA_BUILDDIR "/test1.zone.copied"));
  259. EXPECT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
  260. "/test2-new.zone.in "
  261. TEST_DATA_BUILDDIR "/test2.zone.copied"));
  262. const Command loadzone_cmd(LOADZONE, Element::fromJSON(
  263. "{\"class\": \"IN\","
  264. " \"origin\": \"test1.example\"}"));
  265. EXPECT_TRUE(builder.handleCommand(loadzone_cmd));
  266. EXPECT_EQ(1, map_mutex.lock_count); // we should have acquired the lock
  267. EXPECT_EQ(1, map_mutex.unlock_count); // and released it.
  268. newZoneChecks(clients_map, rrclass);
  269. }
  270. TEST_F(DataSrcClientsBuilderTest,
  271. #ifdef USE_STATIC_LINK
  272. DISABLED_loadZoneSQLite3
  273. #else
  274. loadZoneSQLite3
  275. #endif
  276. )
  277. {
  278. // Prepare the database first
  279. const std::string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
  280. std::stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
  281. createSQLite3DB(rrclass, Name("example.org"), test_db.c_str(), ss);
  282. // This describes the data source in the configuration
  283. const ConstElementPtr config(Element::fromJSON("{"
  284. "\"IN\": [{"
  285. " \"type\": \"sqlite3\","
  286. " \"params\": {\"database_file\": \"" + test_db + "\"},"
  287. " \"cache-enable\": true,"
  288. " \"cache-zones\": [\"example.org\"]"
  289. "}]}"));
  290. clients_map = configureDataSource(config);
  291. // Check that the A record at www.example.org does not exist
  292. EXPECT_EQ(ZoneFinder::NXDOMAIN,
  293. clients_map->find(rrclass)->second->
  294. find(Name("example.org")).finder_->
  295. find(Name("www.example.org"), RRType::A())->code);
  296. // Add the record to the underlying sqlite database, by loading
  297. // it as a separate datasource, and updating it
  298. ConstElementPtr sql_cfg = Element::fromJSON("{ \"type\": \"sqlite3\","
  299. "\"database_file\": \""
  300. + test_db + "\"}");
  301. DataSourceClientContainer sql_ds("sqlite3", sql_cfg);
  302. ZoneUpdaterPtr sql_updater =
  303. sql_ds.getInstance().getUpdater(Name("example.org"), false);
  304. sql_updater->addRRset(
  305. *textToRRset("www.example.org. 60 IN A 192.0.2.1"));
  306. sql_updater->commit();
  307. EXPECT_EQ(ZoneFinder::NXDOMAIN,
  308. clients_map->find(rrclass)->second->
  309. find(Name("example.org")).finder_->
  310. find(Name("www.example.org"), RRType::A())->code);
  311. // Now send the command to reload it
  312. const Command loadzone_cmd(LOADZONE, Element::fromJSON(
  313. "{\"class\": \"IN\","
  314. " \"origin\": \"example.org\"}"));
  315. EXPECT_TRUE(builder.handleCommand(loadzone_cmd));
  316. // And now it should be present too.
  317. EXPECT_EQ(ZoneFinder::SUCCESS,
  318. clients_map->find(rrclass)->second->
  319. find(Name("example.org")).finder_->
  320. find(Name("www.example.org"), RRType::A())->code);
  321. // An error case: the zone has no configuration. (note .com here)
  322. const Command nozone_cmd(LOADZONE, Element::fromJSON(
  323. "{\"class\": \"IN\","
  324. " \"origin\": \"example.com\"}"));
  325. EXPECT_THROW(builder.handleCommand(nozone_cmd),
  326. TestDataSrcClientsBuilder::InternalCommandError);
  327. // The previous zone is not hurt in any way
  328. EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
  329. find(Name("example.org")).finder_->
  330. find(Name("example.org"), RRType::SOA())->code);
  331. // attempt of reloading a zone but in-memory cache is disabled.
  332. const ConstElementPtr config2(Element::fromJSON("{"
  333. "\"IN\": [{"
  334. " \"type\": \"sqlite3\","
  335. " \"params\": {\"database_file\": \"" + test_db + "\"},"
  336. " \"cache-enable\": false,"
  337. " \"cache-zones\": [\"example.org\"]"
  338. "}]}"));
  339. clients_map = configureDataSource(config2);
  340. EXPECT_THROW(builder.handleCommand(
  341. Command(LOADZONE, Element::fromJSON(
  342. "{\"class\": \"IN\","
  343. " \"origin\": \"example.org\"}"))),
  344. TestDataSrcClientsBuilder::InternalCommandError);
  345. // basically impossible case: in-memory cache is completely disabled.
  346. // In this implementation of manager-builder, this should never happen,
  347. // but it catches it like other configuration error and keeps going.
  348. clients_map->clear();
  349. boost::shared_ptr<ConfigurableClientList> nocache_list(
  350. new ConfigurableClientList(rrclass));
  351. nocache_list->configure(
  352. Element::fromJSON(
  353. "[{\"type\": \"sqlite3\","
  354. " \"params\": {\"database_file\": \"" + test_db + "\"},"
  355. " \"cache-enable\": true,"
  356. " \"cache-zones\": [\"example.org\"]"
  357. "}]"), false); // false = disable cache
  358. (*clients_map)[rrclass] = nocache_list;
  359. EXPECT_THROW(builder.handleCommand(
  360. Command(LOADZONE, Element::fromJSON(
  361. "{\"class\": \"IN\","
  362. " \"origin\": \"example.org\"}"))),
  363. TestDataSrcClientsBuilder::InternalCommandError);
  364. }
  365. TEST_F(DataSrcClientsBuilderTest, loadBrokenZone) {
  366. configureZones();
  367. ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_DATA_DIR
  368. "/test1-broken.zone.in "
  369. TEST_DATA_BUILDDIR "/test1.zone.copied"));
  370. // there's an error in the new zone file. reload will be rejected.
  371. const Command loadzone_cmd(LOADZONE, Element::fromJSON(
  372. "{\"class\": \"IN\","
  373. " \"origin\": \"test1.example\"}"));
  374. EXPECT_THROW(builder.handleCommand(loadzone_cmd),
  375. TestDataSrcClientsBuilder::InternalCommandError);
  376. zoneChecks(clients_map, rrclass); // zone shouldn't be replaced
  377. }
  378. TEST_F(DataSrcClientsBuilderTest, loadUnreadableZone) {
  379. configureZones();
  380. // install the zone file as unreadable
  381. ASSERT_EQ(0, std::system(INSTALL_PROG " -c -m 000 " TEST_DATA_DIR
  382. "/test1.zone.in "
  383. TEST_DATA_BUILDDIR "/test1.zone.copied"));
  384. const Command loadzone_cmd(LOADZONE, Element::fromJSON(
  385. "{\"class\": \"IN\","
  386. " \"origin\": \"test1.example\"}"));
  387. EXPECT_THROW(builder.handleCommand(loadzone_cmd),
  388. TestDataSrcClientsBuilder::InternalCommandError);
  389. zoneChecks(clients_map, rrclass); // zone shouldn't be replaced
  390. }
  391. TEST_F(DataSrcClientsBuilderTest, loadZoneWithoutDataSrc) {
  392. // try to execute load command without configuring the zone beforehand.
  393. // it should fail.
  394. EXPECT_THROW(builder.handleCommand(
  395. Command(LOADZONE,
  396. Element::fromJSON(
  397. "{\"class\": \"IN\", "
  398. " \"origin\": \"test1.example\"}"))),
  399. TestDataSrcClientsBuilder::InternalCommandError);
  400. }
  401. TEST_F(DataSrcClientsBuilderTest, loadZoneInvalidParams) {
  402. configureZones();
  403. if (!isc::util::unittests::runningOnValgrind()) {
  404. // null arg: this causes assertion failure
  405. EXPECT_DEATH_IF_SUPPORTED({
  406. builder.handleCommand(Command(LOADZONE, ElementPtr()));
  407. }, "");
  408. }
  409. // zone class is bogus (note that this shouldn't happen except in tests)
  410. EXPECT_THROW(builder.handleCommand(
  411. Command(LOADZONE,
  412. Element::fromJSON(
  413. "{\"origin\": \"test1.example\","
  414. " \"class\": \"no_such_class\"}"))),
  415. InvalidRRClass);
  416. // not a string
  417. EXPECT_THROW(builder.handleCommand(
  418. Command(LOADZONE,
  419. Element::fromJSON(
  420. "{\"origin\": \"test1.example\","
  421. " \"class\": 1}"))),
  422. isc::data::TypeError);
  423. // class or origin is missing: result in assertion failure
  424. if (!isc::util::unittests::runningOnValgrind()) {
  425. /*
  426. EXPECT_DEATH_IF_SUPPORTED({
  427. builder.handleCommand(
  428. Command(LOADZONE,
  429. Element::fromJSON(
  430. "{\"origin\": \"test1.example\"}")));
  431. }, "");
  432. */
  433. EXPECT_DEATH_IF_SUPPORTED({
  434. builder.handleCommand(Command(LOADZONE,
  435. Element::fromJSON(
  436. "{\"class\": \"IN\"}")));
  437. }, "");
  438. }
  439. // zone doesn't exist in the data source
  440. EXPECT_THROW(
  441. builder.handleCommand(
  442. Command(LOADZONE,
  443. Element::fromJSON(
  444. "{\"class\": \"IN\", \"origin\": \"xx\"}"))),
  445. TestDataSrcClientsBuilder::InternalCommandError);
  446. // origin is bogus
  447. EXPECT_THROW(builder.handleCommand(
  448. Command(LOADZONE,
  449. Element::fromJSON(
  450. "{\"class\": \"IN\", \"origin\": \"...\"}"))),
  451. EmptyLabel);
  452. EXPECT_THROW(builder.handleCommand(
  453. Command(LOADZONE,
  454. Element::fromJSON(
  455. "{\"origin\": 10, \"class\": 1}"))),
  456. isc::data::TypeError);
  457. }
  458. } // unnamed namespace