Allow immediately executing RPC with no input

Story: https://tree.taiga.io/project/jktjkt-netconf-cli/us/189
Change-Id: I876437b798e995c4484e97b409117a2bb207e864
diff --git a/tests/interpreter.cpp b/tests/interpreter.cpp
index c6d7c9c..4ef0a96 100644
--- a/tests/interpreter.cpp
+++ b/tests/interpreter.cpp
@@ -32,6 +32,7 @@
     MAKE_CONST_MOCK1(nodeType, yang::NodeTypes(const std::string&), override);
     MAKE_CONST_MOCK2(nodeType, yang::NodeTypes(const schemaPath_&, const ModuleNodePair&), override);
     IMPLEMENT_CONST_MOCK1(status);
+    IMPLEMENT_CONST_MOCK1(hasInputNodes);
 };
 
 TEST_CASE("interpreter tests")
diff --git a/tests/parser_rpc.cpp b/tests/parser_rpc.cpp
new file mode 100644
index 0000000..c8458d8
--- /dev/null
+++ b/tests/parser_rpc.cpp
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
+ * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
+ *
+ * Written by Václav Kubernát <kubervac@fit.cvut.cz>
+ *
+*/
+
+#include "trompeloeil_doctest.hpp"
+#include "parser.hpp"
+#include "pretty_printers.hpp"
+#include "static_schema.hpp"
+
+TEST_CASE("rpc")
+{
+    auto schema = std::make_shared<StaticSchema>();
+    schema->addModule("example");
+    schema->addRpc("/", "example:fire");
+    schema->addRpc("/", "example:shutdown");
+    schema->addLeaf("/example:shutdown/input", "example:interface", yang::String{});
+    schema->addList("/", "example:port", {"name"});
+    schema->addLeaf("/example:port", "example:name", yang::String{});
+    schema->addAction("/example:port", "example:shutdown");
+    Parser parser(schema);
+    std::string input;
+    std::ostringstream errorStream;
+    SECTION("with prepare")
+    {
+        prepare_ prepExpected;
+        prepExpected.m_path.m_scope = Scope::Relative;
+        SECTION("rpc")
+        {
+            input = "prepare example:fire";
+            prepExpected.m_path.m_nodes.emplace_back(module_{"example"}, rpcNode_{"fire"});
+        }
+
+        SECTION("action")
+        {
+            input = "prepare example:port[name='eth0']/shutdown";
+            prepExpected.m_path.m_nodes.emplace_back(module_{"example"}, listElement_{"port", {{"name", std::string{"eth0"}}}});
+            prepExpected.m_path.m_nodes.emplace_back(actionNode_{"shutdown"});
+        }
+
+        command_ command = parser.parseCommand(input, errorStream);
+        auto lol = boost::get<prepare_>(command);
+        REQUIRE(boost::get<prepare_>(command) == prepExpected);
+    }
+
+    SECTION("direct exec")
+    {
+        exec_ execExpected;
+        execExpected.m_path = dataPath_{};
+        execExpected.m_path->m_scope = Scope::Relative;
+        SECTION("the rpc has no arguments")
+        {
+            input = "exec example:fire";
+            execExpected.m_path->m_nodes.emplace_back(module_{"example"}, rpcNode_{"fire"});
+            command_ command = parser.parseCommand(input, errorStream);
+            auto lol = boost::get<exec_>(command);
+            REQUIRE(boost::get<exec_>(command) == execExpected);
+        }
+
+        SECTION("the rpc has arguments")
+        {
+            input = "exec example:shutdown";
+            REQUIRE_THROWS_AS(parser.parseCommand(input, errorStream), InvalidCommandException);
+        }
+    }
+}
diff --git a/tests/prepare.cpp b/tests/prepare.cpp
deleted file mode 100644
index d56a488..0000000
--- a/tests/prepare.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-
-/*
- * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
- * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
- *
- * Written by Václav Kubernát <kubervac@fit.cvut.cz>
- *
-*/
-
-#include "trompeloeil_doctest.hpp"
-#include "parser.hpp"
-#include "pretty_printers.hpp"
-#include "static_schema.hpp"
-
-TEST_CASE("prepare command")
-{
-    auto schema = std::make_shared<StaticSchema>();
-    schema->addModule("example");
-    schema->addRpc("/", "example:fire");
-    schema->addList("/", "example:port", {"name"});
-    schema->addLeaf("/example:port", "example:name", yang::String{});
-    schema->addAction("/example:port", "example:shutdown");
-    Parser parser(schema);
-    std::string input;
-    std::ostringstream errorStream;
-    prepare_ expected;
-    expected.m_path.m_scope = Scope::Relative;
-    SECTION("rpc")
-    {
-        input = "prepare example:fire";
-        expected.m_path.m_nodes.emplace_back(module_{"example"}, rpcNode_{"fire"});
-    }
-
-    SECTION("action")
-    {
-        input = "prepare example:port[name='eth0']/shutdown";
-        expected.m_path.m_nodes.emplace_back(module_{"example"}, listElement_{"port", {{"name", std::string{"eth0"}}}});
-        expected.m_path.m_nodes.emplace_back(actionNode_{"shutdown"});
-    }
-
-    command_ command = parser.parseCommand(input, errorStream);
-    REQUIRE(command.type() == typeid(prepare_));
-    auto lol = boost::get<prepare_>(command);
-    REQUIRE(boost::get<prepare_>(command) == expected);
-}
diff --git a/tests/yang.cpp b/tests/yang.cpp
index 1ec6507..c5ae62a 100644
--- a/tests/yang.cpp
+++ b/tests/yang.cpp
@@ -317,6 +317,22 @@
 
     rpc myRpc {}
 
