Merge "Update dependencies"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 53dda3b..779b968 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -317,7 +317,7 @@
         set_property(TEST ${TESTNAME} ${TESTNAME}_init ${TESTNAME}_cleanup APPEND PROPERTY ENVIRONMENT
             "SYSREPO_REPOSITORY_PATH=${CMAKE_CURRENT_BINARY_DIR}/test_repositories/${TESTNAME}"
             "SYSREPO_SHM_PREFIX=netconf-cli_${TESTNAME}"
-            "NETOPEER_SOCKET=${CMAKE_CURRENT_BINARY_DIR}/test_netopeer_files/${TESTNAME}.sock"
+            "NETOPEER_SOCKET=test_netopeer_files/${TESTNAME}.sock"
             )
 
     endfunction()
@@ -386,7 +386,7 @@
         set_property(TEST test_netconf_cli_py test_netconf_cli_py_init test_netconf_cli_py_cleanup APPEND PROPERTY ENVIRONMENT
             "SYSREPO_REPOSITORY_PATH=${CMAKE_CURRENT_BINARY_DIR}/test_repositories/test_netconf_cli_py"
             "SYSREPO_SHM_PREFIX=netconf-cli_test_netconf_cli_py"
-            "NETOPEER_SOCKET=${CMAKE_CURRENT_BINARY_DIR}/test_netopeer_files/test_netconf_cli_py.sock"
+            "NETOPEER_SOCKET=test_netopeer_files/test_netconf_cli_py.sock"
             )
 
         set(sanitizer_active OFF)
diff --git a/src/cli.cpp b/src/cli.cpp
index a2c9cdc..271d9a5 100644
--- a/src/cli.cpp
+++ b/src/cli.cpp
@@ -63,7 +63,7 @@
 )";
 #include "cli-netconf.hpp"
 #include "netconf_access.hpp"
