Fix sending actions

DatastoreAccess::execute functions always expect input with the prefix
deleted. This does make the test somewhat prettier, because you don't
need to repeat the prefix all the time, however, this adds complexity to
the `execute` functions, because you have to consider that. Also what
really happens inside NetconfAccess/SysrepoAccess is that it just gets
concatenated again.

The actual bug is this: when using ProxyDatastore datastore to send
RPC/action, getItems is called to get the input. This algorithm was
supposed to strip the prefixes. But there was an error in this stripping
algorithm, because stripping an "action" path is more difficult than RPC
because it can be nested.

The solution: get rid of the compacted paths in input to simplify the
algorithms.

Also, there is another bug, where the output XPaths don't get the action
prefix trimmed because the input action path is fully prefixed, but the
output XPaths aren't. More info in comment.

Change-Id: I28acaffadb55d89e508d75d58d365a3523f295bb
diff --git a/src/netconf_access.cpp b/src/netconf_access.cpp
index b5f7ccb..b599c2c 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -136,8 +136,7 @@
 {
     auto root = m_schema->dataNodeFromPath(path);
     for (const auto& [k, v] : input) {
-        auto node = m_schema->dataNodeFromPath(joinPaths(path, k), leafDataToString(v));
-        root->merge(node, 0);
+        root->new_path(m_session->libyangContext(), k.c_str(), leafDataToString(v).c_str(), LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
     }
     auto data = root->print_mem(LYD_XML, 0);
 
@@ -145,8 +144,11 @@
     auto output = m_session->rpc_or_action(data);
     if (output) {
         // If there's output, it will be a top-level node. In case of action, the output can be nested so we need to use
-        // find_path to get to the actual output.
-        lyNodesToTree(res, output->find_path(path.c_str())->data(), joinPaths(path, "/"));
+        // find_path to get to the actual output. Also, our `path` is fully prefixed, but the output paths aren't. So
+        // we use outputNode->path() to get the unprefixed path.
+
+        auto outputNode = output->find_path(path.c_str())->data().front();
+        lyNodesToTree(res, {outputNode}, joinPaths(outputNode->path(), "/"));
     }
     return res;
 }
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index c18ff31..dfdb7f0 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -308,16 +308,18 @@
 {
     auto inputNode = m_schema->dataNodeFromPath(path);
     for (const auto& [k, v] : input) {
-        auto node = m_schema->dataNodeFromPath(joinPaths(path, k), leafDataToString(v));
-        inputNode->merge(node, 0);
+        inputNode->new_path(m_session->get_context(), k.c_str(), leafDataToString(v).c_str(), LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
     }
 
     Tree res;
     auto output = m_session->rpc_send(inputNode);
     if (output) {
         // The output is "some top-level node". If we actually want the output of our RPC/action we need to use
-        // find_path.
-        lyNodesToTree(res, output->find_path(path.c_str())->data(), joinPaths(path, "/"));
+        // find_path.  Also, our `path` is fully prefixed, but the output paths aren't. So we use outputNode->path() to
+        // get the unprefixed path.
+
+        auto outputNode = output->find_path(path.c_str())->data().front();
+        lyNodesToTree(res, {outputNode}, joinPaths(outputNode->path(), "/"));
     }
     return res;
 }
diff --git a/src/yang_access.cpp b/src/yang_access.cpp
index 7a43507..b8631d5 100644
--- a/src/yang_access.cpp
+++ b/src/yang_access.cpp
@@ -131,11 +131,7 @@
     auto set = lyWrap(lyd_find_path(m_datastore.get(), path == "/" ? "/*" : path.c_str()));
     auto setWrapper = libyang::Set(set.get(), nullptr);
     std::optional<std::string> ignoredXPathPrefix;
-    if (m_datastore->schema->nodetype == LYS_RPC) {
-        auto path = std::unique_ptr<char, decltype(&free)>(lys_path(m_datastore->schema, 0), &free);
-        ignoredXPathPrefix = joinPaths(path.get(), "/");
-    }
-    lyNodesToTree(res, setWrapper.data(), ignoredXPathPrefix);
+    lyNodesToTree(res, setWrapper.data());
     return res;
 }
 
@@ -239,10 +235,8 @@
         if (v.type() == typeid(special_) && boost::get<special_>(v).m_value != SpecialValue::PresenceContainer) {
             continue;
         }
-        auto node = lyd_new_path(root.get(), m_ctx.get(), joinPaths(path, k).c_str(), (void*)leafDataToString(v).c_str(), LYD_ANYDATA_CONSTSTRING, 0);
-        if (!node) {
-            getErrorsAndThrow();
-        }
+
+        lyd_new_path(root.get(), m_ctx.get(), k.c_str(), (void*)leafDataToString(v).c_str(), LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
     }
     throw std::logic_error("in-memory datastore doesn't support executing RPC/action");
 }
