tests: Accessing non-existing schema nodes
This is really hilarious because apparently sysrepo's behavior isn't
terribly consistent. So I decided to make the maybe-except-exception
wrapper a tad more generic.
Of course there was an ugly error because now that I'm templating on the
expected exception, I cannot use REQUIRE_THROWS_AS because that one is
written with an implicit assumption that the exception name is not a
type-dependent name. This means that (AFAIK) I have to enumerate all of
these possible exception types explicitly.
The final catch was that type-dependent static assert. After that 💩
everything was 🌈 and I felt 🥰 and saw nothing but 🦄.
Change-Id: I8862b50bf46cd156e71c83640d9d668989b4db9d
diff --git a/tests/datastore_access.cpp b/tests/datastore_access.cpp
index bab7fac..2837c84 100644
--- a/tests/datastore_access.cpp
+++ b/tests/datastore_access.cpp
@@ -10,12 +10,16 @@
#include <sysrepo-cpp/Session.hpp>
#ifdef sysrepo_BACKEND
-#define THROWS_ON_INVALID_SCHEMA_PATHS 0
-#define THROWS_ON_NONEXISTING_KEYS 0
#include "sysrepo_access.hpp"
+using OnInvalidSchemaPathCreate = DatastoreException;
+using OnInvalidSchemaPathDelete = void;
+using OnInvalidSchemaPathMove = sysrepo::sysrepo_exception;
+using OnKeyNotFound = void;
#elif defined(netconf_BACKEND)
-#define THROWS_ON_INVALID_SCHEMA_PATHS 1
-#define THROWS_ON_NONEXISTING_KEYS 1
+using OnInvalidSchemaPathCreate = std::runtime_error;
+using OnInvalidSchemaPathDelete = std::runtime_error;
+using OnInvalidSchemaPathMove = std::runtime_error;
+using OnKeyNotFound = std::runtime_error;
#include "netconf_access.hpp"
#include "netopeer_vars.hpp"
#else
@@ -37,13 +41,25 @@
IMPLEMENT_CONST_MOCK1(get_data);
};
-template <int Flag, typename Callable> void tryThis(const Callable& what) {
- if constexpr (Flag) {
- REQUIRE_THROWS_AS(what(), std::runtime_error);
- } else {
+namespace {
+template <class ...> constexpr std::false_type always_false [[maybe_unused]] {};
+template <class Exception, typename Callable> void catching(const Callable& what) {
+
+ if constexpr (std::is_same_v<Exception, void>) {
what();
+ } else if constexpr (std::is_same<Exception, std::runtime_error>()) {
+ // cannot use REQUIRE_THROWS_AS(..., Exception) directly because that one
+ // needs an extra `typename` deep in the bowels of doctest
+ REQUIRE_THROWS_AS(what(), std::runtime_error);
+ } else if constexpr (std::is_same<Exception, DatastoreException>()) {
+ REQUIRE_THROWS_AS(what(), DatastoreException);
+ } else if constexpr (std::is_same<Exception, sysrepo::sysrepo_exception>()) {
+ REQUIRE_THROWS_AS(what(), sysrepo::sysrepo_exception);
+ } else {
+ static_assert(always_false<Exception>); // https://stackoverflow.com/a/53945549/2245623
}
}
+}
TEST_CASE("setting/getting values")
{
@@ -130,6 +146,13 @@
datastore.commitChanges();
}
+ SECTION("set a non-existing leaf")
+ {
+ catching<OnInvalidSchemaPathCreate>([&]{
+ datastore.setLeaf("/example-schema:non-existing", "what"s);
+ });
+ }
+
SECTION("create presence container")
{
REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
@@ -155,15 +178,19 @@
SECTION("deleting non-existing list keys")
{
- tryThis<THROWS_ON_NONEXISTING_KEYS>([&]{
+ catching<OnKeyNotFound>([&]{
datastore.deleteItem("/example-schema:person[name='non existing']");
datastore.commitChanges();
});
}
- SECTION("deleting non-existing schema nodes as a list")
+ SECTION("accessing non-existing schema nodes as a list")
{
- tryThis<THROWS_ON_INVALID_SCHEMA_PATHS>([&]{
+ catching<OnInvalidSchemaPathCreate>([&]{
+ datastore.createItem("/example-schema:non-existing-list[xxx='blah']");
+ datastore.commitChanges();
+ });
+ catching<OnInvalidSchemaPathDelete>([&]{
datastore.deleteItem("/example-schema:non-existing-list[xxx='non existing']");
datastore.commitChanges();
});
@@ -312,9 +339,17 @@
REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
}
+ SECTION("creating a non-existing schema node as a container")
+ {
+ catching<OnInvalidSchemaPathCreate>([&]{
+ datastore.createItem("/example-schema:non-existing-presence-container");
+ datastore.commitChanges();
+ });
+ }
+
SECTION("deleting a non-existing schema node as a container or leaf")
{
- tryThis<THROWS_ON_INVALID_SCHEMA_PATHS>([&]{
+ catching<OnInvalidSchemaPathDelete>([&]{
datastore.deleteItem("/example-schema:non-existing-presence-container");
datastore.commitChanges();
});
@@ -456,15 +491,20 @@
SECTION("deleting a non-existing leaf-list")
{
- tryThis<THROWS_ON_NONEXISTING_KEYS>([&]{
+ catching<OnKeyNotFound>([&]{
datastore.deleteItem("/example-schema:addresses[.='non-existing']");
datastore.commitChanges();
});
}
- SECTION("deleting a non-existing schema node as a leaf-list")
+ SECTION("accessing a non-existing schema node as a leaf-list")
{
- tryThis<THROWS_ON_INVALID_SCHEMA_PATHS>([&]{
+ catching<OnInvalidSchemaPathCreate>([&]{
+ datastore.createItem("/example-schema:non-existing[.='non-existing']");
+ datastore.commitChanges();
+ });
+
+ catching<OnInvalidSchemaPathDelete>([&]{
datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
datastore.commitChanges();
});
@@ -569,6 +609,14 @@
}
}
+ SECTION("moving non-existing schema nodes")
+ {
+ catching<OnInvalidSchemaPathMove>([&]{
+ datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
+ datastore.commitChanges();
+ });
+ }
+
SECTION("moving list instances")
{
DatastoreAccess::Tree expected;
@@ -757,52 +805,60 @@
#error "Unknown backend"
#endif
- std::string rpc;
- DatastoreAccess::Tree input, output;
+ SECTION("valid")
+ {
+ std::string rpc;
+ DatastoreAccess::Tree input, output;
- SECTION("noop") {
- rpc = "/example-schema:noop";
+ SECTION("noop") {
+ rpc = "/example-schema:noop";
+ }
+
+ SECTION("small nuke") {
+ rpc = "/example-schema:launch-nukes";
+ input = {
+ {"description", "dummy"s},
+ {"payload/kilotons", uint64_t{333'666}},
+ };
+ // no data are returned
+ }
+
+ SECTION("small nuke") {
+ rpc = "/example-schema:launch-nukes";
+ input = {
+ {"description", "dummy"s},
+ {"payload/kilotons", uint64_t{4}},
+ };
+ output = {
+ {"blast-radius", uint32_t{33'666}},
+ {"actual-yield", uint64_t{5}},
+ };
+ }
+
+ SECTION("with lists") {
+ rpc = "/example-schema:launch-nukes";
+ input = {
+ {"payload/kilotons", uint64_t{6}},
+ {"cities/targets[city='Prague']/city", "Prague"s},
+ };
+ output = {
+ {"blast-radius", uint32_t{33'666}},
+ {"actual-yield", uint64_t{7}},
+ {"damaged-places", special_{SpecialValue::PresenceContainer}},
+ {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
+ {"damaged-places/targets[city='London']/city", "London"s},
+ {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
+ {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
+ };
+ }
+
+ REQUIRE(datastore.executeRpc(rpc, input) == output);
}
- SECTION("small nuke") {
- rpc = "/example-schema:launch-nukes";
- input = {
- {"description", "dummy"s},
- {"payload/kilotons", uint64_t{333'666}},
- };
- // no data are returned
+ SECTION("non-existing RPC")
+ {
+ REQUIRE_THROWS_AS(datastore.executeRpc("/example-schema:non-existing", DatastoreAccess::Tree{}), std::runtime_error);
}
- SECTION("small nuke") {
- rpc = "/example-schema:launch-nukes";
- input = {
- {"description", "dummy"s},
- {"payload/kilotons", uint64_t{4}},
- };
- output = {
- {"blast-radius", uint32_t{33'666}},
- {"actual-yield", uint64_t{5}},
- };
- }
-
- SECTION("with lists") {
- rpc = "/example-schema:launch-nukes";
- input = {
- {"payload/kilotons", uint64_t{6}},
- {"cities/targets[city='Prague']/city", "Prague"s},
- };
- output = {
- {"blast-radius", uint32_t{33'666}},
- {"actual-yield", uint64_t{7}},
- {"damaged-places", special_{SpecialValue::PresenceContainer}},
- {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
- {"damaged-places/targets[city='London']/city", "London"s},
- {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
- {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
- };
- }
-
- REQUIRE(datastore.executeRpc(rpc, input) == output);
-
waitForCompletionAndBitMore(seq1);
}