-#define PROGRAM_NAME "netconf-access"
+#define PROGRAM_NAME "netconf-cli"
 // FIXME: this should be replaced by C++20 std::jthread at some point
 struct PoorMansJThread {
     ~PoorMansJThread()
diff --git a/src/datastore_access.hpp b/src/datastore_access.hpp
index 057c96a..b29e152 100644
--- a/src/datastore_access.hpp
+++ b/src/datastore_access.hpp
@@ -50,8 +50,7 @@
     virtual void createItem(const std::string& path) = 0;
     virtual void deleteItem(const std::string& path) = 0;
     virtual void moveItem(const std::string& path, std::variant<yang::move::Absolute, yang::move::Relative> move) = 0;
-    virtual Tree executeRpc(const std::string& path, const Tree& input) = 0;
-    virtual Tree executeAction(const std::string& path, const Tree& input) = 0;
+    virtual Tree execute(const std::string& path, const Tree& input) = 0;
 
     virtual std::shared_ptr<Schema> schema() = 0;
 
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 02da179..7cc8de6 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -137,7 +137,7 @@
     copy_::name > space_separator > copy_args;
 
 auto const describe_def =
-    describe_::name >> space_separator > (dataPathListEnd | anyPath);
+    describe_::name >> space_separator > anyPath;
 
 struct mode_table : x3::symbols<MoveMode> {
     mode_table()
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index f1682bf..9046b64 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -127,6 +127,7 @@
 std::string Interpreter::buildTypeInfo(const std::string& path) const
 {
     std::ostringstream ss;
+    std::string typeDescription;
     switch (m_datastore.schema()->nodeType(path)) {
     case yang::NodeTypes::Container:
         ss << "container";
@@ -156,6 +157,10 @@
             ss << " [" + *leafType.m_units + "]";
         }
 
+        if (leafType.m_description) {
+            typeDescription = "\nType description: " + *leafType.m_description;
+        }
+
         if (m_datastore.schema()->leafIsKey(path)) {
             ss << " (key)";
         }
@@ -184,24 +189,29 @@
     }
 
     if (!m_datastore.schema()->isConfig(path)) {
-        ss << " (ro)";
+        ss << " (ro)\n";
     }
 
-    return ss.str();
-}
-
-void Interpreter::operator()(const describe_& describe) const
-{
-    auto path = pathToString(toCanonicalPath(describe.m_path));
     auto status = m_datastore.schema()->status(path);
     auto statusStr = status == yang::Status::Deprecated ? " (deprecated)" :
         status == yang::Status::Obsolete ? " (obsolete)" :
         "";
 
-    std::cout << path << ": " << buildTypeInfo(path) << statusStr << std::endl;
+    ss << statusStr;
+
     if (auto description = m_datastore.schema()->description(path)) {
-        std::cout << std::endl << *description << std::endl;
+        ss << std::endl << *description << std::endl;
     }
+
+    ss << typeDescription;
+    return ss.str();
+}
+
+void Interpreter::operator()(const describe_& describe) const
+{
+    auto fullPath = pathToString(toCanonicalPath(describe.m_path));
+
+    std::cout << pathToString(describe.m_path) << ": " << buildTypeInfo(fullPath) << std::endl;
 }
 
 void Interpreter::operator()(const move_& move) const
@@ -216,11 +226,7 @@
 
 void Interpreter::operator()(const prepare_& prepare) const
 {
-    if (std::holds_alternative<rpcNode_>(prepare.m_path.m_nodes.back().m_suffix)) {
-        m_datastore.initiateRpc(pathToString(toCanonicalPath(prepare.m_path)));
-    } else {
-        m_datastore.initiateAction(pathToString(toCanonicalPath(prepare.m_path)));
-    }
+    m_datastore.initiate(pathToString(toCanonicalPath(prepare.m_path)));
     m_parser.changeNode(prepare.m_path);
 }
 
diff --git a/src/leaf_data.hpp b/src/leaf_data.hpp
index 7bab723..df0f9b3 100644
--- a/src/leaf_data.hpp
+++ b/src/leaf_data.hpp
@@ -162,6 +162,7 @@
             parser.add(bit, bit);
             parserContext.m_suggestions.insert(Completion{bit});
         }
+        parserContext.m_completionIterator = first;
 
         std::vector<std::string> bitsRes;
 
diff --git a/src/leaf_data_type.cpp b/src/leaf_data_type.cpp
index fe6fe04..f97e382 100644
--- a/src/leaf_data_type.cpp
+++ b/src/leaf_data_type.cpp
@@ -10,11 +10,12 @@
 namespace yang {
 bool TypeInfo::operator==(const TypeInfo& other) const
 {
-    return this->m_type == other.m_type && this->m_units == other.m_units;
+    return std::tie(this->m_type, this->m_units, this->m_description) == std::tie(other.m_type, other.m_units, other.m_description);
 }
-TypeInfo::TypeInfo(const yang::LeafDataType& type, const std::optional<std::string> units)
+TypeInfo::TypeInfo(const yang::LeafDataType& type, const std::optional<std::string> units, const std::optional<std::string> description)
     : m_type(type)
     , m_units(units)
+    , m_description(description)
 {
 }
 Enum::Enum(std::set<enum_>&& values)
diff --git a/src/leaf_data_type.hpp b/src/leaf_data_type.hpp
index 98cb12e..35a2961 100644
--- a/src/leaf_data_type.hpp
+++ b/src/leaf_data_type.hpp
@@ -105,9 +105,12 @@
     std::vector<TypeInfo> m_unionTypes;
 };
 struct TypeInfo {
-    TypeInfo(const yang::LeafDataType& type, const std::optional<std::string> units = std::nullopt);
+    TypeInfo(const yang::LeafDataType& type,
+            const std::optional<std::string> units = std::nullopt,
+            const std::optional<std::string> description = std::nullopt);
     bool operator==(const TypeInfo& other) const;
     yang::LeafDataType m_type;
     std::optional<std::string> m_units;
+    std::optional<std::string> m_description;
 };
 }
