]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[3436] Added configuration permutations test from file to D2
authorThomas Markwalder <tmark@isc.org>
Thu, 26 Jun 2014 14:33:57 +0000 (10:33 -0400)
committerThomas Markwalder <tmark@isc.org>
Thu, 26 Jun 2014 14:33:57 +0000 (10:33 -0400)
Added the unit test D2CfgMgrTest.configPermutations to
d2_cfg_mgr_unittests.cc.  This test iterates through the
list of test configurations defined in a specialzed JSON
data file.  It provides a relatively painless way to test
a large number configurations without hard-coding them.

Added the test data file:

It currently contains over sixty tests. The vast majority
of these tests are invalid content tests.

src/bin/d2/d2_config.cc
src/bin/d2/d2_config.h
src/bin/d2/tests/Makefile.am
src/bin/d2/tests/d2_cfg_mgr_unittests.cc
src/bin/d2/tests/test_data_files_config.h.in
src/bin/d2/tests/testdata/d2_cfg_tests.json [new file with mode: 0644]

index 63ce0e01fcc6206bfdbd08b9e69cc25a10a51f80..ddc2f84096a8b44301c68f5eeb96e50d3376e77a 100644 (file)
@@ -349,7 +349,8 @@ TSIGKeyInfoParser::build(isc::data::ConstElementPtr key_config) {
     // data to the parser's local storage.
     BOOST_FOREACH (config_pair, key_config->mapValue()) {
         isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
-                                    config_pair.second->getPosition()));
+                                                       config_pair.second->
+                                                       getPosition()));
         parser->build(config_pair.second);
         parser->commit();
     }
@@ -387,8 +388,7 @@ TSIGKeyInfoParser::build(isc::data::ConstElementPtr key_config) {
     try {
         TSIGKeyInfo::stringToAlgorithmName(algorithm);
     } catch (const std::exception& ex) {
-        isc_throw(D2CfgError, "TSIG key invalid algorithm : "
-                  << algorithm << " : " << pos[1]);
+        isc_throw(D2CfgError, "TSIG key : " << ex.what() << " : " << pos[1]);
     }
 
     // Secret cannot be blank.
@@ -513,7 +513,8 @@ DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
     // data to the parser's local storage.
     BOOST_FOREACH (config_pair, server_config->mapValue()) {
         isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
-                                    config_pair.second->getPosition()));
+                                                       config_pair.second->
+                                                       getPosition()));
         parser->build(config_pair.second);
         parser->commit();
     }
@@ -682,7 +683,8 @@ DdnsDomainParser::build(isc::data::ConstElementPtr domain_config) {
     isc::dhcp::ConfigPair config_pair;
     BOOST_FOREACH(config_pair, domain_config->mapValue()) {
         isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
-                                    config_pair.second->getPosition()));
+                                                       config_pair.second->
+                                                       getPosition()));
         parser->build(config_pair.second);
         parser->commit();
     }
@@ -838,7 +840,9 @@ DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
     // data to the parser's local storage.
     isc::dhcp::ConfigPair config_pair;
     BOOST_FOREACH(config_pair, domain_config->mapValue()) {
-        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
+        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
+                                                       config_pair.second->
+                                                       getPosition()));
         parser->build(config_pair.second);
         parser->commit();
     }
@@ -848,7 +852,9 @@ DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
 }
 
 isc::dhcp::ParserPtr
