Add support for executing RPCs

Creating a temporary YangAccess for RPC input means I need to somehow
give the right libyang schemas. For that reason I supply a callable
which is able to fetch the schema and create a YangAccess instance for
ProxyDatastore.

The ProxyDatastore class now has a simple mechanism for deciding whether
to use the normal datastore and the temporary based on a path prefix.

Change-Id: Ib455f53237598bc2620161a44fb89c48ddfeb6e3
diff --git a/tests/interpreter.cpp b/tests/interpreter.cpp
index 89fbd29..a88aa48 100644
--- a/tests/interpreter.cpp
+++ b/tests/interpreter.cpp
@@ -39,7 +39,11 @@
     auto schema = std::make_shared<MockSchema>();
     Parser parser(schema);
     auto datastore = std::make_shared<MockDatastoreAccess>();
-    ProxyDatastore proxyDatastore(datastore);
+    auto input_datastore = std::make_shared<MockDatastoreAccess>();
+    auto createTemporaryDatastore = [input_datastore]([[maybe_unused]] const std::shared_ptr<DatastoreAccess>& datastore) {
+        return input_datastore;
+    };
+    ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
     std::vector<std::unique_ptr<trompeloeil::expectation>> expectations;
 
     std::vector<command_> toInterpret;
@@ -422,3 +426,44 @@
         boost::apply_visitor(Interpreter(parser, proxyDatastore), command);
     }
 }
+
+TEST_CASE("rpc")
+{
+    auto schema = std::make_shared<MockSchema>();
+    Parser parser(schema);
+    auto datastore = std::make_shared<MockDatastoreAccess>();
+    auto input_datastore = std::make_shared<MockDatastoreAccess>();
+    auto createTemporaryDatastore = [input_datastore]([[maybe_unused]] const std::shared_ptr<DatastoreAccess>& datastore) {
+        return input_datastore;
+    };
+    ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
+
+    SECTION("entering/leaving rpc context")
+    {
+        dataPath_ rpcPath;
+        rpcPath.pushFragment({{"example"}, rpcNode_{"launch-nukes"}});
+        rpc_ rpcCmd;
+        rpcCmd.m_path = rpcPath;
+
+        {
+            REQUIRE_CALL(*input_datastore, createItem("/example:launch-nukes"));
+            boost::apply_visitor(Interpreter(parser, proxyDatastore), command_{rpcCmd});
+        }
+
+        REQUIRE(parser.currentPath() == rpcPath);
+
+        SECTION("exec")
+        {
+            REQUIRE_CALL(*input_datastore, getItems("/")).RETURN(DatastoreAccess::Tree{});
+            REQUIRE_CALL(*datastore, executeRpc("/example:launch-nukes", DatastoreAccess::Tree{})).RETURN(DatastoreAccess::Tree{});
+            boost::apply_visitor(Interpreter(parser, proxyDatastore), command_{exec_{}});
+        }
+
+        SECTION("cancel")
+        {
+            boost::apply_visitor(Interpreter(parser, proxyDatastore), command_{cancel_{}});
+        }
+
+        REQUIRE(parser.currentPath() == dataPath_{});
+    }
+}