diff --git a/src/netconf_access.cpp b/src/netconf_access.cpp
index 1e56659..4ed29ae 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -132,7 +132,7 @@
     m_session->discard();
 }
 
-DatastoreAccess::Tree NetconfAccess::impl_execute(const std::string& path, const Tree& input)
+DatastoreAccess::Tree NetconfAccess::execute(const std::string& path, const Tree& input)
 {
     auto root = m_schema->dataNodeFromPath(path);
     for (const auto& [k, v] : input) {
@@ -149,16 +149,6 @@
     return res;
 }
 
-DatastoreAccess::Tree NetconfAccess::executeRpc(const std::string& path, const Tree& input)
-{
-    return impl_execute(path, input);
-}
-
-DatastoreAccess::Tree NetconfAccess::executeAction(const std::string& path, const Tree& input)
-{
-    return impl_execute(path, input);
-}
-
 NC_DATASTORE toNcDatastore(Datastore datastore)
 {
     switch (datastore) {
diff --git a/src/netconf_access.hpp b/src/netconf_access.hpp
index 45973a6..c54434d 100644
--- a/src/netconf_access.hpp
+++ b/src/netconf_access.hpp
@@ -47,8 +47,7 @@
     void moveItem(const std::string& path, std::variant<yang::move::Absolute, yang::move::Relative> move) override;
     void commitChanges() override;
     void discardChanges() override;
-    Tree executeRpc(const std::string& path, const Tree& input) override;
-    Tree executeAction(const std::string& path, const Tree& input) override;
+    Tree execute(const std::string& path, const Tree& input) override;
     void copyConfig(const Datastore source, const Datastore destination) override;
 
     std::shared_ptr<Schema> schema() override;
@@ -57,14 +56,12 @@
 
 private:
     std::vector<ListInstance> listInstances(const std::string& path) override;
-    DatastoreAccess::Tree impl_execute(const std::string& path, const Tree& input);
 
     std::string fetchSchema(const std::string_view module, const
             std::optional<std::string_view> revision, const
             std::optional<std::string_view> submodule, const
             std::optional<std::string_view> submoduleRevision);
     std::vector<std::string> listImplementedSchemas();
-    void datastoreInit();
     void doEditFromDataNode(std::shared_ptr<libyang::Data_Node> dataNode);
 
     std::unique_ptr<libnetconf::client::Session> m_session;
diff --git a/src/path_parser.hpp b/src/path_parser.hpp
index fca760d..58d174e 100644
--- a/src/path_parser.hpp
+++ b/src/path_parser.hpp
@@ -76,10 +76,8 @@
     {
     }
 
-    // GCC complains that `end` isn't used when doing completions only
-    // FIXME: GCC 10.1 doesn't emit a warning here. Remove [[maybe_unused]] when GCC 10 is available
     template <typename It, typename Ctx, typename RCtx, typename Attr>
-    bool parse(It& begin, [[maybe_unused]] It end, Ctx const& ctx, RCtx& rctx, Attr& attr) const
+    bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, Attr& attr) const
     {
         std::string tableName;
         if constexpr (std::is_same<attribute_type, schemaNode_>()) {
@@ -165,12 +163,7 @@
         if constexpr (PARSER_MODE == NodeParserMode::CompletionsOnly) {
             return true;
         } else {
-            It saveIter;
-            // GCC complains that I assign saveIter because I use it only if NodeType is dataNode_
-            // FIXME: GCC 10.1 doesn't emit a warning here. Make this unconditional when GCC 10 is available.
-            if constexpr (std::is_same<attribute_type, dataNode_>()) {
-                saveIter = begin;
-            }
+            It saveIter = begin;
 
             auto res = table.parse(begin, end, ctx, rctx, attr);
 
diff --git a/src/proxy_datastore.cpp b/src/proxy_datastore.cpp
index 113a923..ef3c5db 100644
--- a/src/proxy_datastore.cpp
+++ b/src/proxy_datastore.cpp
@@ -59,34 +59,14 @@
     return m_datastore->dump(format);
 }
 
-namespace {
-struct getInputPath {
-    template <typename InputType>
-    auto operator()(const InputType& input)
-    {
-        return input.m_path;
-    }
-};
-}
-
-void ProxyDatastore::initiateRpc(const std::string& rpcPath)
+void ProxyDatastore::initiate(const std::string& path)
 {
     if (m_inputDatastore) {
-        throw std::runtime_error("RPC/action input already in progress (" + std::visit(getInputPath{}, m_inputPath) + ")");
+        throw std::runtime_error("RPC/action input already in progress (" + m_inputPath + ")");
     }
     m_inputDatastore = m_createTemporaryDatastore(m_datastore);
-    m_inputPath = RpcInput{rpcPath};
-    m_inputDatastore->createItem(rpcPath);
-}
-
-void ProxyDatastore::initiateAction(const std::string& actionPath)
-{
-    if (m_inputDatastore) {
-        throw std::runtime_error("RPC/action input already in progress (" + std::visit(getInputPath{}, m_inputPath) + ")");
-    }
-    m_inputDatastore = m_createTemporaryDatastore(m_datastore);
-    m_inputPath = ActionInput{actionPath};
-    m_inputDatastore->createItem(actionPath);
+    m_inputPath = path;
+    m_inputDatastore->createItem(path);
 }
 
 DatastoreAccess::Tree ProxyDatastore::execute()
@@ -96,11 +76,7 @@
     }
     auto inputData = m_inputDatastore->getItems("/");
     m_inputDatastore = nullptr;
-    if (std::holds_alternative<RpcInput>(m_inputPath)) {
-        return m_datastore->executeRpc(std::visit(getInputPath{}, m_inputPath), inputData);
-    } else {
-        return m_datastore->executeAction(std::visit(getInputPath{}, m_inputPath), inputData);
-    }
+    return m_datastore->execute(m_inputPath, inputData);
 }
 
 void ProxyDatastore::cancel()
@@ -115,7 +91,7 @@
 
 std::shared_ptr<DatastoreAccess> ProxyDatastore::pickDatastore(const std::string& path) const
 {
-    if (!m_inputDatastore || !boost::starts_with(path, std::visit(getInputPath{}, m_inputPath))) {
+    if (!m_inputDatastore || !boost::starts_with(path, m_inputPath)) {
         return m_datastore;
     } else {
         return m_inputDatastore;
diff --git a/src/proxy_datastore.hpp b/src/proxy_datastore.hpp
index e7650cd..ab7e3a2 100644
--- a/src/proxy_datastore.hpp
+++ b/src/proxy_datastore.hpp
@@ -29,8 +29,7 @@
     void copyConfig(const Datastore source, const Datastore destination);
     [[nodiscard]] std::string dump(const DataFormat format) const;
 
-    void initiateRpc(const std::string& rpcPath);
-    void initiateAction(const std::string& actionPath);
+    void initiate(const std::string& path);
     [[nodiscard]] DatastoreAccess::Tree execute();
     void cancel();
 
@@ -48,14 +47,5 @@
     std::function<std::shared_ptr<DatastoreAccess>(const std::shared_ptr<DatastoreAccess>&)> m_createTemporaryDatastore;
     std::shared_ptr<DatastoreAccess> m_inputDatastore;
 
-    struct RpcInput {
-        std::string m_path;
-    };
-
-    struct ActionInput {
-        std::string m_path;
-    };
-    // This variant is needed, so that I know whether to call executeRpc or executeAction
-    // TODO: get rid of this variant with sysrepo2 because the method for RPC/action is the same there
-    std::variant<ActionInput, RpcInput> m_inputPath;
+    std::string m_inputPath;
 };
diff --git a/src/python_netconf.cpp b/src/python_netconf.cpp
index 7a0e4d3..c6e3c44 100644
--- a/src/python_netconf.cpp
+++ b/src/python_netconf.cpp
@@ -70,7 +70,7 @@
             .def("setLeaf", &NetconfAccess::setLeaf, "xpath"_a, "value"_a)
             .def("createItem", &NetconfAccess::createItem, "xpath"_a)
             .def("deleteItem", &NetconfAccess::deleteItem, "xpath"_a)
-            .def("executeRpc", &NetconfAccess::executeRpc, "rpc"_a, "input"_a=DatastoreAccess::Tree{})
+            .def("execute", &NetconfAccess::execute, "rpc"_a, "input"_a=DatastoreAccess::Tree{})
             .def("commitChanges", &NetconfAccess::commitChanges)
             ;
 }
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index 7c21cad..21eca7e 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -329,15 +329,7 @@
 }
 }
 
-// TODO: merge this with executeAction
-DatastoreAccess::Tree SysrepoAccess::executeRpc(const std::string& path, const Tree& input)
-{
-    auto srInput = toSrVals(path, input);
-    auto output = m_session->rpc_send(path.c_str(), srInput);
-    return toTree(path, output);
-}
-
-DatastoreAccess::Tree SysrepoAccess::executeAction(const std::string& path, const Tree& input)
+DatastoreAccess::Tree SysrepoAccess::execute(const std::string& path, const Tree& input)
 {
     auto srInput = toSrVals(path, input);
     auto output = m_session->rpc_send(path.c_str(), srInput);
diff --git a/src/sysrepo_access.hpp b/src/sysrepo_access.hpp
index d0ad869..55e8ac6 100644
--- a/src/sysrepo_access.hpp
+++ b/src/sysrepo_access.hpp
@@ -33,8 +33,7 @@
     void createItem(const std::string& path) override;
     void deleteItem(const std::string& path) override;
     void moveItem(const std::string& source, std::variant<yang::move::Absolute, yang::move::Relative> move) override;
-    Tree executeRpc(const std::string& path, const Tree& input) override;
-    Tree executeAction(const std::string& path, const Tree& input) override;
+    Tree execute(const std::string& path, const Tree& input) override;
 
     std::shared_ptr<Schema> schema() override;
 
diff --git a/src/yang_access.cpp b/src/yang_access.cpp
index a460150..7a43507 100644
--- a/src/yang_access.cpp
+++ b/src/yang_access.cpp
@@ -229,7 +229,7 @@
 {
 }
 
-[[noreturn]] void YangAccess::impl_execute(const std::string& type, const std::string& path, const Tree& input)
+[[noreturn]] DatastoreAccess::Tree YangAccess::execute(const std::string& path, const Tree& input)
 {
     auto root = lyWrap(lyd_new_path(nullptr, m_ctx.get(), path.c_str(), nullptr, LYD_ANYDATA_CONSTSTRING, 0));
     if (!root) {
@@ -244,17 +244,7 @@
             getErrorsAndThrow();
         }
     }
-    throw std::logic_error("in-memory datastore doesn't support executing " + type + "s");
-}
-
-DatastoreAccess::Tree YangAccess::executeRpc(const std::string& path, const Tree& input)
-{
-    impl_execute("RPC", path, input);
-}
-
-DatastoreAccess::Tree YangAccess::executeAction(const std::string& path, const Tree& input)
-{
-    impl_execute("action", path, input);
+    throw std::logic_error("in-memory datastore doesn't support executing RPC/action");
 }
 
 void YangAccess::copyConfig(const Datastore source, const Datastore dest)
diff --git a/src/yang_access.hpp b/src/yang_access.hpp
index 62c43d6..c009420 100644
--- a/src/yang_access.hpp
+++ b/src/yang_access.hpp
@@ -30,8 +30,7 @@
     void moveItem(const std::string& source, std::variant<yang::move::Absolute, yang::move::Relative> move) override;
     void commitChanges() override;
     void discardChanges() override;
-    Tree executeRpc(const std::string& path, const Tree& input) override;
-    Tree executeAction(const std::string& path, const Tree& input) override;
+    [[noreturn]] Tree execute(const std::string& path, const Tree& input) override;
     void copyConfig(const Datastore source, const Datastore destination) override;
 
     std::shared_ptr<Schema> schema() override;
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 7f27711..7e5bb9f 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -284,7 +284,19 @@
             }
         }
 