diff --git a/tests/datastore_access.cpp b/tests/datastore_access.cpp
index cf92a45..000f3c7 100644
--- a/tests/datastore_access.cpp
+++ b/tests/datastore_access.cpp
@@ -961,8 +961,8 @@
             {
                 rpc = "/example-schema:launch-nukes";
                 input = {
-                    {"description", "dummy"s},
-                    {"payload/kilotons", uint64_t{333'666}},
+                    {joinPaths(rpc, "description"), "dummy"s},
+                    {joinPaths(rpc, "payload/kilotons"), uint64_t{333'666}},
                 };
                 proxyDatastore.initiate(rpc);
                 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{333'666});
@@ -973,8 +973,8 @@
             {
                 rpc = "/example-schema:launch-nukes";
                 input = {
-                    {"description", "dummy"s},
-                    {"payload/kilotons", uint64_t{4}},
+                    {joinPaths(rpc, "description"), "dummy"s},
+                    {joinPaths(rpc, "payload/kilotons"), uint64_t{4}},
                 };
                 proxyDatastore.initiate(rpc);
                 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{4});
@@ -989,8 +989,8 @@
             {
                 rpc = "/example-schema:launch-nukes";
                 input = {
-                    {"payload/kilotons", uint64_t{6}},
-                    {"cities/targets[city='Prague']/city", "Prague"s},
+                    {joinPaths(rpc, "payload/kilotons"), uint64_t{6}},
+                    {joinPaths(rpc, "cities/targets[city='Prague']/city"), "Prague"s},
                 };
                 proxyDatastore.initiate(rpc);
                 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{6});
@@ -1013,7 +1013,7 @@
 
                 rpc = "/example-schema:fire";
                 input = {
-                    {"whom", "Colton"s}
+                    {joinPaths(rpc, "whom"), "Colton"s}
                 };
                 proxyDatastore.initiate(rpc);
                 proxyDatastore.setLeaf("/example-schema:fire/example-schema:whom", "Colton"s);
@@ -1038,6 +1038,10 @@
 
     SECTION("action")
     {
+        auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
+            return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
+        };
+        ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
         std::string path;
         DatastoreAccess::Tree input, output;
 
@@ -1048,10 +1052,17 @@
         datastore->commitChanges();
         SECTION("shutdown")
         {
-            path = "/example-schema:ports[name='A']/shutdown";
+            path = "/example-schema:ports[name='A']/example-schema:shutdown";
+            input = {
+                {"/example-schema:ports[name='A']/shutdown/force", true}
+            };
+            proxyDatastore.initiate(path);
+            proxyDatastore.setLeaf("/example-schema:ports[name='A']/example-schema:shutdown/example-schema:force", true);
+
         }
 
         catching<OnExec>([&] { REQUIRE(datastore->execute(path, input) == output); });
+        catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
     }
 
     waitForCompletionAndBitMore(seq1);
diff --git a/tests/example-schema.yang b/tests/example-schema.yang
index 44baf80..6e040ae 100644
--- a/tests/example-schema.yang
+++ b/tests/example-schema.yang
@@ -206,6 +206,12 @@
         }
 
         action shutdown {
+            input {
+                leaf force {
+                    mandatory true;
+                    type boolean;
+                }
+            }
             output {
                 leaf success {
                     mandatory true;