blob: e32076c57de4f5ef197e3bd3783ae6e37db40e01 [file] [log] [blame]
Václav Kubernát73109382018-09-14 19:52:03 +02001/*
2 * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
3 * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
4 *
5 * Written by Václav Kubernát <kubervac@fit.cvut.cz>
6 *
7*/
8
Václav Kubernát26b56082020-02-03 18:28:56 +01009#include "trompeloeil_doctest.hpp"
Jan Kundrát6ee84792020-01-24 01:43:36 +010010#include <sysrepo-cpp/Session.hpp>
Václav Kubernátcfdb9222021-07-07 22:36:24 +020011#include <sysrepo-cpp/utils/exception.hpp>
12#include <sysrepo-cpp/utils/utils.hpp>
Václav Kubernáte7248b22020-06-26 15:38:59 +020013#include "proxy_datastore.hpp"
Václav Kubernátb4e5b182020-11-16 19:55:09 +010014#include "yang_schema.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020015
Václav Kubernátc31bd602019-03-07 11:44:48 +010016#ifdef sysrepo_BACKEND
Václav Kubernát73109382018-09-14 19:52:03 +020017#include "sysrepo_access.hpp"
Jan Kundrát7ec214d2020-06-19 17:05:07 +020018using OnInvalidSchemaPathCreate = DatastoreException;
Václav Kubernát654303f2020-07-31 13:16:54 +020019using OnInvalidSchemaPathDelete = DatastoreException;
Václav Kubernátcfdb9222021-07-07 22:36:24 +020020using OnInvalidSchemaPathMove = sysrepo::ErrorWithCode;
Václav Kubernát40776132021-02-03 08:47:33 +010021using OnInvalidRpcPath = std::runtime_error;
Václav Kubernátcfdb9222021-07-07 22:36:24 +020022using OnInvalidRpcInput = sysrepo::ErrorWithCode;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020023using OnKeyNotFound = void;
Václav Kubernáta8789602020-07-20 15:18:19 +020024using OnExec = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010025#elif defined(netconf_BACKEND)
Jan Kundrát7ec214d2020-06-19 17:05:07 +020026using OnInvalidSchemaPathCreate = std::runtime_error;
27using OnInvalidSchemaPathDelete = std::runtime_error;
28using OnInvalidSchemaPathMove = std::runtime_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020029using OnInvalidRpcPath = std::runtime_error;
Václav Kubernát2edfe542021-02-03 08:08:29 +010030using OnInvalidRpcInput = std::runtime_error;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020031using OnKeyNotFound = std::runtime_error;
Václav Kubernáta8789602020-07-20 15:18:19 +020032using OnExec = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010033#include "netconf_access.hpp"
Václav Kubernát74487df2020-06-04 01:29:28 +020034#elif defined(yang_BACKEND)
35#include <fstream>
36#include "yang_access.hpp"
37#include "yang_access_test_vars.hpp"
38using OnInvalidSchemaPathCreate = DatastoreException;
39using OnInvalidSchemaPathDelete = DatastoreException;
40using OnInvalidSchemaPathMove = DatastoreException;
41using OnInvalidRpcPath = DatastoreException;
Václav Kubernát2edfe542021-02-03 08:08:29 +010042using OnInvalidRpcInput = std::logic_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020043using OnKeyNotFound = DatastoreException;
Václav Kubernáta8789602020-07-20 15:18:19 +020044using OnExec = std::logic_error;
Václav Kubernátc31bd602019-03-07 11:44:48 +010045#else
46#error "Unknown backend"
47#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +010048#include "pretty_printers.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020049#include "sysrepo_subscription.hpp"
Václav Kubernát8e121ff2019-10-15 15:47:45 +020050#include "utils.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020051
Jan Kundrát6ee84792020-01-24 01:43:36 +010052using namespace std::literals::string_literals;
53
Václav Kubernát69aabe92020-01-24 16:53:12 +010054class MockRecorder : public trompeloeil::mock_interface<Recorder> {
Václav Kubernát73109382018-09-14 19:52:03 +020055public:
Václav Kubernátcfdb9222021-07-07 22:36:24 +020056 IMPLEMENT_MOCK5(write);
Václav Kubernát73109382018-09-14 19:52:03 +020057};
58
Jan Kundrátbb525b42020-02-04 11:56:59 +010059class MockDataSupplier : public trompeloeil::mock_interface<DataSupplier> {
60public:
61 IMPLEMENT_CONST_MOCK1(get_data);
62};
63
Jan Kundrát7ec214d2020-06-19 17:05:07 +020064namespace {
65template <class ...> constexpr std::false_type always_false [[maybe_unused]] {};
66template <class Exception, typename Callable> void catching(const Callable& what) {
67
68 if constexpr (std::is_same_v<Exception, void>) {
Jan Kundrát3867c9e2020-06-18 20:26:45 +020069 what();
Jan Kundrát7ec214d2020-06-19 17:05:07 +020070 } else if constexpr (std::is_same<Exception, std::runtime_error>()) {
71 // cannot use REQUIRE_THROWS_AS(..., Exception) directly because that one
72 // needs an extra `typename` deep in the bowels of doctest
73 REQUIRE_THROWS_AS(what(), std::runtime_error);
Václav Kubernát74487df2020-06-04 01:29:28 +020074 } else if constexpr (std::is_same<Exception, std::logic_error>()) {
75 REQUIRE_THROWS_AS(what(), std::logic_error);
Jan Kundrát7ec214d2020-06-19 17:05:07 +020076 } else if constexpr (std::is_same<Exception, DatastoreException>()) {
77 REQUIRE_THROWS_AS(what(), DatastoreException);
Václav Kubernátcfdb9222021-07-07 22:36:24 +020078 } else if constexpr (std::is_same<Exception, sysrepo::ErrorWithCode>()) {
79 REQUIRE_THROWS_AS(what(), sysrepo::ErrorWithCode);
Jan Kundrát7ec214d2020-06-19 17:05:07 +020080 } else {
81 static_assert(always_false<Exception>); // https://stackoverflow.com/a/53945549/2245623
Jan Kundrát3867c9e2020-06-18 20:26:45 +020082 }
83}
Jan Kundrát7ec214d2020-06-19 17:05:07 +020084}
Jan Kundrát3867c9e2020-06-18 20:26:45 +020085
Václav Kubernát74487df2020-06-04 01:29:28 +020086#if defined(yang_BACKEND)
87class TestYangAccess : public YangAccess {
88public:
89 void commitChanges() override
90 {
91 YangAccess::commitChanges();
92 dumpToSysrepo();
93 }
94
95 void copyConfig(const Datastore source, const Datastore destination) override
96 {
97 YangAccess::copyConfig(source, destination);
98 dumpToSysrepo();
99 }
100
101private:
102 void dumpToSysrepo()
103 {
104 {
105 std::ofstream of(testConfigFile);
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200106 of << dump(DataFormat::Xml);
Václav Kubernát74487df2020-06-04 01:29:28 +0200107 }
Jan Kundrát86840ec2021-01-27 15:38:08 +0100108 auto command = std::string(sysrepocfgExecutable) + " --import=" + testConfigFile + " --format=xml --datastore=running --module=example-schema";
Jan Kundrát685c60a2023-08-28 19:13:27 +0200109 if (std::system(command.c_str())) {
110 throw std::runtime_error{"sysrepocfg import failed"};
111 }
Václav Kubernát74487df2020-06-04 01:29:28 +0200112 }
113};
114#endif
115
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200116TEST_CASE("setting/getting values")
Václav Kubernát73109382018-09-14 19:52:03 +0200117{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200118 sysrepo::setLogLevelStderr(sysrepo::LogLevel::Information);
Václav Kubernát73109382018-09-14 19:52:03 +0200119 trompeloeil::sequence seq1;
Václav Kubernáteaf56682021-02-22 17:15:41 +0100120 MockRecorder mockRunning;
121 MockRecorder mockStartup;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200122
123 sysrepo::Connection{}.sessionStart().copyConfig(sysrepo::Datastore::Startup, "example-schema", std::chrono::milliseconds(1000));
124
Václav Kubernáteaf56682021-02-22 17:15:41 +0100125 SysrepoSubscription subRunning("example-schema", &mockRunning);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200126 SysrepoSubscription subStartup("example-schema", &mockStartup, sysrepo::Datastore::Startup);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100127
128#ifdef sysrepo_BACKEND
Václav Kubernátf5d75152020-12-03 03:52:34 +0100129 SysrepoAccess datastore;
Václav Kubernátc31bd602019-03-07 11:44:48 +0100130#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200131 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
132 NetconfAccess datastore(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200133#elif defined(yang_BACKEND)
134 TestYangAccess datastore;
135 datastore.addSchemaDir(schemaDir);
136 datastore.addSchemaFile(exampleSchemaFile);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100137#else
138#error "Unknown backend"
139#endif
Václav Kubernát73109382018-09-14 19:52:03 +0200140
Václav Kubernát69aabe92020-01-24 16:53:12 +0100141
Václav Kubernát134d78f2019-09-03 16:42:29 +0200142 SECTION("set leafInt8 to -128")
Václav Kubernát73109382018-09-14 19:52:03 +0200143 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200144 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt8", std::nullopt, "-128"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200145 datastore.setLeaf("/example-schema:leafInt8", int8_t{-128});
146 datastore.commitChanges();
147 }
148
149 SECTION("set leafInt16 to -32768")
150 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200151 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt16", std::nullopt, "-32768"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200152 datastore.setLeaf("/example-schema:leafInt16", int16_t{-32768});
153 datastore.commitChanges();
154 }
155
156 SECTION("set leafInt32 to -2147483648")
157 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200158 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt32", std::nullopt, "-2147483648"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200159 datastore.setLeaf("/example-schema:leafInt32", int32_t{-2147483648});
160 datastore.commitChanges();
161 }
162
163 SECTION("set leafInt64 to -50000000000")
164 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200165 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt64", std::nullopt, "-50000000000"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200166 datastore.setLeaf("/example-schema:leafInt64", int64_t{-50000000000});
167 datastore.commitChanges();
168 }
169
170 SECTION("set leafUInt8 to 255")
171 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200172 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafUInt8", std::nullopt, "255"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200173 datastore.setLeaf("/example-schema:leafUInt8", uint8_t{255});
174 datastore.commitChanges();
175 }
176
177 SECTION("set leafUInt16 to 65535")
178 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200179 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafUInt16", std::nullopt, "65535"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200180 datastore.setLeaf("/example-schema:leafUInt16", uint16_t{65535});
181 datastore.commitChanges();
182 }
183
184 SECTION("set leafUInt32 to 4294967295")
185 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200186 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafUInt32", std::nullopt, "4294967295"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200187 datastore.setLeaf("/example-schema:leafUInt32", uint32_t{4294967295});
188 datastore.commitChanges();
189 }
190
191 SECTION("set leafUInt64 to 50000000000")
192 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200193 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafUInt64", std::nullopt, "50000000000"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200194 datastore.setLeaf("/example-schema:leafUInt64", uint64_t{50000000000});
Václav Kubernát73109382018-09-14 19:52:03 +0200195 datastore.commitChanges();
196 }
197
198 SECTION("set leafEnum to coze")
199 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200200 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafEnum", std::nullopt, "coze"s, std::nullopt));
Václav Kubernát73109382018-09-14 19:52:03 +0200201 datastore.setLeaf("/example-schema:leafEnum", enum_{"coze"});
202 datastore.commitChanges();
203 }
204
205 SECTION("set leafDecimal to 123.544")
206 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200207 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafDecimal", std::nullopt, "123.544"s, std::nullopt));
Václav Kubernát73109382018-09-14 19:52:03 +0200208 datastore.setLeaf("/example-schema:leafDecimal", 123.544);
209 datastore.commitChanges();
210 }
211
Jan Kundrátd2872862020-06-18 21:02:00 +0200212 SECTION("set a string, then delete it")
213 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200214 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafString", std::nullopt, "blah"s, std::nullopt));
Jan Kundrátd2872862020-06-18 21:02:00 +0200215 datastore.setLeaf("/example-schema:leafString", "blah"s);
216 datastore.commitChanges();
217 DatastoreAccess::Tree expected{{"/example-schema:leafString", "blah"s}};
218 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
219
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200220 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:leafString", "blah"s, std::nullopt, std::nullopt));
Jan Kundrátd2872862020-06-18 21:02:00 +0200221 datastore.deleteItem("/example-schema:leafString");
222 datastore.commitChanges();
223 expected.clear();
224 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
225 }
226
Václav Kubernát8e756642021-11-05 00:04:54 +0100227 SECTION("set a string, then set it to something else without commiting")
228 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200229 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafString", std::nullopt, "oops"s, std::nullopt));
Václav Kubernát8e756642021-11-05 00:04:54 +0100230 datastore.setLeaf("/example-schema:leafString", "blah"s);
231 datastore.setLeaf("/example-schema:leafString", "oops"s);
232 datastore.commitChanges();
233 DatastoreAccess::Tree expected{{"/example-schema:leafString", "oops"s}};
234 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
235
236 }
237
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200238 SECTION("set a non-existing leaf")
239 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100240 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200241 datastore.setLeaf("/example-schema:non-existing", "what"s);
242 });
243 }
244
Václav Kubernát73109382018-09-14 19:52:03 +0200245 SECTION("create presence container")
246 {
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200247 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") == std::string::npos);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200248 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:pContainer", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200249 datastore.createItem("/example-schema:pContainer");
Václav Kubernát73109382018-09-14 19:52:03 +0200250 datastore.commitChanges();
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200251 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") != std::string::npos);
Václav Kubernát73109382018-09-14 19:52:03 +0200252 }
253
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100254 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200255 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100256 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200257 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Nguyen']", std::nullopt, std::nullopt, std::nullopt));
258 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Nguyen']/name", std::nullopt, "Nguyen"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200259 datastore.createItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100260 datastore.commitChanges();
261 }
262 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200263 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:person[name='Nguyen']", std::nullopt, std::nullopt, std::nullopt));
264 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:person[name='Nguyen']/name", "Nguyen"s, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200265 datastore.deleteItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100266 datastore.commitChanges();
267 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200268 }
269
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200270 SECTION("deleting non-existing list keys")
271 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100272 catching<OnKeyNotFound>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200273 datastore.deleteItem("/example-schema:person[name='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200274 datastore.commitChanges();
275 });
276 }
277
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200278 SECTION("accessing non-existing schema nodes as a list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200279 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100280 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200281 datastore.createItem("/example-schema:non-existing-list[xxx='blah']");
282 datastore.commitChanges();
283 });
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100284 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200285 datastore.deleteItem("/example-schema:non-existing-list[xxx='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200286 datastore.commitChanges();
287 });
288 }
289
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200290 SECTION("leafref pointing to a key of a list")
291 {
292 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200293 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Dan']", std::nullopt, std::nullopt, std::nullopt));
294 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s, std::nullopt));
295 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Elfi']", std::nullopt, std::nullopt, std::nullopt));
296 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s, std::nullopt));
297 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Kolafa']", std::nullopt, std::nullopt, std::nullopt));
298 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Kolafa']/name", std::nullopt, "Kolafa"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200299 datastore.createItem("/example-schema:person[name='Dan']");
300 datastore.createItem("/example-schema:person[name='Elfi']");
301 datastore.createItem("/example-schema:person[name='Kolafa']");
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200302 datastore.commitChanges();
303 }
304
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200305 std::string value;
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200306 SECTION("Dan")
307 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200308 value = "Dan";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200309 }
310
311 SECTION("Elfi")
312 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200313 value = "Elfi";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200314 }
315
316 SECTION("Kolafa")
317 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200318 value = "Kolafa";
319 }
320
321 datastore.setLeaf("/example-schema:bossPerson", value);
322 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200323 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:bossPerson", std::nullopt, value, std::nullopt));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200324 datastore.commitChanges();
325 }
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200326 REQUIRE(datastore.getItems("/example-schema:bossPerson") == DatastoreAccess::Tree{{"/example-schema:bossPerson", value}});
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200327 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200328
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200329 SECTION("bool values get correctly represented as bools")
330 {
331 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200332 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:down", std::nullopt, "true"s, std::nullopt));
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200333 datastore.setLeaf("/example-schema:down", bool{true});
334 datastore.commitChanges();
335 }
336
Jan Kundrátb331b552020-01-23 15:25:29 +0100337 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200338 REQUIRE(datastore.getItems("/example-schema:down") == expected);
339 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200340
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200341 SECTION("getting items from the whole module")
342 {
343 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200344 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:up", std::nullopt, "true"s, std::nullopt));
345 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:down", std::nullopt, "false"s, std::nullopt));
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200346 datastore.setLeaf("/example-schema:up", bool{true});
347 datastore.setLeaf("/example-schema:down", bool{false});
348 datastore.commitChanges();
349 }
350
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200351 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200352 {"/example-schema:up", bool{true}},
353 {"/example-schema:down", bool{false}}
354 };
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200355 REQUIRE(datastore.getItems("/example-schema:*") == expected);
356 }
357
Václav Kubernát152ce222019-12-19 12:23:32 +0100358 SECTION("getItems returns correct datatypes")
359 {
360 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200361 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafEnum", std::nullopt, "lol"s, std::nullopt));
Václav Kubernát152ce222019-12-19 12:23:32 +0100362 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
363 datastore.commitChanges();
364 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100365 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100366
367 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
368 }
369
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100370 SECTION("getItems on a list")
371 {
372 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200373 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Jan']", std::nullopt, std::nullopt, std::nullopt));
374 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s, std::nullopt));
375 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Michal']", std::nullopt, std::nullopt, std::nullopt));
376 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s, std::nullopt));
377 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Petr']", std::nullopt, std::nullopt, std::nullopt));
378 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Petr']/name", std::nullopt, "Petr"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200379 datastore.createItem("/example-schema:person[name='Jan']");
380 datastore.createItem("/example-schema:person[name='Michal']");
381 datastore.createItem("/example-schema:person[name='Petr']");
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100382 datastore.commitChanges();
383 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100384 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100385 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100386 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100387 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100388 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100389 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100390 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
391 };
392
393 REQUIRE(datastore.getItems("/example-schema:person") == expected);
394 }
395
Václav Kubernát69aabe92020-01-24 16:53:12 +0100396 SECTION("presence containers")
397 {
398 DatastoreAccess::Tree expected;
399 // Make sure it's not there before we create it
400 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
401
402 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200403 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:pContainer", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200404 datastore.createItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100405 datastore.commitChanges();
406 }
407 expected = {
408 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
409 };
410 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
411
412 // Make sure it's not there after we delete it
413 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200414 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:pContainer", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200415 datastore.deleteItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100416 datastore.commitChanges();
417 }
418 expected = {};
419 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100420 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100421
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200422 SECTION("creating a non-existing schema node as a container")
423 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100424 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200425 datastore.createItem("/example-schema:non-existing-presence-container");
426 datastore.commitChanges();
427 });
428 }
429
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200430 SECTION("deleting a non-existing schema node as a container or leaf")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200431 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100432 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200433 datastore.deleteItem("/example-schema:non-existing-presence-container");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200434 datastore.commitChanges();
435 });
436 }
437
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100438 SECTION("nested presence container")
439 {
440 DatastoreAccess::Tree expected;
441 // Make sure it's not there before we create it
442 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
443 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200444 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:inventory/stuff", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200445 datastore.createItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100446 datastore.commitChanges();
447 }
448 expected = {
449 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
450 };
451 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
452 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200453 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:inventory/stuff", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200454 datastore.deleteItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100455 datastore.commitChanges();
456 }
457 expected = {};
458 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100459 }
460
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100461 SECTION("floats")
462 {
463 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200464 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafDecimal", std::nullopt, "123.4"s, std::nullopt));
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100465 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100466 DatastoreAccess::Tree expected{
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100467 {"/example-schema:leafDecimal", 123.4},
468 };
469 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
470 }
471
Jan Kundrát3ff50122020-05-07 00:37:50 +0200472 SECTION("unions")
473 {
474 datastore.setLeaf("/example-schema:unionIntString", int32_t{10});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200475 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:unionIntString", std::nullopt, "10"s, std::nullopt));
Jan Kundrát3ff50122020-05-07 00:37:50 +0200476 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100477 DatastoreAccess::Tree expected{
Jan Kundrát3ff50122020-05-07 00:37:50 +0200478 {"/example-schema:unionIntString", int32_t{10}},
479 };
480 REQUIRE(datastore.getItems("/example-schema:unionIntString") == expected);
481 }
482
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100483 SECTION("identityref")
484 {
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200485 datastore.setLeaf("/example-schema:beast", identityRef_{"example-schema", "Mammal"});
Jan Kundrátdfcc4852023-06-05 18:26:36 +0200486 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:beast", std::nullopt, "Mammal"s, std::nullopt));
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200487 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100488 DatastoreAccess::Tree expected{
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200489 {"/example-schema:beast", identityRef_{"example-schema", "Mammal"}},
490 };
491 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
492
493 datastore.setLeaf("/example-schema:beast", identityRef_{"Whale"});
Jan Kundrátdfcc4852023-06-05 18:26:36 +0200494 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Modified, "/example-schema:beast", "Mammal", "Whale"s, std::nullopt));
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200495 datastore.commitChanges();
496 expected = {
497 {"/example-schema:beast", identityRef_{"example-schema", "Whale"}},
498 };
499 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
500 }
501
Jan Kundrát68985442020-05-07 02:15:34 +0200502 SECTION("binary")
503 {
504 datastore.setLeaf("/example-schema:blob", binary_{"cHduegByIQ=="s});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200505 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:blob", std::nullopt, "cHduegByIQ=="s, std::nullopt));
Jan Kundrát68985442020-05-07 02:15:34 +0200506 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100507 DatastoreAccess::Tree expected{
Jan Kundrát68985442020-05-07 02:15:34 +0200508 {"/example-schema:blob", binary_{"cHduegByIQ=="s}},
509 };
510 REQUIRE(datastore.getItems("/example-schema:blob") == expected);
511 }
512
Jan Kundrát379bb572020-05-07 03:23:13 +0200513 SECTION("empty")
514 {
515 datastore.setLeaf("/example-schema:dummy", empty_{});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200516 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:dummy", std::nullopt, ""s, std::nullopt));
Jan Kundrát379bb572020-05-07 03:23:13 +0200517 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100518 DatastoreAccess::Tree expected{
Jan Kundrát379bb572020-05-07 03:23:13 +0200519 {"/example-schema:dummy", empty_{}},
520 };
521 REQUIRE(datastore.getItems("/example-schema:dummy") == expected);
522 }
523
Václav Kubernát19097f32020-10-05 10:08:29 +0200524 SECTION("bits")
525 {
526 datastore.setLeaf("/example-schema:flags", bits_{{"sign", "carry"}});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200527 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:flags", std::nullopt, "carry sign"s, std::nullopt));
Václav Kubernát19097f32020-10-05 10:08:29 +0200528 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100529 DatastoreAccess::Tree expected{
Václav Kubernát19097f32020-10-05 10:08:29 +0200530 {"/example-schema:flags", bits_{{"carry", "sign"}}},
531 };
532 REQUIRE(datastore.getItems("/example-schema:flags") == expected);
533 }
534
Václav Kubernát74487df2020-06-04 01:29:28 +0200535#if not defined(yang_BACKEND)
Jan Kundrátbb525b42020-02-04 11:56:59 +0100536 SECTION("operational data")
537 {
538 MockDataSupplier mockOpsData;
Václav Kubernát654303f2020-07-31 13:16:54 +0200539 OperationalDataSubscription opsDataSub("example-schema", "/example-schema:temperature", mockOpsData);
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100540 OperationalDataSubscription opsDataSub2("example-schema", "/example-schema:users", mockOpsData);
Jan Kundrátbb525b42020-02-04 11:56:59 +0100541 DatastoreAccess::Tree expected;
542 std::string xpath;
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100543
Jan Kundrátbb525b42020-02-04 11:56:59 +0100544 SECTION("temperature")
545 {
546 expected = {{"/example-schema:temperature", int32_t{22}}};
547 xpath = "/example-schema:temperature";
548 }
549
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100550 SECTION("key-less lists")
551 {
552 expected = {
553 {"/example-schema:users/userList[1]", special_{SpecialValue::List}},
554 {"/example-schema:users/userList[1]/name", std::string{"John"}},
555 {"/example-schema:users/userList[1]/otherfield", std::string{"LOL"}},
556 {"/example-schema:users/userList[2]", special_{SpecialValue::List}},
557 {"/example-schema:users/userList[2]/name", std::string{"Foo"}},
558 {"/example-schema:users/userList[2]/otherfield", std::string{"Bar"}},
559 };
560 xpath = "/example-schema:users";
561 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100562 REQUIRE_CALL(mockOpsData, get_data(xpath)).RETURN(expected);
563 REQUIRE(datastore.getItems(xpath) == expected);
564 }
Václav Kubernát74487df2020-06-04 01:29:28 +0200565#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +0100566
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200567 SECTION("leaf list")
568 {
569 DatastoreAccess::Tree expected;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200570 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:addresses[.='0.0.0.0']", std::nullopt, "0.0.0.0"s, std::nullopt));
571 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:addresses[.='127.0.0.1']", std::nullopt, "127.0.0.1"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200572 datastore.createItem("/example-schema:addresses[.='0.0.0.0']");
573 datastore.createItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200574 datastore.commitChanges();
575 expected = {
576 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
577 {"/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s},
578 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
579 };
580 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
581
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200582 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200583 datastore.deleteItem("/example-schema:addresses[.='0.0.0.0']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200584 datastore.commitChanges();
585 expected = {
586 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
587 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
588 };
589 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
590
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200591 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200592 datastore.deleteItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200593 datastore.commitChanges();
594 expected = {};
595 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
596 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100597
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200598 SECTION("deleting a non-existing leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200599 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100600 catching<OnKeyNotFound>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200601 datastore.deleteItem("/example-schema:addresses[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200602 datastore.commitChanges();
603 });
604 }
605
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200606 SECTION("accessing a non-existing schema node as a leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200607 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100608 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200609 datastore.createItem("/example-schema:non-existing[.='non-existing']");
610 datastore.commitChanges();
611 });
612
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100613 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200614 datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200615 datastore.commitChanges();
616 });
617 }
618
Václav Kubernát7160a132020-04-03 02:11:01 +0200619 SECTION("copying data from startup refreshes the data")
620 {
621 {
622 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200623 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt16", std::nullopt, "123"s, std::nullopt));
Václav Kubernát7160a132020-04-03 02:11:01 +0200624 datastore.setLeaf("/example-schema:leafInt16", int16_t{123});
625 datastore.commitChanges();
626 }
627 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{{"/example-schema:leafInt16", int16_t{123}}});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200628 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:leafInt16", "123"s, std::nullopt, std::nullopt));
Václav Kubernát7160a132020-04-03 02:11:01 +0200629 datastore.copyConfig(Datastore::Startup, Datastore::Running);
630 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
631 }
632
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200633 SECTION("moving leaflist instances")
634 {
635 DatastoreAccess::Tree expected;
636 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200637 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:protocols[.='http']", "", "http"s, std::nullopt));
638 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:protocols[.='ftp']", "http"s, "ftp"s, std::nullopt));
639 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:protocols[.='pop3']", "ftp"s, "pop3"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200640 datastore.createItem("/example-schema:protocols[.='http']");
641 datastore.createItem("/example-schema:protocols[.='ftp']");
642 datastore.createItem("/example-schema:protocols[.='pop3']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200643 datastore.commitChanges();
644 expected = {
645 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
646 {"/example-schema:protocols[.='http']", "http"s},
647 {"/example-schema:protocols[.='ftp']", "ftp"s},
648 {"/example-schema:protocols[.='pop3']", "pop3"s},
649 };
650 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
651 }
652
653 std::string sourcePath;
654 SECTION("begin")
655 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200656 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='pop3']", ""s, "pop3"s, std::nullopt));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200657 sourcePath = "/example-schema:protocols[.='pop3']";
658 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
659 datastore.commitChanges();
660 expected = {
661 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
662 {"/example-schema:protocols[.='pop3']", "pop3"s},
663 {"/example-schema:protocols[.='http']", "http"s},
664 {"/example-schema:protocols[.='ftp']", "ftp"s},
665 };
666 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
667 }
668
669 SECTION("end")
670 {
671 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200672
673#if defined(yang_BACKEND) || defined(netconf_BACKEND)
674 // Due to the libyang diff algorithm being imperfect, the move operations differ between backends.
675 // The same applies for the stuff below.
676 // https://github.com/sysrepo/sysrepo/issues/2732
677 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='ftp']", ""s, "ftp"s, std::nullopt));
678 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='pop3']", "ftp"s, "pop3"s, std::nullopt));
679#else
680 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='http']", "pop3"s, "http"s, std::nullopt));
681#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200682 datastore.moveItem(sourcePath, yang::move::Absolute::End);
683 datastore.commitChanges();
684 expected = {
685 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
686 {"/example-schema:protocols[.='ftp']", "ftp"s},
687 {"/example-schema:protocols[.='pop3']", "pop3"s},
688 {"/example-schema:protocols[.='http']", "http"s},
689 };
690 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
691 }
692
693 SECTION("after")
694 {
695 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200696#if defined(yang_BACKEND) || defined(netconf_BACKEND)
697 // see the test for "end" for explanation if this #ifdef
698 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='ftp']", ""s, "ftp"s, std::nullopt));
699#else
700 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='http']", "ftp"s, "http"s, std::nullopt));
701#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200702 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{".", "ftp"s}}});
703 datastore.commitChanges();
704 expected = {
705 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
706 {"/example-schema:protocols[.='ftp']", "ftp"s},
707 {"/example-schema:protocols[.='http']", "http"s},
708 {"/example-schema:protocols[.='pop3']", "pop3"s},
709 };
710 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
711 }
712
713 SECTION("before")
714 {
715 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200716#if defined(yang_BACKEND) || defined(netconf_BACKEND)
717 // see the test for "end" for explanation if this #ifdef
718 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='ftp']", ""s, "ftp"s, std::nullopt));
719#else
720 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='http']", "ftp"s, "http"s, std::nullopt));
721#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200722 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{".", "pop3"s}}});
723 datastore.commitChanges();
724 expected = {
725 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
726 {"/example-schema:protocols[.='ftp']", "ftp"s},
727 {"/example-schema:protocols[.='http']", "http"s},
728 {"/example-schema:protocols[.='pop3']", "pop3"s},
729 };
730 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
731 }
732 }
733
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200734 SECTION("moving non-existing schema nodes")
735 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100736 catching<OnInvalidSchemaPathMove>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200737 datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
738 datastore.commitChanges();
739 });
740 }
741
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200742 SECTION("moving list instances")
743 {
744 DatastoreAccess::Tree expected;
745 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200746 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='John']", std::nullopt, std::nullopt, ""s));
747 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='John']/name", std::nullopt, "John"s, std::nullopt));
748 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, "[name='John']"));
749 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Eve']/name", std::nullopt, "Eve"s, std::nullopt));
750 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Adam']", std::nullopt, std::nullopt, "[name='Eve']"));
751 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Adam']/name", std::nullopt, "Adam"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200752 datastore.createItem("/example-schema:players[name='John']");
753 datastore.createItem("/example-schema:players[name='Eve']");
754 datastore.createItem("/example-schema:players[name='Adam']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200755 datastore.commitChanges();
756 expected = {
757 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
758 {"/example-schema:players[name='John']/name", "John"s},
759 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
760 {"/example-schema:players[name='Eve']/name", "Eve"s},
761 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
762 {"/example-schema:players[name='Adam']/name", "Adam"s},
763 };
764 REQUIRE(datastore.getItems("/example-schema:players") == expected);
765 }
766
767 std::string sourcePath;
768 SECTION("begin")
769 {
770 sourcePath = "/example-schema:players[name='Adam']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200771 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Adam']", std::nullopt, std::nullopt, ""s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200772 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
773 datastore.commitChanges();
774 expected = {
775 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
776 {"/example-schema:players[name='Adam']/name", "Adam"s},
777 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
778 {"/example-schema:players[name='John']/name", "John"s},
779 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
780 {"/example-schema:players[name='Eve']/name", "Eve"s},
781 };
782 REQUIRE(datastore.getItems("/example-schema:players") == expected);
783 }
784
785 SECTION("end")
786 {
787 sourcePath = "/example-schema:players[name='John']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200788#if defined(yang_BACKEND) || defined(netconf_BACKEND)
789 // TODO: see TODO comment in leaflist/end
790 // Although these make much less sense
791 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, ""));
792 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Adam']", std::nullopt, std::nullopt, "[name='Eve']"));
793#else
794 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='John']", std::nullopt, std::nullopt, "[name='Adam']"));
795#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200796 datastore.moveItem(sourcePath, yang::move::Absolute::End);
797 datastore.commitChanges();
798 expected = {
799 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
800 {"/example-schema:players[name='Eve']/name", "Eve"s},
801 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
802 {"/example-schema:players[name='Adam']/name", "Adam"s},
803 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
804 {"/example-schema:players[name='John']/name", "John"s},
805 };
806 REQUIRE(datastore.getItems("/example-schema:players") == expected);
807 }
808
809 SECTION("after")
810 {
811 sourcePath = "/example-schema:players[name='John']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200812#if defined(yang_BACKEND) || defined(netconf_BACKEND)
813 // TODO: see TODO comment in leaflist/end
814 // Although these make much less sense
815 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, ""));
816#else
817 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='John']", std::nullopt, std::nullopt, "[name='Eve']"));
818#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200819 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{"name", "Eve"s}}});
820 datastore.commitChanges();
821 expected = {
822 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
823 {"/example-schema:players[name='Eve']/name", "Eve"s},
824 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
825 {"/example-schema:players[name='John']/name", "John"s},
826 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
827 {"/example-schema:players[name='Adam']/name", "Adam"s},
828 };
829 REQUIRE(datastore.getItems("/example-schema:players") == expected);
830 }
831
832 SECTION("before")
833 {
834 sourcePath = "/example-schema:players[name='John']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200835#if defined(yang_BACKEND) || defined(netconf_BACKEND)
836 // TODO: see TODO comment in leaflist/end
837 // Although these make much less sense
838 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, ""));
839#else
840 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='John']", std::nullopt, std::nullopt, "[name='Eve']"));
841#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200842 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{"name", "Adam"s}}});
843 datastore.commitChanges();
844 expected = {
845 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
846 {"/example-schema:players[name='Eve']/name", "Eve"s},
847 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
848 {"/example-schema:players[name='John']/name", "John"s},
849 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
850 {"/example-schema:players[name='Adam']/name", "Adam"s},
851 };
852 REQUIRE(datastore.getItems("/example-schema:players") == expected);
853 }
854 }
855
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200856 SECTION("getting /")
857 {
858 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200859 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt32", std::nullopt, "64"s, std::nullopt));
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200860 datastore.setLeaf("/example-schema:leafInt32", 64);
861 datastore.commitChanges();
862 }
863
864 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200865 {"/example-schema:leafInt32", 64}
866 };
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200867 // This tests if we at least get the data WE added.
Václav Kubernát654303f2020-07-31 13:16:54 +0200868 REQUIRE(std::all_of(expected.begin(), expected.end(), [items = datastore.getItems("/")] (const auto& item) {
869 return std::find(items.begin(), items.end(), item) != items.end();
870 }));
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200871 }
872
Václav Kubernát36986c52020-06-25 10:30:05 +0200873 SECTION("setting and removing without commit")
874 {
875 datastore.setLeaf("/example-schema:leafInt32", 64);
876 datastore.deleteItem("/example-schema:leafInt32");
877 }
878
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200879 SECTION("two key lists")
880 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200881 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:point[x='12'][y='10']", std::nullopt, std::nullopt, std::nullopt));
882 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:point[x='12'][y='10']/x", std::nullopt, "12"s, std::nullopt));
883 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:point[x='12'][y='10']/y", std::nullopt, "10"s, std::nullopt));
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200884 datastore.createItem("/example-schema:point[x='12'][y='10']");
885 datastore.commitChanges();
886 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:point") != std::string::npos);
887 }
888
Václav Kubernát73109382018-09-14 19:52:03 +0200889 waitForCompletionAndBitMore(seq1);
890}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100891
Václav Kubernát654303f2020-07-31 13:16:54 +0200892struct ActionCb {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200893 sysrepo::ErrorCode operator()(
894 [[maybe_unused]] sysrepo::Session session,
895 [[maybe_unused]] uint32_t subscriptionId,
896 std::string_view xpath,
897 [[maybe_unused]] const libyang::DataNode input,
898 [[maybe_unused]] sysrepo::Event event,
899 [[maybe_unused]] uint32_t requestId,
900 libyang::DataNode output)
Václav Kubernáta8789602020-07-20 15:18:19 +0200901 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200902 if (session.getContext().findPath(xpath.data()).path() == "/example-schema:ports/shutdown") {
903 // `xpath` holds the subscription xpath which won't have list keys. We need the path with list keys and
904 // we'll find that in the input.
905 auto inputPath = input.findXPath("/example-schema:ports/shutdown").front().path();
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100906 output.newPath(joinPaths(inputPath, "success"), "true", libyang::CreationOptions::Output);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200907 return sysrepo::ErrorCode::Ok;
Václav Kubernáta8789602020-07-20 15:18:19 +0200908 }
909 throw std::runtime_error("unrecognized RPC");
910 }
Václav Kubernát654303f2020-07-31 13:16:54 +0200911};
Václav Kubernáta8789602020-07-20 15:18:19 +0200912
Václav Kubernát654303f2020-07-31 13:16:54 +0200913struct RpcCb {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200914 sysrepo::ErrorCode operator()(
915 [[maybe_unused]] sysrepo::Session session,
916 [[maybe_unused]] uint32_t subscriptionId,
917 std::string_view xpath,
918 const libyang::DataNode input,
919 [[maybe_unused]] sysrepo::Event event,
920 [[maybe_unused]] uint32_t requestId,
921 libyang::DataNode output)
Jan Kundrát6ee84792020-01-24 01:43:36 +0100922 {
923 const auto nukes = "/example-schema:launch-nukes"s;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200924 if (xpath == "/example-schema:noop"s || xpath == "/example-schema:fire"s) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200925 return sysrepo::ErrorCode::Ok;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200926 }
927
928 if (xpath == nukes) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100929 uint64_t kilotons = 0;
930 bool hasCities = false;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200931 for (const auto& inputNode : input.childrenDfs()) {
932 if (inputNode.path() == nukes) {
933 continue; // ignore, top-level RPC
934 }
935 if (inputNode.path() == nukes + "/payload") {
Václav Kubernátf4b6a932020-07-09 10:34:19 +0200936 continue; // ignore, container
937 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200938 if (inputNode.path() == nukes + "/description") {
Václav Kubernátf4b6a932020-07-09 10:34:19 +0200939 continue; // unused
940 }
941
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200942 if (inputNode.path() == nukes + "/payload/kilotons") {
943 kilotons = std::get<uint64_t>(inputNode.asTerm().value());
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100944 } else if (inputNode.path().find(nukes + "/cities") == 0) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100945 hasCities = true;
946 } else {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100947 throw std::runtime_error("RPC launch-nukes: unexpected input "s + inputNode.path());
Jan Kundrát6ee84792020-01-24 01:43:36 +0100948 }
949 }
950 if (kilotons == 333'666) {
951 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200952 return sysrepo::ErrorCode::Ok;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100953 }
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100954 output.newPath(nukes + "/blast-radius", "33666", libyang::CreationOptions::Output);
955 output.newPath(nukes + "/actual-yield", std::to_string(static_cast<uint64_t>(1.33 * kilotons)), libyang::CreationOptions::Output);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100956 if (hasCities) {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100957 output.newPath(nukes + "/damaged-places/targets[city='London']/city", "London", libyang::CreationOptions::Output);
958 output.newPath(nukes + "/damaged-places/targets[city='Berlin']/city", "Berlin", libyang::CreationOptions::Output);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100959 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200960 return sysrepo::ErrorCode::Ok;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100961 }
962 throw std::runtime_error("unrecognized RPC");
963 }
964};
965
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100966TEST_CASE("rpc/action")
967{
Jan Kundrát6ee84792020-01-24 01:43:36 +0100968 trompeloeil::sequence seq1;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100969
970#ifdef sysrepo_BACKEND
Václav Kubernátf5d75152020-12-03 03:52:34 +0100971 auto datastore = std::make_shared<SysrepoAccess>();
Jan Kundrát6ee84792020-01-24 01:43:36 +0100972#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200973 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
974 auto datastore = std::make_shared<NetconfAccess>(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200975#elif defined(yang_BACKEND)
Václav Kubernáte7248b22020-06-26 15:38:59 +0200976 auto datastore = std::make_shared<YangAccess>();
977 datastore->addSchemaDir(schemaDir);
978 datastore->addSchemaFile(exampleSchemaFile);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100979#else
980#error "Unknown backend"
981#endif
982
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200983 sysrepo::setLogLevelStderr(sysrepo::LogLevel::Information);
984
985 auto srSubscription = sysrepo::Connection{}.sessionStart().onRPCAction("/example-schema:noop", RpcCb{});
986 srSubscription.onRPCAction("/example-schema:launch-nukes", RpcCb{});
987 srSubscription.onRPCAction("/example-schema:fire", RpcCb{});
988 srSubscription.onRPCAction("/example-schema:ports/shutdown", ActionCb{});
989
Václav Kubernát654303f2020-07-31 13:16:54 +0200990 SysrepoSubscription subscription("example-schema", nullptr);
Václav Kubernáte7248b22020-06-26 15:38:59 +0200991
Václav Kubernáta8789602020-07-20 15:18:19 +0200992 SECTION("rpc")
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200993 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200994 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
995 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
996 };
997 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100998
Václav Kubernáta8789602020-07-20 15:18:19 +0200999 // ProxyDatastore cannot easily read DatastoreAccess::Tree, so we need to set the input via create/setLeaf/etc.
1000 SECTION("valid")
1001 {
1002 std::string rpc;
1003 DatastoreAccess::Tree input, output;
1004
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001005 SECTION("noop")
1006 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001007 rpc = "/example-schema:noop";
Václav Kubernátb3960f82020-12-01 03:21:48 +01001008 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001009 }
1010
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001011 SECTION("small nuke")
1012 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001013 rpc = "/example-schema:launch-nukes";
1014 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001015 {joinPaths(rpc, "description"), "dummy"s},
1016 {joinPaths(rpc, "payload/kilotons"), uint64_t{333'666}},
Václav Kubernáta8789602020-07-20 15:18:19 +02001017 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001018 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001019 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{333'666});
1020 // no data are returned
1021 }
1022
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001023 SECTION("small nuke")
1024 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001025 rpc = "/example-schema:launch-nukes";
1026 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001027 {joinPaths(rpc, "description"), "dummy"s},
1028 {joinPaths(rpc, "payload/kilotons"), uint64_t{4}},
Václav Kubernáta8789602020-07-20 15:18:19 +02001029 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001030 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001031 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{4});
1032
1033 output = {
1034 {"blast-radius", uint32_t{33'666}},
1035 {"actual-yield", uint64_t{5}},
1036 };
1037 }
1038
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001039 SECTION("with lists")
1040 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001041 rpc = "/example-schema:launch-nukes";
1042 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001043 {joinPaths(rpc, "payload/kilotons"), uint64_t{6}},
1044 {joinPaths(rpc, "cities/targets[city='Prague']/city"), "Prague"s},
Václav Kubernáta8789602020-07-20 15:18:19 +02001045 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001046 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001047 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{6});
1048 proxyDatastore.createItem("/example-schema:launch-nukes/example-schema:cities/example-schema:targets[city='Prague']");
1049 output = {
1050 {"blast-radius", uint32_t{33'666}},
1051 {"actual-yield", uint64_t{7}},
1052 {"damaged-places", special_{SpecialValue::PresenceContainer}},
1053 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
1054 {"damaged-places/targets[city='London']/city", "London"s},
1055 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
1056 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
1057 };
1058 }
1059
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001060 SECTION("with leafref")
1061 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001062 datastore->createItem("/example-schema:person[name='Colton']");
1063 datastore->commitChanges();
1064
1065 rpc = "/example-schema:fire";
1066 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001067 {joinPaths(rpc, "whom"), "Colton"s}
Václav Kubernáta8789602020-07-20 15:18:19 +02001068 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001069 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001070 proxyDatastore.setLeaf("/example-schema:fire/example-schema:whom", "Colton"s);
1071 }
1072
Václav Kubernátb3960f82020-12-01 03:21:48 +01001073 catching<OnExec>([&] { REQUIRE(datastore->execute(rpc, input) == output); });
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001074 catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001075 }
1076
Václav Kubernáta8789602020-07-20 15:18:19 +02001077 SECTION("non-existing RPC")
1078 {
Václav Kubernátb3960f82020-12-01 03:21:48 +01001079 catching<OnInvalidRpcPath>([&] { datastore->execute("/example-schema:non-existing", DatastoreAccess::Tree{}); });
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001080 }
Václav Kubernát2edfe542021-02-03 08:08:29 +01001081
1082 SECTION("invalid RPC exec resets temporary datastore")
1083 {
1084 proxyDatastore.initiate("/example-schema:setIp");
1085 catching<OnInvalidRpcInput>([&] { auto output = proxyDatastore.execute(); });
1086 REQUIRE(proxyDatastore.inputDatastorePath() == std::nullopt);
1087 }
Jan Kundrát6ee84792020-01-24 01:43:36 +01001088 }
1089
Václav Kubernáta8789602020-07-20 15:18:19 +02001090 SECTION("action")
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001091 {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001092 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
1093 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
1094 };
1095 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Václav Kubernáta8789602020-07-20 15:18:19 +02001096 std::string path;
1097 DatastoreAccess::Tree input, output;
1098
1099 output = {
Václav Kubernáta8789602020-07-20 15:18:19 +02001100 {"success", true}
1101 };
1102 datastore->createItem("/example-schema:ports[name='A']");
1103 datastore->commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001104 SECTION("shutdown")
1105 {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001106 path = "/example-schema:ports[name='A']/example-schema:shutdown";
1107 input = {
1108 {"/example-schema:ports[name='A']/shutdown/force", true}
1109 };
1110 proxyDatastore.initiate(path);
1111 proxyDatastore.setLeaf("/example-schema:ports[name='A']/example-schema:shutdown/example-schema:force", true);
1112
Václav Kubernáta8789602020-07-20 15:18:19 +02001113 }
1114
Václav Kubernátb3960f82020-12-01 03:21:48 +01001115 catching<OnExec>([&] { REQUIRE(datastore->execute(path, input) == output); });
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001116 catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
Jan Kundrát6ee84792020-01-24 01:43:36 +01001117 }
1118
Václav Kubernáted4e3782022-03-02 23:57:33 +01001119 SECTION("parsed info in yang context")
1120 {
1121 auto schema = datastore->schema();
1122 auto leafTypeName = schema->leafTypeName("/example-schema:typedefedLeaf");
1123
1124#if defined(sysrepo_BACKEND)
1125 // Sysrepo is not available yet, with libyang parsed info context
1126 REQUIRE(leafTypeName == std::nullopt);
1127#else
1128 REQUIRE(leafTypeName == "myType");
1129#endif
1130 }
1131
Jan Kundrát6ee84792020-01-24 01:43:36 +01001132 waitForCompletionAndBitMore(seq1);
1133}
Václav Kubernátf5d75152020-12-03 03:52:34 +01001134
1135#if not defined(yang_BACKEND)
1136TEST_CASE("datastore targets")
1137{
1138 const auto testNode = "/example-schema:leafInt32";
1139 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +02001140 auto sess = sysrepo::Connection{}.sessionStart();
1141 sess.deleteItem(testNode);
1142 sess.applyChanges(std::chrono::milliseconds{1000});
1143 sess.switchDatastore(sysrepo::Datastore::Startup);
1144 sess.deleteItem(testNode);
1145 sess.applyChanges(std::chrono::milliseconds{1000});
Václav Kubernátf5d75152020-12-03 03:52:34 +01001146 }
1147 MockRecorder mockRunning;
1148 MockRecorder mockStartup;
1149
1150#ifdef sysrepo_BACKEND
1151 SysrepoAccess datastore;
1152#elif defined(netconf_BACKEND)
1153 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
1154 NetconfAccess datastore(NETOPEER_SOCKET);
1155#else
1156#error "Unknown backend"
1157#endif
1158
1159 auto testGetItems = [&datastore] (const auto& path, const DatastoreAccess::Tree& expected) {
1160 REQUIRE(datastore.getItems(path) == expected);
1161 };
1162
1163 SECTION("subscriptions change operational target")
1164 {
1165 // Default target is operational, so setting a value and reading it should return no values as there are no
1166 // subscriptions yet.
1167 datastore.setLeaf(testNode, 10);
1168 datastore.commitChanges();
1169 testGetItems(testNode, {});
1170
1171 // Now we create a subscription and try again.
1172 SysrepoSubscription subRunning("example-schema", &mockRunning);
1173 testGetItems(testNode, {{testNode, 10}});
1174 }
1175
1176 SECTION("running shows stuff even without subscriptions")
1177 {
1178 datastore.setTarget(DatastoreTarget::Running);
1179 datastore.setLeaf(testNode, 10);
1180 datastore.commitChanges();
1181 testGetItems(testNode, {{testNode, 10}});
1182
1183 // Actually creating a subscription shouldn't make a difference.
1184 SysrepoSubscription subRunning("example-schema", &mockRunning);
1185 testGetItems(testNode, {{testNode, 10}});
1186 }
1187
1188 SECTION("startup changes only affect startup")
1189 {
1190 datastore.setTarget(DatastoreTarget::Startup);
1191 datastore.setLeaf(testNode, 10);
1192 datastore.commitChanges();
1193 testGetItems(testNode, {{testNode, 10}});
1194 datastore.setTarget(DatastoreTarget::Running);
1195 testGetItems(testNode, {});
1196 datastore.setTarget(DatastoreTarget::Operational);
1197 testGetItems(testNode, {});
1198 }
1199
1200}
1201#endif