-DdnsDomainListMgrParser::createConfigParser(const std::string& config_id) {
+DdnsDomainListMgrParser::createConfigParser(const std::string& config_id,
+                                            const isc::data::Element::
+                                            Position& pos) {
     DhcpConfigParser* parser = NULL;
     if (config_id == "ddns_domains") {
        // Domain list parser is given our local domain storage. It will pass
@@ -857,7 +863,8 @@ DdnsDomainListMgrParser::createConfigParser(const std::string& config_id) {
        parser = new DdnsDomainListParser(config_id, local_domains_, keys_);
     } else {
        isc_throw(NotImplemented, "parser error: "
-                 "DdnsDomainListMgr parameter not supported: " << config_id);
+                 "DdnsDomainListMgr parameter not supported: " << config_id
+                 << " : " << pos);
     }
 
     // Return the new domain parser instance.
index 27eaf7e2687bf74016e2012681bf06e9df23ac9e..b9696d2116e9e41863aa5823489f7a3f02051553 100644 (file)
@@ -1145,10 +1145,16 @@ public:
     ///
     /// @param config_id is the "item_name" for a specific member element of
     /// the manager specification.
+    /// @param pos position within the configuration text (or file) of element
+    /// to be parsed.  This is passed for error messaging.
     ///
     /// @return returns a pointer to newly created parser.
-    virtual isc::dhcp::ParserPtr createConfigParser(const std::string&
-                                                    config_id);
+    ///
+    /// @throw D2CfgError if configuration contains an unknown parameter
+    virtual isc::dhcp::ParserPtr
+    createConfigParser(const std::string& config_id,
+                       const isc::data::Element::Position& pos =
+                       isc::data::Element::ZERO_POSITION());
 
     /// @brief Commits the configured DdsnDomainListMgr
     /// Currently this method is a NOP, as the manager instance is created
index 355d4c70927037a5e24e8c904362991a1e59b94f..629ed798972432c04bfa23d4d12a8d2f3eb1bedc 100644 (file)
@@ -12,6 +12,7 @@ noinst_SCRIPTS = d2_process_tests.sh
 
 EXTRA_DIST  = $(PYTESTS)
 EXTRA_DIST += d2_process_tests.sh.in
+EXTRA_DIST += testdata/d2_cfg_tests.json
 
 # Explicitly specify paths to dynamic libraries required by loadable python
 # modules. That is required on Mac OS systems. Otherwise we will get exception
index f3dd745dbea5faf86ae400ce71ce196dc7c1ea55..c2dc27912b9f625913b062b411204c7969d3c988 100644 (file)
@@ -32,6 +32,10 @@ std::string specfile(const std::string& name) {
     return (std::string(D2_SRC_DIR) + "/" + name);
 }
 
+std::string testDataFile(const std::string& name) {
+    return (std::string(D2_TEST_DATA_DIR) + "/" + name);
+}
+
 /// @brief Test fixture class for testing D2CfgMgr class.
 /// It maintains an member instance of D2CfgMgr and provides methods for
 /// converting JSON strings to configuration element sets, checking parse
@@ -1564,4 +1568,101 @@ TEST_F(D2CfgMgrTest, matchReverse) {
     ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError);
 }
 
+/// @brief Tests D2 config parsing against a wide range of config permutations.
+/// It iterates over all of the test configurations described in given file.
+/// The file content is JSON specialized to this test. The format of the file
+/// is:
+///
+/// @code
+/// # The file must open with a list. It's name is arbitrary.
+///
+/// { "test_list" :
+/// [
+///
+/// #    Test one starts here:
+///      {
+///
+/// #    Each test has:
+/// #      1. description - optional text description
+/// #      2. should_fail - bool indicator if parsing is expected to file
+/// #         (defaults to false)
+/// #       3. data - configuration text to parse
+/// #
+///      "description" : "<text describing test>",
+///      "should_fail" : <true|false> ,
+///      "data" :
+///          {
+/// #        configuration elements here
+///          "bool_val" : false,
+///          "some_map" :  {}
+/// #         :
+///          }
+///      }
+///
+/// #    Next test would start here
+///      ,
+///      {
+///      }
+///
+/// ]}
+///
+/// @endcode
+///
+/// (The file supports comments per Element::fromJSONFile())
+///
+TEST_F(D2CfgMgrTest, configPermutations) {
+    std::string test_file = testDataFile("d2_cfg_tests.json");
+    isc::data::ConstElementPtr tests;
+
+    // Read contents of the file and parse it as JSON. Note it must contain
+    // all valid JSON, we aren't testing JSON parsing.
+    try {
+        tests = isc::data::Element::fromJSONFile(test_file, true);
+    } catch (const std::exception& ex) {
+        FAIL() << "ERROR parsing file : " << test_file << " : " << ex.what();
+    }
+
+    // Read in each test For each test, read:
+    //  1. description - optional text description
+    //  2. should_fail - bool indicator if parsing is expected to file (defaults
+    //     to false
+    //  3. data - configuration text to parse
+    //
+    // Next attempt to parse the configuration by passing it into
+    // D2CfgMgr::parseConfig().  Then check the parsing outcome against the
+    // expected outcome as given by should_fail.
+    isc::data::ConstElementPtr test;
+    BOOST_FOREACH(test, tests->get("test_list")->listValue()) {
+
+        // Grab the description.
+        std::string description = "<no desc>";
+        isc::data::ConstElementPtr elem = test->get("description");
+        if (elem) {
+            elem->getValue(description);
+        }
+
+        // Grab the outcome flag, should_fail, defaults to false if it's
+        // not specified.
+        bool should_fail = false;
+        elem = test->get("should_fail");
+        if (elem)  {
+            elem->getValue(should_fail);
+        }
+
+        // Grab the test's configuration data.
+        isc::data::ConstElementPtr data = test->get("data");
+        ASSERT_TRUE(data) << "No data for test: "
+                          << " : " << test->getPosition();
+
+        // Verify that we can parse the configuration.
+        answer_ = cfg_mgr_->parseConfig(data);
+        if (checkAnswer(!should_fail)) {
+            ADD_FAILURE() << "Parsing should have "
+                          << (should_fail ? "failed" : "passed")
+                          << " for : " << description
+                          << " : "  << test->getPosition();
+        }
+    }
+}
+
 } // end of anonymous namespace