-        return yang::TypeInfo(resType, resUnits);
+        std::optional<std::string> resDescription;
+
+        // checking for parentTypedef->type()->der() means I'm going to enter inside base types like "string". These
+        // also have a description, but it isn't too helpful ("human-readable string")
+        for (auto parentTypedef = type->der(); parentTypedef && parentTypedef->type()->der(); parentTypedef = parentTypedef->type()->der()) {
+            auto dsc = parentTypedef->dsc();
+            if (dsc) {
+                resDescription = dsc;
+                break;
+            }
+        }
+
+        return yang::TypeInfo(resType, resUnits, resDescription);
     };
     return resolveType(leaf->type());
 }
diff --git a/tests/datastore_access.cpp b/tests/datastore_access.cpp
index 43d6038..ddfe288 100644
--- a/tests/datastore_access.cpp
+++ b/tests/datastore_access.cpp
@@ -951,7 +951,7 @@
             SECTION("noop")
             {
                 rpc = "/example-schema:noop";
-                proxyDatastore.initiateRpc(rpc);
+                proxyDatastore.initiate(rpc);
             }
 
             SECTION("small nuke")
@@ -961,7 +961,7 @@
                     {"description", "dummy"s},
                     {"payload/kilotons", uint64_t{333'666}},
                 };
-                proxyDatastore.initiateRpc(rpc);
+                proxyDatastore.initiate(rpc);
                 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{333'666});
                 // no data are returned
             }
@@ -973,7 +973,7 @@
                     {"description", "dummy"s},
                     {"payload/kilotons", uint64_t{4}},
                 };