+    rpc rpcOneOutput {
+        output {
+            leaf ahoj {
+                type string;
+            }
+        }
+    }
+
+    rpc rpcOneInput {
+        input {
+            leaf ahoj {
+                type string;
+            }
+        }
+    }
+
     leaf numberOrString {
         type union {
             type int32;
@@ -776,6 +792,8 @@
                         {"example-schema"s, "obsoleteLeafWithDeprecatedType"},
                         {"example-schema"s, "obsoleteLeafWithObsoleteType"},
                         {"example-schema"s, "myRpc"},
+                        {"example-schema"s, "rpcOneOutput"},
+                        {"example-schema"s, "rpcOneInput"},
                         {"example-schema"s, "systemStats"},
                         {"example-schema"s, "dummyLeaf"},
                         {"example-schema"s, "addresses"},
@@ -855,6 +873,8 @@
                         {"example-schema"s, "length"},
                         {"example-schema"s, "loopback"},
                         {"example-schema"s, "myRpc"},
+                        {"example-schema"s, "rpcOneOutput"},
+                        {"example-schema"s, "rpcOneInput"},
                         {"example-schema"s, "numberOrString"},
                         {"example-schema"s, "obsoleteLeaf"},
                         {"example-schema"s, "obsoleteLeafWithDeprecatedType"},
@@ -920,6 +940,14 @@
                         {boost::none, "/example-schema:myRpc"},
                         {boost::none, "/example-schema:myRpc/input"},
                         {boost::none, "/example-schema:myRpc/output"},
+                        {boost::none, "/example-schema:rpcOneOutput"},
+                        {boost::none, "/example-schema:rpcOneOutput/input"},
+                        {boost::none, "/example-schema:rpcOneOutput/output"},
+                        {boost::none, "/example-schema:rpcOneOutput/output/ahoj"},
+                        {boost::none, "/example-schema:rpcOneInput"},
+                        {boost::none, "/example-schema:rpcOneInput/input"},
+                        {boost::none, "/example-schema:rpcOneInput/input/ahoj"},
+                        {boost::none, "/example-schema:rpcOneInput/output"},
                         {boost::none, "/example-schema:numberOrString"},
                         {boost::none, "/example-schema:obsoleteLeaf"},
                         {boost::none, "/example-schema:obsoleteLeafWithDeprecatedType"},
@@ -1135,6 +1163,30 @@
             REQUIRE(ys.dataPathToSchemaPath("/example-schema:portSettings[port='eth0']") == "/example-schema:portSettings");
             REQUIRE(ys.dataPathToSchemaPath("/example-schema:portSettings[port='eth0']/shutdown") == "/example-schema:portSettings/shutdown");
         }
+
+        SECTION("has input nodes")
+        {
+            bool expected;
+            SECTION("example-schema:myRpc")
+            {
+                path.m_nodes.emplace_back(module_{"example-schema"}, rpcNode_{"myRpc"});
+                expected = false;
+            }
+
+            SECTION("example-schema:rpcOneInput")
+            {
+                path.m_nodes.emplace_back(module_{"example-schema"}, rpcNode_{"rpcOneInput"});
+                expected = true;
+            }
+
+            SECTION("example-schema:rpcOneOutput")
+            {
+                path.m_nodes.emplace_back(module_{"example-schema"}, rpcNode_{"rpcOneOutput"});
+                expected = false;
+            }
+
+            REQUIRE(ys.hasInputNodes(pathToSchemaString(path, Prefixes::WhenNeeded)) == expected);
+        }
     }
 
     SECTION("negative")