index b351f88d1e322f5344421797e0a0952b4146fbb2..cbecca8c003f5fdd98f1e8838a17ffe820ff9224 100644 (file)
@@ -15,3 +15,4 @@
 /// @brief Path to D2 source dir so tests against the dhcp-ddns.spec file
 /// can find it reliably.
 #define D2_SRC_DIR "@abs_top_srcdir@/src/bin/d2"
+#define D2_TEST_DATA_DIR "@abs_top_srcdir@/src/bin/d2/tests/testdata"
diff --git a/src/bin/d2/tests/testdata/d2_cfg_tests.json b/src/bin/d2/tests/testdata/d2_cfg_tests.json
new file mode 100644 (file)
index 0000000..fb41102
--- /dev/null
@@ -0,0 +1,1273 @@
+# Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+
+# File of DHCP-DDNS configuration permutation tests
+# Each test entry consists of:
+#
+# description - text describing the test (optional)
+# should_fail - indicates whether parsing is expected to fail, defaults to
+#               false
+# data {} - Configuration text to submit for parsing.
+#
+# The vast majority of the tests in this file are invalid and are expected
+# to fail.  There are some that should succeed and are used more or less
+# as sanity checks.
+
+{ "test_list" : [
+#-----
+{
+# This test is a bit of sanity check for the "file of configs" test mechanism,
+# as well as validating this as the smallest config which makes writing
+# permutations easier.
+"description" : "D2 smallest, valid config",
+"should_fail" : false,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2 missing forward_ddns map",
+"should_fail" : true,
+"data" :
+    {
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2 missing reverse_ddns map",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+
+#-----
+,{
+"description" : "D2 missing tsig_keys list",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {}
+    }
+}
+
+#-----
+,{
+"description" : "D2 unknown scalar",
+"should_fail" : true,
+"data" :
+    {
+    "bogus_scalar" : true,
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2 unknown map",
+"should_fail" : true,
+"data" :
+    {
+    "bogus_map" : {},
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2 unknown list",
+"should_fail" : true,
+"data" :
+    {
+    "bogus_list" : [],
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#----- D2Params Test
+
+#----- D2Params.ip_address
+,{
+"description" : "D2Params.ip_address: valid v4",
+"data" :
+    {
+    "ip_address" : "192.168.0.1",
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2Params.ip_address: valid v6",
+"data" :
+    {
+    "ip_address" : "2001:db8::1",
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2Params.ip_address invalid value",
+"should_fail" : true,
+"data" :
+    {
+    "ip_address" : "bogus",
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+#-----
+}
+
+#----- D2Params.port
+,{
+"description" : "D2Params.port, valid value",
+"data" :
+    {
+    "port" : 100,
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2Params.port can't be 0",
+"should_fail" : true,
+"data" :
+    {
+    "port" : 0,
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2Params.port, non numeric",
+"should_fail" : true,
+"data" :
+    {
+    "port" : "bogus",
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#----- D2Params.dns_server_timeout
+,{
+"description" : "D2Params.dns_server_timeout, valid value",
+"data" :
+    {
+    "dns_server_timeout" : 1000,
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2Params.dns_server_timeout can't be 0",
+"should_fail" : true,
+"data" :
+    {
+    "dns_server_timeout" : 0,
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2Params.dns_server_timeout, non numeric",
+"should_fail" : true,
+"data" :
+    {
+    "dns_server_timeout" : "bogus",
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+#-----
+
+#----- D2Params.ncr_protocol
+,{
+"description" : "D2Params.ncr_protocol, valid UDP",
+"data" :
+    {
+    "ncr_protocol" : "UDP",
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2Params.ncr_protocol, unsupported TCP",
+"should_fail" : true,
+"data" :
+    {
+    "ncr_protocol" : "TCP",
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+
+#-----
+,{
+"description" : "D2Params.ncr_protocol, invalid value",
+"should_fail" : true,
+"data" :
+    {
+    "ncr_protocol" : "bogus",
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+
+#----- D2Params.ncr_format tests
+
+,{
+"description" : "D2Params.ncr_format, valid JSON",
+"data" :
+    {
+    "ncr_format" : "JSON",
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2Params.ncr_format, invalid value",
+"should_fail" : true,
+"data" :
+    {
+    "ncr_format" : "bogus",
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#----- TSIGKey Tests
+
+#-----
+,{
+# This test is a sanity check that valid TSIG entries work.
+"description" : "D2.tsig_keys, valid key",
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "d2.md5.key",
+            "algorithm" : "HMAC-MD5",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#-----
+,{
+"description" : "D2.tsig_keys, missing key name",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "algorithm" : "HMAC-MD5",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#-----
+,{
+"description" : "D2.tsig_keys, blank key name",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "",
+            "algorithm" : "HMAC-MD5",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#-----
+,{
+"description" : "D2.tsig_keys, duplicate key name",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "first.key",
+            "algorithm" : "HMAC-MD5",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+            ,{
+            "name" : "first.key",
+            "algorithm" : "HMAC-MD5",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#----- D2.tsig_keys, algorithm tests
+
+,{
+"description" : "D2.tsig_keys, all valid algorthms",
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "d2.md5.key",
+            "algorithm" : "HMAC-MD5",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            },
+            {
+            "name" : "d2.sha1.key",
+            "algorithm" : "HMAC-SHA1",
+            "secret" : "hRrp29wzUv3uzSNRLlY68w=="
+            },
+            {
+            "name" : "d2.sha224.key",
+            "algorithm" : "HMAC-SHA224",
+            "secret" : "bZEG7Ow8OgAUPfLWV3aAUQ=="
+            },
+            {
+            "name" : "d2.sha256.key",
+            "algorithm" : "hmac-sha256",
+            "secret" : "bjF4hYhTfQ5MX0siagelsw=="
+            },
+            {
+            "name" : "d2.sha384.key",
+            "algorithm" : "hmac-sha384",
+            "secret" : "Gwk53fvy3CmbupoI9TgigA=="
+            },
+            {
+            "name" : "d2.sha512.key",
+            "algorithm" : "hmac-sha512",
+            "secret" : "wP+5FqMnKXCxBWljU/BZAA=="
+            }
+        ]
+    }
+}
+
+#----- D2.tsig_keys, algorithm tests
+,{
+"description" : "D2.tsig_keys, missing algorithm",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "first.key",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#-----
+,{
+"description" : "D2.tsig_keys, blank algorithm",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "first.key",
+            "algorithm" : "",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#-----
+,{
+"description" : "D2.tsig_keys, invalid algorithm",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "first.key",
+            "algorithm" : "bogus",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#----- D2.tsig_keys, secret tests
+,{
+"description" : "D2.tsig_keys, missing secret",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "first.key",
+            "algorithm" : "HMAC-MD5"
+            }
+        ]
+    }
+}
+
+#-----
+,{
+"description" : "D2.tsig_keys, blank secret",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "first.key",
+            "algorithm" : "HMAC-MD5",
+            "secret" : ""
+            }
+        ]
+    }
+}
+
+#-----
+,{
+"description" : "D2.tsig_keys, invalid secret",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "first.key",
+            "algorithm" : "HMAC-MD5",
+            "secret" : "bogus"
+            }
+        ]
+    }
+}
+
+#----- D2.forward_ddns tests
+,{
+"description" : "D2.forward_ddns, valid, empty ddns_domains",
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" : []
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#------
+,{
+"description" : "D2.forward_ddns, unknown parameter",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "bogus" : true,
+        "ddns_domains" : []
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#------
+,{
+"description" : "D2.forward_ddns, one valid, domain",
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "four.example.com.",
+            "key_name" : "d2.md5.key",
+            "dns_servers" :
+            [
+                {
+                "ip_address" : "172.16.1.1"
+                }
+            ]
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "d2.md5.key",
+            "algorithm" : "HMAC-MD5",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#------
+,{
+"description" : "D2.forward_ddns, duplicate domain",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "four.example.com.",
+            "dns_servers" :
+            [
+                {
+                "ip_address" : "172.16.1.1"
+                }
+            ]
+            },
+            {
+            "name" : "four.example.com.",
+            "dns_servers" :
+            [
+                {
+                "ip_address" : "172.16.1.2"
+                }
+            ]
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#----- D2.forward_ddns.dhcp_ddns  tests
+,{
+"description" : "D2.forward_ddns.dhcp_ddns, unknown parameter",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "bogus" : true
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#----- D2.forward_ddns.dhcp_ddns.name tests
+,{
+"description" : "D2.forward_ddns.dhcp_ddns, no name",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.forward_ddns.dhcp_ddns, blank name",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : ""
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#------ "D2.forward_ddns.dhcp_ddns, key_name tests
+,{
+"description" : "D2.forward_ddns, no matching key name",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "four.example.com.",
+            "key_name" : "no.such.key",
+            "dns_servers" :
+            [
+                {
+                "ip_address" : "172.16.1.1"
+                }
+            ]
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" :
+        [
+            {
+            "name" : "d2.md5.key",
+            "algorithm" : "HMAC-MD5",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#----- D2.forward_ddns.dhcp_ddns.dns_servers tests
+,{
+"description" : "D2.forward_ddns.dhcp_ddns.dns_servers, no servers",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "four.example.com.",
+            "dns_servers" : []
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#----- D2.forward_ddns.dhcp_ddns.dns_servers tests
+,{
+"description" : "D2.forward_ddns.dhcp_ddns.dns_servers, unknown parameter",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "four.example.com.",
+            "dns_servers" :
+                [
+                    {
+                    "bogus" : true
+                    }
+                ]
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.forward_ddns.dhcp_ddns.dns_servers.hostname unsupported",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "four.example.com.",
+            "dns_servers" :
+                [
+                    {
+                    "hostname" : "myhost.com"
+                    }
+                ]
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.forward_ddns.dhcp_ddns.dns_servers.ip_address v4 address ",
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "four.example.com.",
+            "dns_servers" :
+                [
+                    {
+                    "ip_address" : "172.16.1.1"
+                    }
+                ]
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.forward_ddns.dhcp_ddns.dns_servers.ip_address v6 address ",
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "four.example.com.",
+            "dns_servers" :
+                [
+                    {
+                    "ip_address" : "2001:db8::1"
+                    }
+                ]
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.forward_ddns.dhcp_ddns.dns_servers.port valid value ",
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "four.example.com.",
+            "dns_servers" :
+                [
+                    {
+                    "ip_address" : "2001:db8::1",
+                    "port" : 77
+                    }
+                ]
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.forward_ddns.dhcp_ddns.dns_servers.port cannot be 0 ",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "four.example.com.",
+            "dns_servers" :
+                [
+                    {
+                    "ip_address" : "2001:db8::1",
+                    "port" : 0
+                    }
+                ]
+            }
+        ]
+    },
+    "reverse_ddns" : {},
+    "tsig_keys" : []
+    }
+}
+
+#----- D2.reverse_ddns tests
+,{
+"description" : "D2.reverse_ddns, valid, empty ddns_domains",
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" : []
+    },
+    "tsig_keys" : []
+    }
+}
+
+#------
+,{
+"description" : "D2.reverse_ddns, unknown parameter",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "bogus" : true,
+        "ddns_domains" : []
+    },
+    "tsig_keys" : []
+    }
+}
+
+#------
+,{
+"description" : "D2.reverse_ddns, one valid, domain",
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "2.0.192.in-addra.arpa.",
+            "key_name" : "d2.md5.key",
+            "dns_servers" :
+            [
+                {
+                "ip_address" : "172.16.1.1"
+                }
+            ]
+            }
+        ]
+    },
+    "tsig_keys" :
+        [
+            {
+            "name" : "d2.md5.key",
+            "algorithm" : "HMAC-MD5",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#------
+,{
+"description" : "D2.reverse_ddns, duplicate domain",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "2.0.192.in-addra.arpa.",
+            "dns_servers" :
+            [
+                {
+                "ip_address" : "172.16.1.1"
+                }
+            ]
+            },
+            {
+            "name" : "2.0.192.in-addra.arpa.",
+            "dns_servers" :
+            [
+                {
+                "ip_address" : "172.16.1.2"
+                }
+            ]
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+#----- D2.reverse_ddns.dhcp_ddns  tests
+,{
+"description" : "D2.reverse_ddns.dhcp_ddns, unknown parameter",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "bogus" : true
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+#----- D2.reverse_ddns.dhcp_ddns.name tests
+,{
+"description" : "D2.reverse_ddns.dhcp_ddns, no name",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.reverse_ddns.dhcp_ddns, blank name",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : ""
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+#------ "D2.reverse_ddns.dhcp_ddns, key_name tests
+,{
+"description" : "D2.reverse_ddns, no matching key name",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "2.0.192.in-addr.arpa.",
+            "key_name" : "no.such.key",
+            "dns_servers" :
+            [
+                {
+                "ip_address" : "172.16.1.1"
+                }
+            ]
+            }
+        ]
+    },
+    "tsig_keys" :
+        [
+            {
+            "name" : "d2.md5.key",
+            "algorithm" : "HMAC-MD5",
+            "secret" : "LSWXnfkKZjdPJI5QxlpnfQ=="
+            }
+        ]
+    }
+}
+
+#----- D2.reverse_ddns.dhcp_ddns.dns_servers tests
+,{
+"description" : "D2.reverse_ddns.dhcp_ddns.dns_servers, no servers",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "2.0.192.in-addr.arpa.",
+            "dns_servers" : []
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+#----- D2.reverse_ddns.dhcp_ddns.dns_servers tests
+,{
+"description" : "D2.reverse_ddns.dhcp_ddns.dns_servers, unknown parameter",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "2.0.192.in-addr.arpa.",
+            "dns_servers" :
+                [
+                    {
+                    "bogus" : true
+                    }
+                ]
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.reverse_ddns.dhcp_ddns.dns_servers.hostname unsupported",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "2.0.192.in-addr.arpa.",
+            "dns_servers" :
+                [
+                    {
+                    "hostname" : "myhost.com"
+                    }
+                ]
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.reverse_ddns.dhcp_ddns.dns_servers.ip_address v4 address ",
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "2.0.192.in-addr.arpa.",
+            "dns_servers" :
+                [
+                    {
+                    "ip_address" : "172.16.1.1"
+                    }
+                ]
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.reverse_ddns.dhcp_ddns.dns_servers.ip_address v6 address ",
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "2.0.192.in-addr.arpa.",
+            "dns_servers" :
+                [
+                    {
+                    "ip_address" : "2001:db8::1"
+                    }
+                ]
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.reverse_ddns.dhcp_ddns.dns_servers.port valid value ",
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "2.0.192.in-addr.arpa.",
+            "dns_servers" :
+                [
+                    {
+                    "ip_address" : "2001:db8::1",
+                    "port" : 77
+                    }
+                ]
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+#-----
+,{
+"description" : "D2.reverse_ddns.dhcp_ddns.dns_servers.port cannot be 0 ",
+"should_fail" : true,
+"data" :
+    {
+    "forward_ddns" : {},
+    "reverse_ddns" :
+    {
+        "ddns_domains" :
+        [
+            {
+            "name" : "2.0.192.in-addr.arpa.",
+            "dns_servers" :
+                [
+                    {
+                    "ip_address" : "2001:db8::1",
+                    "port" : 0
+                    }
+                ]
+            }
+        ]
+    },
+    "tsig_keys" : []
+    }
+}
+
+# ----- End of Tests
+]}