-                proxyDatastore.initiateRpc(rpc);
+                proxyDatastore.initiate(rpc);
                 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{4});
 
                 output = {
@@ -989,7 +989,7 @@
                     {"payload/kilotons", uint64_t{6}},
                     {"cities/targets[city='Prague']/city", "Prague"s},
                 };
-                proxyDatastore.initiateRpc(rpc);
+                proxyDatastore.initiate(rpc);
                 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{6});
                 proxyDatastore.createItem("/example-schema:launch-nukes/example-schema:cities/example-schema:targets[city='Prague']");
                 output = {
@@ -1012,17 +1012,17 @@
                 input = {
                     {"whom", "Colton"s}
                 };
-                proxyDatastore.initiateRpc(rpc);
+                proxyDatastore.initiate(rpc);
                 proxyDatastore.setLeaf("/example-schema:fire/example-schema:whom", "Colton"s);
             }
 
-            catching<OnExec>([&] { REQUIRE(datastore->executeRpc(rpc, input) == output); });
+            catching<OnExec>([&] { REQUIRE(datastore->execute(rpc, input) == output); });
             catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
         }
 
         SECTION("non-existing RPC")
         {
-            catching<OnInvalidRpcPath>([&] { datastore->executeRpc("/example-schema:non-existing", DatastoreAccess::Tree{}); });
+            catching<OnInvalidRpcPath>([&] { datastore->execute("/example-schema:non-existing", DatastoreAccess::Tree{}); });
         }
     }
 
@@ -1045,7 +1045,7 @@
             path = "/example-schema:ports[name='A']/shutdown";
         }
 
-        catching<OnExec>([&] { REQUIRE(datastore->executeAction(path, input) == output); });
+        catching<OnExec>([&] { REQUIRE(datastore->execute(path, input) == output); });
     }
 
     waitForCompletionAndBitMore(seq1);
diff --git a/tests/datastoreaccess_mock.hpp b/tests/datastoreaccess_mock.hpp
index 751cdad..e708149 100644
--- a/tests/datastoreaccess_mock.hpp
+++ b/tests/datastoreaccess_mock.hpp
@@ -24,8 +24,7 @@
     IMPLEMENT_MOCK1(createItem);
     IMPLEMENT_MOCK1(deleteItem);
     IMPLEMENT_MOCK2(moveItem);
-    IMPLEMENT_MOCK2(executeRpc);
-    IMPLEMENT_MOCK2(executeAction);
+    IMPLEMENT_MOCK2(execute);
 
     // Can't use IMPLEMENT_MOCK for private methods - IMPLEMENT_MOCK needs full visibility of the method
     MAKE_MOCK1(listInstances, std::vector<ListInstance>(const std::string&), override);
diff --git a/tests/example-schema.yang b/tests/example-schema.yang
index 6c546d3..0db4c4f 100644
--- a/tests/example-schema.yang
+++ b/tests/example-schema.yang
@@ -88,6 +88,16 @@
         }
     }
 
+    typedef myType {
+        type int32;
+        description "My type.";
+    }
+
+    leaf typedefedLeaf {
+        type myType;
+        description "This is a typedefed leaf.";
+    }
+
     grouping upAndDown {
         leaf up {
             type boolean;
diff --git a/tests/interpreter.cpp b/tests/interpreter.cpp
index d57ad64..c6d7c9c 100644
--- a/tests/interpreter.cpp
+++ b/tests/interpreter.cpp
@@ -454,7 +454,7 @@
         SECTION("exec")
         {
             REQUIRE_CALL(*input_datastore, getItems("/")).RETURN(DatastoreAccess::Tree{});
-            REQUIRE_CALL(*datastore, executeRpc("/example:launch-nukes", DatastoreAccess::Tree{})).RETURN(DatastoreAccess::Tree{});
+            REQUIRE_CALL(*datastore, execute("/example:launch-nukes", DatastoreAccess::Tree{})).RETURN(DatastoreAccess::Tree{});
             boost::apply_visitor(Interpreter(parser, proxyDatastore), command_{exec_{}});
         }
 
diff --git a/tests/path_completion.cpp b/tests/path_completion.cpp
index ffd3e93..6c7a8e6 100644
--- a/tests/path_completion.cpp
+++ b/tests/path_completion.cpp
@@ -205,6 +205,16 @@
         }
     }
 
+    SECTION("describe completion")
+    {
+        SECTION("path with list at the end")
+        {
+            input = "describe /example:list/";
+            expectedCompletions = {"contInList/", "number "};
+            expectedContextLength = 0;
+        }
+    }
+
     SECTION("list keys completion")
     {
         SECTION("cd example:lis")
diff --git a/tests/prepare.cpp b/tests/prepare.cpp
index 139ef4b..d56a488 100644
--- a/tests/prepare.cpp
+++ b/tests/prepare.cpp
@@ -28,14 +28,14 @@
     SECTION("rpc")
     {
         input = "prepare example:fire";
-        expected.m_path.m_nodes.push_back({module_{"example"}, rpcNode_{"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.push_back({module_{"example"}, listElement_{"port", {{"name", std::string{"eth0"}}}}});
-        expected.m_path.m_nodes.push_back({actionNode_{"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);
diff --git a/tests/pretty_printers.hpp b/tests/pretty_printers.hpp
index 1c7154b..7f19a8e 100644
--- a/tests/pretty_printers.hpp
+++ b/tests/pretty_printers.hpp
@@ -96,7 +96,9 @@
 
 std::ostream& operator<<(std::ostream& s, const yang::TypeInfo& type)
 {
-    s << type.m_type << (type.m_units ? " units: " + *type.m_units : "");
+    s << type.m_type;
+    s << " units: " << (type.m_units ? *type.m_units : "std::nullopt");
+    s << " description: " << (type.m_description ? *type.m_description : "std::nullopt");
     return s;
 }
 
diff --git a/tests/set_value_completion.cpp b/tests/set_value_completion.cpp
index dd36e34..f4d8124 100644
--- a/tests/set_value_completion.cpp
+++ b/tests/set_value_completion.cpp
@@ -29,6 +29,7 @@
     schema->addIdentity(identityRef_{"mod", "food"}, identityRef_{"mod", "spaghetti"});
     schema->addIdentity(identityRef_{"mod", "pizza"}, identityRef_{"pizza-module", "hawaii"});
     schema->addLeaf("/", "mod:foodIdentRef", yang::IdentityRef{schema->validIdentities("mod", "food")});
+    schema->addLeaf("/", "mod:flags", yang::Bits{{"parity", "zero", "carry", "sign"}});
     auto mockDatastore = std::make_shared<MockDatastoreAccess>();
     // The parser will use DataQuery for key value completion, but I'm not testing that here, so I don't return anything.
     ALLOW_CALL(*mockDatastore, listInstances("/mod:list"))
@@ -88,5 +89,19 @@
         expectedContextLength = 0;
     }
 
+    SECTION("set mod:flags ")
+    {
+        input = "set mod:flags ";
+        expectedCompletions = {"carry", "sign", "parity", "zero"};
+        expectedContextLength = 0;
+    }
+
+    SECTION("set mod:flags ze")
+    {
+        input = "set mod:flags ze";
+        expectedCompletions = {"zero"};
+        expectedContextLength = 2;
+    }
+
     REQUIRE(parser.completeCommand(input, errorStream) == (Completions{expectedCompletions, expectedContextLength}));
 }
diff --git a/tests/yang.cpp b/tests/yang.cpp
index 1ec6507..699a05c 100644
--- a/tests/yang.cpp
+++ b/tests/yang.cpp
@@ -171,6 +171,7 @@
             enum lol;
             enum data;
         }
+        description "This is a restricted enum typedef.";
     }
 
     leaf leafEnumTypedef {
@@ -518,6 +519,7 @@
         SECTION("leafType")
         {
             yang::LeafDataType type;
+            std::optional<std::string> expectedDescription;
 
             SECTION("leafString")
             {
@@ -622,6 +624,7 @@
                 node.first = "example-schema";
                 node.second = "leafEnumTypedefRestricted2";
                 type = createEnum({"lol", "data"});
+                expectedDescription = "This is a restricted enum typedef.";
             }
 
             SECTION("pizzaSize")
@@ -736,7 +739,7 @@
             }
 
 
-            REQUIRE(ys.leafType(path, node) == type);
+            REQUIRE(ys.leafType(path, node) == yang::TypeInfo(type, std::nullopt, expectedDescription));
         }
         SECTION("availableNodes")
         {
@@ -1072,6 +1075,16 @@
             REQUIRE(ys.leafType(pathToSchemaString(path, Prefixes::WhenNeeded)) == yang::TypeInfo{expectedType, expectedUnits});
         }
 
+        SECTION("type description")
+        {
+            yang::LeafDataType expectedType = createEnum({"lol", "data"});
+            std::optional<std::string> expectedDescription;
+
+            path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("leafEnumTypedefRestricted2"));
+            expectedDescription = "This is a restricted enum typedef.";
+            REQUIRE(ys.leafType(pathToSchemaString(path, Prefixes::WhenNeeded)) == yang::TypeInfo{expectedType, std::nullopt, expectedDescription});
+        }
+
         SECTION("nodeType")
         {
             yang::NodeTypes expected;