blob: cc97aa9af720d6d517c6c83397b43823fc40149d [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áte7248b22020-06-26 15:38:59 +020011#include "yang_schema.hpp"
12#include "proxy_datastore.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020013
Václav Kubernátc31bd602019-03-07 11:44:48 +010014#ifdef sysrepo_BACKEND
Václav Kubernát73109382018-09-14 19:52:03 +020015#include "sysrepo_access.hpp"
Jan Kundrát7ec214d2020-06-19 17:05:07 +020016using OnInvalidSchemaPathCreate = DatastoreException;
Václav Kubernát654303f2020-07-31 13:16:54 +020017using OnInvalidSchemaPathDelete = DatastoreException;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020018using OnInvalidSchemaPathMove = sysrepo::sysrepo_exception;
Václav Kubernát74487df2020-06-04 01:29:28 +020019using OnInvalidRpcPath = sysrepo::sysrepo_exception;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020020using OnKeyNotFound = void;
Václav Kubernáta8789602020-07-20 15:18:19 +020021using OnExec = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010022#elif defined(netconf_BACKEND)
Jan Kundrát7ec214d2020-06-19 17:05:07 +020023using OnInvalidSchemaPathCreate = std::runtime_error;
24using OnInvalidSchemaPathDelete = std::runtime_error;
25using OnInvalidSchemaPathMove = std::runtime_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020026using OnInvalidRpcPath = std::runtime_error;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020027using OnKeyNotFound = std::runtime_error;
Václav Kubernáta8789602020-07-20 15:18:19 +020028using OnExec = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010029#include "netconf_access.hpp"
Václav Kubernát74487df2020-06-04 01:29:28 +020030#elif defined(yang_BACKEND)
31#include <fstream>
32#include "yang_access.hpp"
33#include "yang_access_test_vars.hpp"
34using OnInvalidSchemaPathCreate = DatastoreException;
35using OnInvalidSchemaPathDelete = DatastoreException;
36using OnInvalidSchemaPathMove = DatastoreException;
37using OnInvalidRpcPath = DatastoreException;
38using OnKeyNotFound = DatastoreException;
Václav Kubernáta8789602020-07-20 15:18:19 +020039using OnExec = std::logic_error;
Václav Kubernátc31bd602019-03-07 11:44:48 +010040#else
41#error "Unknown backend"
42#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +010043#include "pretty_printers.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020044#include "sysrepo_subscription.hpp"
Václav Kubernát8e121ff2019-10-15 15:47:45 +020045#include "utils.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020046
Jan Kundrát6ee84792020-01-24 01:43:36 +010047using namespace std::literals::string_literals;
48
Václav Kubernát69aabe92020-01-24 16:53:12 +010049class MockRecorder : public trompeloeil::mock_interface<Recorder> {
Václav Kubernát73109382018-09-14 19:52:03 +020050public:
Václav Kubernát69aabe92020-01-24 16:53:12 +010051 IMPLEMENT_MOCK3(write);
Václav Kubernát73109382018-09-14 19:52:03 +020052};
53
Jan Kundrátbb525b42020-02-04 11:56:59 +010054class MockDataSupplier : public trompeloeil::mock_interface<DataSupplier> {
55public:
56 IMPLEMENT_CONST_MOCK1(get_data);
57};
58
Jan Kundrát7ec214d2020-06-19 17:05:07 +020059namespace {
60template <class ...> constexpr std::false_type always_false [[maybe_unused]] {};
61template <class Exception, typename Callable> void catching(const Callable& what) {
62
63 if constexpr (std::is_same_v<Exception, void>) {
Jan Kundrát3867c9e2020-06-18 20:26:45 +020064 what();
Jan Kundrát7ec214d2020-06-19 17:05:07 +020065 } else if constexpr (std::is_same<Exception, std::runtime_error>()) {
66 // cannot use REQUIRE_THROWS_AS(..., Exception) directly because that one
67 // needs an extra `typename` deep in the bowels of doctest
68 REQUIRE_THROWS_AS(what(), std::runtime_error);
Václav Kubernát74487df2020-06-04 01:29:28 +020069 } else if constexpr (std::is_same<Exception, std::logic_error>()) {
70 REQUIRE_THROWS_AS(what(), std::logic_error);
Jan Kundrát7ec214d2020-06-19 17:05:07 +020071 } else if constexpr (std::is_same<Exception, DatastoreException>()) {
72 REQUIRE_THROWS_AS(what(), DatastoreException);
73 } else if constexpr (std::is_same<Exception, sysrepo::sysrepo_exception>()) {
74 REQUIRE_THROWS_AS(what(), sysrepo::sysrepo_exception);
75 } else {
76 static_assert(always_false<Exception>); // https://stackoverflow.com/a/53945549/2245623
Jan Kundrát3867c9e2020-06-18 20:26:45 +020077 }
78}
Jan Kundrát7ec214d2020-06-19 17:05:07 +020079}
Jan Kundrát3867c9e2020-06-18 20:26:45 +020080
Václav Kubernát74487df2020-06-04 01:29:28 +020081#if defined(yang_BACKEND)
82class TestYangAccess : public YangAccess {
83public:
84 void commitChanges() override
85 {
86 YangAccess::commitChanges();
87 dumpToSysrepo();
88 }
89
90 void copyConfig(const Datastore source, const Datastore destination) override
91 {
92 YangAccess::copyConfig(source, destination);
93 dumpToSysrepo();
94 }
95
96private:
97 void dumpToSysrepo()
98 {
99 {
100 std::ofstream of(testConfigFile);
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200101 of << dump(DataFormat::Xml);
Václav Kubernát74487df2020-06-04 01:29:28 +0200102 }
Václav Kubernát654303f2020-07-31 13:16:54 +0200103 auto command = std::string(sysrepocfgExecutable) + " --import=" + testConfigFile + " --format=xml --datastore=running --module=example-schema -w";
Václav Kubernát74487df2020-06-04 01:29:28 +0200104 REQUIRE(std::system(command.c_str()) == 0);
105 }
106};
107#endif
108
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200109TEST_CASE("setting/getting values")
Václav Kubernát73109382018-09-14 19:52:03 +0200110{
Václav Kubernát654303f2020-07-31 13:16:54 +0200111 sr_log_stderr(SR_LL_DBG);
Václav Kubernát73109382018-09-14 19:52:03 +0200112 trompeloeil::sequence seq1;
113 MockRecorder mock;
Václav Kubernát654303f2020-07-31 13:16:54 +0200114 {
115 auto conn = std::make_shared<sysrepo::Connection>();
116 auto sess = std::make_shared<sysrepo::Session>(conn);
117 sess->copy_config(SR_DS_STARTUP, "example-schema", 1000, true);
118 }
Václav Kubernátab612e92019-11-26 19:51:31 +0100119 SysrepoSubscription subscription("example-schema", &mock);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100120
121#ifdef sysrepo_BACKEND
Václav Kubernát654303f2020-07-31 13:16:54 +0200122 SysrepoAccess datastore(Datastore::Running);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100123#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200124 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
125 NetconfAccess datastore(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200126#elif defined(yang_BACKEND)
127 TestYangAccess datastore;
128 datastore.addSchemaDir(schemaDir);
129 datastore.addSchemaFile(exampleSchemaFile);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100130#else
131#error "Unknown backend"
132#endif
Václav Kubernát73109382018-09-14 19:52:03 +0200133
Václav Kubernát69aabe92020-01-24 16:53:12 +0100134
Václav Kubernát134d78f2019-09-03 16:42:29 +0200135 SECTION("set leafInt8 to -128")
Václav Kubernát73109382018-09-14 19:52:03 +0200136 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100137 REQUIRE_CALL(mock, write("/example-schema:leafInt8", std::nullopt, "-128"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200138 datastore.setLeaf("/example-schema:leafInt8", int8_t{-128});
139 datastore.commitChanges();
140 }
141
142 SECTION("set leafInt16 to -32768")
143 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100144 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "-32768"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200145 datastore.setLeaf("/example-schema:leafInt16", int16_t{-32768});
146 datastore.commitChanges();
147 }
148
149 SECTION("set leafInt32 to -2147483648")
150 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100151 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "-2147483648"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200152 datastore.setLeaf("/example-schema:leafInt32", int32_t{-2147483648});
153 datastore.commitChanges();
154 }
155
156 SECTION("set leafInt64 to -50000000000")
157 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100158 REQUIRE_CALL(mock, write("/example-schema:leafInt64", std::nullopt, "-50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200159 datastore.setLeaf("/example-schema:leafInt64", int64_t{-50000000000});
160 datastore.commitChanges();
161 }
162
163 SECTION("set leafUInt8 to 255")
164 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100165 REQUIRE_CALL(mock, write("/example-schema:leafUInt8", std::nullopt, "255"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200166 datastore.setLeaf("/example-schema:leafUInt8", uint8_t{255});
167 datastore.commitChanges();
168 }
169
170 SECTION("set leafUInt16 to 65535")
171 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100172 REQUIRE_CALL(mock, write("/example-schema:leafUInt16", std::nullopt, "65535"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200173 datastore.setLeaf("/example-schema:leafUInt16", uint16_t{65535});
174 datastore.commitChanges();
175 }
176
177 SECTION("set leafUInt32 to 4294967295")
178 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100179 REQUIRE_CALL(mock, write("/example-schema:leafUInt32", std::nullopt, "4294967295"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200180 datastore.setLeaf("/example-schema:leafUInt32", uint32_t{4294967295});
181 datastore.commitChanges();
182 }
183
184 SECTION("set leafUInt64 to 50000000000")
185 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100186 REQUIRE_CALL(mock, write("/example-schema:leafUInt64", std::nullopt, "50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200187 datastore.setLeaf("/example-schema:leafUInt64", uint64_t{50000000000});
Václav Kubernát73109382018-09-14 19:52:03 +0200188 datastore.commitChanges();
189 }
190
191 SECTION("set leafEnum to coze")
192 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100193 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "coze"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200194 datastore.setLeaf("/example-schema:leafEnum", enum_{"coze"});
195 datastore.commitChanges();
196 }
197
198 SECTION("set leafDecimal to 123.544")
199 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100200 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.544"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200201 datastore.setLeaf("/example-schema:leafDecimal", 123.544);
202 datastore.commitChanges();
203 }
204
Jan Kundrátd2872862020-06-18 21:02:00 +0200205 SECTION("set a string, then delete it")
206 {
207 REQUIRE_CALL(mock, write("/example-schema:leafString", std::nullopt, "blah"s));
208 datastore.setLeaf("/example-schema:leafString", "blah"s);
209 datastore.commitChanges();
210 DatastoreAccess::Tree expected{{"/example-schema:leafString", "blah"s}};
211 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
212
213 REQUIRE_CALL(mock, write("/example-schema:leafString", "blah"s, std::nullopt));
214 datastore.deleteItem("/example-schema:leafString");
215 datastore.commitChanges();
216 expected.clear();
217 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
218 }
219
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200220 SECTION("set a non-existing leaf")
221 {
222 catching<OnInvalidSchemaPathCreate>([&]{
223 datastore.setLeaf("/example-schema:non-existing", "what"s);
224 });
225 }
226
Václav Kubernát73109382018-09-14 19:52:03 +0200227 SECTION("create presence container")
228 {
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200229 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") == std::string::npos);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100230 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200231 datastore.createItem("/example-schema:pContainer");
Václav Kubernát73109382018-09-14 19:52:03 +0200232 datastore.commitChanges();
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200233 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") != std::string::npos);
Václav Kubernát73109382018-09-14 19:52:03 +0200234 }
235
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100236 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200237 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100238 {
239 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", std::nullopt, ""s));
240 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", std::nullopt, "Nguyen"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200241 datastore.createItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100242 datastore.commitChanges();
243 }
244 {
245 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", ""s, std::nullopt));
246 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", "Nguyen"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200247 datastore.deleteItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100248 datastore.commitChanges();
249 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200250 }
251
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200252 SECTION("deleting non-existing list keys")
253 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200254 catching<OnKeyNotFound>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200255 datastore.deleteItem("/example-schema:person[name='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200256 datastore.commitChanges();
257 });
258 }
259
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200260 SECTION("accessing non-existing schema nodes as a list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200261 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200262 catching<OnInvalidSchemaPathCreate>([&]{
263 datastore.createItem("/example-schema:non-existing-list[xxx='blah']");
264 datastore.commitChanges();
265 });
266 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200267 datastore.deleteItem("/example-schema:non-existing-list[xxx='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200268 datastore.commitChanges();
269 });
270 }
271
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200272 SECTION("leafref pointing to a key of a list")
273 {
274 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100275 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']", std::nullopt, ""s));
276 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s));
277 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']", std::nullopt, ""s));
278 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s));
279 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']", std::nullopt, ""s));
280 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']/name", std::nullopt, "Kolafa"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200281 datastore.createItem("/example-schema:person[name='Dan']");
282 datastore.createItem("/example-schema:person[name='Elfi']");
283 datastore.createItem("/example-schema:person[name='Kolafa']");
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200284 datastore.commitChanges();
285 }
286
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200287 std::string value;
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200288 SECTION("Dan")
289 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200290 value = "Dan";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200291 }
292
293 SECTION("Elfi")
294 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200295 value = "Elfi";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200296 }
297
298 SECTION("Kolafa")
299 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200300 value = "Kolafa";
301 }
302
303 datastore.setLeaf("/example-schema:bossPerson", value);
304 {
305 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, value));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200306 datastore.commitChanges();
307 }
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200308 REQUIRE(datastore.getItems("/example-schema:bossPerson") == DatastoreAccess::Tree{{"/example-schema:bossPerson", value}});
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200309 }
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200310 SECTION("bool values get correctly represented as bools")
311 {
312 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100313 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "true"s));
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200314 datastore.setLeaf("/example-schema:down", bool{true});
315 datastore.commitChanges();
316 }
317
Jan Kundrátb331b552020-01-23 15:25:29 +0100318 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200319 REQUIRE(datastore.getItems("/example-schema:down") == expected);
320 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200321
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200322 SECTION("getting items from the whole module")
323 {
324 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100325 REQUIRE_CALL(mock, write("/example-schema:up", std::nullopt, "true"s));
326 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "false"s));
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200327 datastore.setLeaf("/example-schema:up", bool{true});
328 datastore.setLeaf("/example-schema:down", bool{false});
329 datastore.commitChanges();
330 }
331
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200332 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200333 {"/example-schema:up", bool{true}},
334 {"/example-schema:down", bool{false}}
335 };
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200336 REQUIRE(datastore.getItems("/example-schema:*") == expected);
337 }
338
Václav Kubernát152ce222019-12-19 12:23:32 +0100339 SECTION("getItems returns correct datatypes")
340 {
341 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100342 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "lol"s));
Václav Kubernát152ce222019-12-19 12:23:32 +0100343 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
344 datastore.commitChanges();
345 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100346 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100347
348 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
349 }
350
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100351 SECTION("getItems on a list")
352 {
353 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100354 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']", std::nullopt, ""s));
355 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s));
356 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']", std::nullopt, ""s));
357 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s));
358 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']", std::nullopt, ""s));
359 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']/name", std::nullopt, "Petr"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200360 datastore.createItem("/example-schema:person[name='Jan']");
361 datastore.createItem("/example-schema:person[name='Michal']");
362 datastore.createItem("/example-schema:person[name='Petr']");
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100363 datastore.commitChanges();
364 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100365 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100366 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100367 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100368 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100369 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100370 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100371 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
372 };
373
374 REQUIRE(datastore.getItems("/example-schema:person") == expected);
375 }
376
Václav Kubernát69aabe92020-01-24 16:53:12 +0100377 SECTION("presence containers")
378 {
379 DatastoreAccess::Tree expected;
380 // Make sure it's not there before we create it
381 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
382
383 {
384 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200385 datastore.createItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100386 datastore.commitChanges();
387 }
388 expected = {
389 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
390 };
391 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
392
393 // Make sure it's not there after we delete it
394 {
395 REQUIRE_CALL(mock, write("/example-schema:pContainer", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200396 datastore.deleteItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100397 datastore.commitChanges();
398 }
399 expected = {};
400 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100401 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100402
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200403 SECTION("creating a non-existing schema node as a container")
404 {
405 catching<OnInvalidSchemaPathCreate>([&]{
406 datastore.createItem("/example-schema:non-existing-presence-container");
407 datastore.commitChanges();
408 });
409 }
410
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200411 SECTION("deleting a non-existing schema node as a container or leaf")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200412 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200413 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200414 datastore.deleteItem("/example-schema:non-existing-presence-container");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200415 datastore.commitChanges();
416 });
417 }
418
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100419 SECTION("nested presence container")
420 {
421 DatastoreAccess::Tree expected;
422 // Make sure it's not there before we create it
423 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
424 {
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100425 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200426 datastore.createItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100427 datastore.commitChanges();
428 }
429 expected = {
430 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
431 };
432 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
433 {
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100434 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200435 datastore.deleteItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100436 datastore.commitChanges();
437 }
438 expected = {};
439 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100440 }
441
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100442 SECTION("floats")
443 {
444 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
445 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.4"s));
446 datastore.commitChanges();
447 DatastoreAccess::Tree expected {
448 {"/example-schema:leafDecimal", 123.4},
449 };
450 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
451 }
452
Jan Kundrát3ff50122020-05-07 00:37:50 +0200453 SECTION("unions")
454 {
455 datastore.setLeaf("/example-schema:unionIntString", int32_t{10});
456 REQUIRE_CALL(mock, write("/example-schema:unionIntString", std::nullopt, "10"s));
457 datastore.commitChanges();
458 DatastoreAccess::Tree expected {
459 {"/example-schema:unionIntString", int32_t{10}},
460 };
461 REQUIRE(datastore.getItems("/example-schema:unionIntString") == expected);
462 }
463
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200464 SECTION("identityref") {
465 datastore.setLeaf("/example-schema:beast", identityRef_{"example-schema", "Mammal"});
466 REQUIRE_CALL(mock, write("/example-schema:beast", std::nullopt, "example-schema:Mammal"s));
467 datastore.commitChanges();
468 DatastoreAccess::Tree expected {
469 {"/example-schema:beast", identityRef_{"example-schema", "Mammal"}},
470 };
471 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
472
473 datastore.setLeaf("/example-schema:beast", identityRef_{"Whale"});
474 REQUIRE_CALL(mock, write("/example-schema:beast", "example-schema:Mammal", "example-schema:Whale"s));
475 datastore.commitChanges();
476 expected = {
477 {"/example-schema:beast", identityRef_{"example-schema", "Whale"}},
478 };
479 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
480 }
481
Jan Kundrát68985442020-05-07 02:15:34 +0200482 SECTION("binary")
483 {
484 datastore.setLeaf("/example-schema:blob", binary_{"cHduegByIQ=="s});
485 REQUIRE_CALL(mock, write("/example-schema:blob", std::nullopt, "cHduegByIQ=="s));
486 datastore.commitChanges();
487 DatastoreAccess::Tree expected {
488 {"/example-schema:blob", binary_{"cHduegByIQ=="s}},
489 };
490 REQUIRE(datastore.getItems("/example-schema:blob") == expected);
491 }
492
Jan Kundrát379bb572020-05-07 03:23:13 +0200493 SECTION("empty")
494 {
495 datastore.setLeaf("/example-schema:dummy", empty_{});
496 REQUIRE_CALL(mock, write("/example-schema:dummy", std::nullopt, ""s));
497 datastore.commitChanges();
498 DatastoreAccess::Tree expected {
499 {"/example-schema:dummy", empty_{}},
500 };
501 REQUIRE(datastore.getItems("/example-schema:dummy") == expected);
502 }
503
Václav Kubernát19097f32020-10-05 10:08:29 +0200504 SECTION("bits")
505 {
506 datastore.setLeaf("/example-schema:flags", bits_{{"sign", "carry"}});
507 REQUIRE_CALL(mock, write("/example-schema:flags", std::nullopt, "carry sign"s));
508 datastore.commitChanges();
509 DatastoreAccess::Tree expected {
510 {"/example-schema:flags", bits_{{"carry", "sign"}}},
511 };
512 REQUIRE(datastore.getItems("/example-schema:flags") == expected);
513 }
514
Václav Kubernát74487df2020-06-04 01:29:28 +0200515#if not defined(yang_BACKEND)
Jan Kundrátbb525b42020-02-04 11:56:59 +0100516 SECTION("operational data")
517 {
518 MockDataSupplier mockOpsData;
Václav Kubernát654303f2020-07-31 13:16:54 +0200519 OperationalDataSubscription opsDataSub("example-schema", "/example-schema:temperature", mockOpsData);
Jan Kundrátbb525b42020-02-04 11:56:59 +0100520 DatastoreAccess::Tree expected;
521 std::string xpath;
522 SECTION("temperature")
523 {
524 expected = {{"/example-schema:temperature", int32_t{22}}};
525 xpath = "/example-schema:temperature";
526 }
527
528 REQUIRE_CALL(mockOpsData, get_data(xpath)).RETURN(expected);
529 REQUIRE(datastore.getItems(xpath) == expected);
530 }
Václav Kubernát74487df2020-06-04 01:29:28 +0200531#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +0100532
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200533 SECTION("leaf list")
534 {
535 DatastoreAccess::Tree expected;
536 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "0.0.0.0"s));
537 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "127.0.0.1"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200538 datastore.createItem("/example-schema:addresses[.='0.0.0.0']");
539 datastore.createItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200540 datastore.commitChanges();
541 expected = {
542 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
543 {"/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s},
544 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
545 };
546 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
547
548 REQUIRE_CALL(mock, write("/example-schema:addresses", "0.0.0.0"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200549 datastore.deleteItem("/example-schema:addresses[.='0.0.0.0']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200550 datastore.commitChanges();
551 expected = {
552 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
553 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
554 };
555 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
556
557 REQUIRE_CALL(mock, write("/example-schema:addresses", "127.0.0.1"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200558 datastore.deleteItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200559 datastore.commitChanges();
560 expected = {};
561 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
562 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100563
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200564 SECTION("deleting a non-existing leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200565 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200566 catching<OnKeyNotFound>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200567 datastore.deleteItem("/example-schema:addresses[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200568 datastore.commitChanges();
569 });
570 }
571
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200572 SECTION("accessing a non-existing schema node as a leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200573 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200574 catching<OnInvalidSchemaPathCreate>([&]{
575 datastore.createItem("/example-schema:non-existing[.='non-existing']");
576 datastore.commitChanges();
577 });
578
579 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200580 datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200581 datastore.commitChanges();
582 });
583 }
584
Václav Kubernát7160a132020-04-03 02:11:01 +0200585 SECTION("copying data from startup refreshes the data")
586 {
587 {
588 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
589 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "123"s));
590 datastore.setLeaf("/example-schema:leafInt16", int16_t{123});
591 datastore.commitChanges();
592 }
593 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{{"/example-schema:leafInt16", int16_t{123}}});
594 REQUIRE_CALL(mock, write("/example-schema:leafInt16", "123"s, std::nullopt));
595 datastore.copyConfig(Datastore::Startup, Datastore::Running);
596 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
597 }
598
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200599 SECTION("moving leaflist instances")
600 {
601 DatastoreAccess::Tree expected;
602 {
Václav Kubernát654303f2020-07-31 13:16:54 +0200603 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "http"s));
604 // FIXME: Why no notifications for these??
605 // ... possibly because my subscription doesn't extract it properly?
606 // REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "ftp"s));
607 // REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200608 REQUIRE_CALL(mock, write("/example-schema:protocols", "http"s, "ftp"s));
609 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "pop3"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200610 datastore.createItem("/example-schema:protocols[.='http']");
611 datastore.createItem("/example-schema:protocols[.='ftp']");
612 datastore.createItem("/example-schema:protocols[.='pop3']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200613 datastore.commitChanges();
614 expected = {
615 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
616 {"/example-schema:protocols[.='http']", "http"s},
617 {"/example-schema:protocols[.='ftp']", "ftp"s},
618 {"/example-schema:protocols[.='pop3']", "pop3"s},
619 };
620 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
621 }
622
623 std::string sourcePath;
624 SECTION("begin")
625 {
626 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
627 sourcePath = "/example-schema:protocols[.='pop3']";
628 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
629 datastore.commitChanges();
630 expected = {
631 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
632 {"/example-schema:protocols[.='pop3']", "pop3"s},
633 {"/example-schema:protocols[.='http']", "http"s},
634 {"/example-schema:protocols[.='ftp']", "ftp"s},
635 };
636 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
637 }
638
639 SECTION("end")
640 {
641 sourcePath = "/example-schema:protocols[.='http']";
642 REQUIRE_CALL(mock, write("/example-schema:protocols", "pop3"s, "http"s));
643 datastore.moveItem(sourcePath, yang::move::Absolute::End);
644 datastore.commitChanges();
645 expected = {
646 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
647 {"/example-schema:protocols[.='ftp']", "ftp"s},
648 {"/example-schema:protocols[.='pop3']", "pop3"s},
649 {"/example-schema:protocols[.='http']", "http"s},
650 };
651 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
652 }
653
654 SECTION("after")
655 {
656 sourcePath = "/example-schema:protocols[.='http']";
657 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
658 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{".", "ftp"s}}});
659 datastore.commitChanges();
660 expected = {
661 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
662 {"/example-schema:protocols[.='ftp']", "ftp"s},
663 {"/example-schema:protocols[.='http']", "http"s},
664 {"/example-schema:protocols[.='pop3']", "pop3"s},
665 };
666 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
667 }
668
669 SECTION("before")
670 {
671 sourcePath = "/example-schema:protocols[.='http']";
672 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
673 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{".", "pop3"s}}});
674 datastore.commitChanges();
675 expected = {
676 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
677 {"/example-schema:protocols[.='ftp']", "ftp"s},
678 {"/example-schema:protocols[.='http']", "http"s},
679 {"/example-schema:protocols[.='pop3']", "pop3"s},
680 };
681 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
682 }
683 }
684
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200685 SECTION("moving non-existing schema nodes")
686 {
687 catching<OnInvalidSchemaPathMove>([&]{
688 datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
689 datastore.commitChanges();
690 });
691 }
692
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200693 SECTION("moving list instances")
694 {
695 DatastoreAccess::Tree expected;
696 {
Václav Kubernát654303f2020-07-31 13:16:54 +0200697 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", std::nullopt, ""s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200698 REQUIRE_CALL(mock, write("/example-schema:players[name='John']/name", std::nullopt, "John"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200699 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']", ""s, ""s));
700 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']/name", std::nullopt, "Eve"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200701 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", ""s, ""s));
Václav Kubernát654303f2020-07-31 13:16:54 +0200702 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']/name", std::nullopt, "Adam"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200703 datastore.createItem("/example-schema:players[name='John']");
704 datastore.createItem("/example-schema:players[name='Eve']");
705 datastore.createItem("/example-schema:players[name='Adam']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200706 datastore.commitChanges();
707 expected = {
708 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
709 {"/example-schema:players[name='John']/name", "John"s},
710 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
711 {"/example-schema:players[name='Eve']/name", "Eve"s},
712 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
713 {"/example-schema:players[name='Adam']/name", "Adam"s},
714 };
715 REQUIRE(datastore.getItems("/example-schema:players") == expected);
716 }
717
718 std::string sourcePath;
719 SECTION("begin")
720 {
721 sourcePath = "/example-schema:players[name='Adam']";
722 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", std::nullopt, ""s));
723 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
724 datastore.commitChanges();
725 expected = {
726 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
727 {"/example-schema:players[name='Adam']/name", "Adam"s},
728 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
729 {"/example-schema:players[name='John']/name", "John"s},
730 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
731 {"/example-schema:players[name='Eve']/name", "Eve"s},
732 };
733 REQUIRE(datastore.getItems("/example-schema:players") == expected);
734 }
735
736 SECTION("end")
737 {
738 sourcePath = "/example-schema:players[name='John']";
739 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
740 datastore.moveItem(sourcePath, yang::move::Absolute::End);
741 datastore.commitChanges();
742 expected = {
743 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
744 {"/example-schema:players[name='Eve']/name", "Eve"s},
745 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
746 {"/example-schema:players[name='Adam']/name", "Adam"s},
747 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
748 {"/example-schema:players[name='John']/name", "John"s},
749 };
750 REQUIRE(datastore.getItems("/example-schema:players") == expected);
751 }
752
753 SECTION("after")
754 {
755 sourcePath = "/example-schema:players[name='John']";
756 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
757 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{"name", "Eve"s}}});
758 datastore.commitChanges();
759 expected = {
760 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
761 {"/example-schema:players[name='Eve']/name", "Eve"s},
762 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
763 {"/example-schema:players[name='John']/name", "John"s},
764 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
765 {"/example-schema:players[name='Adam']/name", "Adam"s},
766 };
767 REQUIRE(datastore.getItems("/example-schema:players") == expected);
768 }
769
770 SECTION("before")
771 {
772 sourcePath = "/example-schema:players[name='John']";
773 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
774 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{"name", "Adam"s}}});
775 datastore.commitChanges();
776 expected = {
777 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
778 {"/example-schema:players[name='Eve']/name", "Eve"s},
779 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
780 {"/example-schema:players[name='John']/name", "John"s},
781 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
782 {"/example-schema:players[name='Adam']/name", "Adam"s},
783 };
784 REQUIRE(datastore.getItems("/example-schema:players") == expected);
785 }
786 }
787
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200788 SECTION("getting /")
789 {
790 {
791 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "64"s));
792 datastore.setLeaf("/example-schema:leafInt32", 64);
793 datastore.commitChanges();
794 }
795
796 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200797 {"/example-schema:leafInt32", 64}
798 };
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200799 // This tests if we at least get the data WE added.
Václav Kubernát654303f2020-07-31 13:16:54 +0200800 REQUIRE(std::all_of(expected.begin(), expected.end(), [items = datastore.getItems("/")] (const auto& item) {
801 return std::find(items.begin(), items.end(), item) != items.end();
802 }));
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200803 }
804
Václav Kubernát36986c52020-06-25 10:30:05 +0200805 SECTION("setting and removing without commit")
806 {
807 datastore.setLeaf("/example-schema:leafInt32", 64);
808 datastore.deleteItem("/example-schema:leafInt32");
809 }
810
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200811 SECTION("two key lists")
812 {
813 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']", std::nullopt, ""s));
814 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']/x", std::nullopt, "12"s));
815 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']/y", std::nullopt, "10"s));
816 datastore.createItem("/example-schema:point[x='12'][y='10']");
817 datastore.commitChanges();
818 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:point") != std::string::npos);
819 }
820
Václav Kubernát73109382018-09-14 19:52:03 +0200821 waitForCompletionAndBitMore(seq1);
822}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100823
Václav Kubernát654303f2020-07-31 13:16:54 +0200824struct ActionCb {
825 int operator()(sysrepo::S_Session session,
826 const char* xpath,
827 [[maybe_unused]] const sysrepo::S_Vals input,
828 [[maybe_unused]] sr_event_t event,
829 [[maybe_unused]] uint32_t request_id,
830 sysrepo::S_Vals_Holder output)
Václav Kubernáta8789602020-07-20 15:18:19 +0200831 {
Václav Kubernát654303f2020-07-31 13:16:54 +0200832 if (session->get_context()->get_node(nullptr, xpath)->path(LYS_PATH_FIRST_PREFIX) == "/example-schema:ports/shutdown") {
Václav Kubernáta8789602020-07-20 15:18:19 +0200833 auto buf = output->allocate(1);
834 buf->val(0)->set(joinPaths(xpath, "success").c_str(), true);
835 return SR_ERR_OK;
836 }
837 throw std::runtime_error("unrecognized RPC");
838 }
Václav Kubernát654303f2020-07-31 13:16:54 +0200839};
Václav Kubernáta8789602020-07-20 15:18:19 +0200840
Václav Kubernát654303f2020-07-31 13:16:54 +0200841struct RpcCb {
842 int operator()([[maybe_unused]] sysrepo::S_Session session,
843 const char* xpath,
844 const sysrepo::S_Vals input,
845 [[maybe_unused]] sr_event_t event,
846 [[maybe_unused]] uint32_t request_id,
847 sysrepo::S_Vals_Holder output)
Jan Kundrát6ee84792020-01-24 01:43:36 +0100848 {
849 const auto nukes = "/example-schema:launch-nukes"s;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200850 if (xpath == "/example-schema:noop"s || xpath == "/example-schema:fire"s) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100851 return SR_ERR_OK;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200852 }
853
854 if (xpath == nukes) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100855 uint64_t kilotons = 0;
856 bool hasCities = false;
857 for (size_t i = 0; i < input->val_cnt(); ++i) {
858 const auto& val = input->val(i);
Václav Kubernátf4b6a932020-07-09 10:34:19 +0200859 if (val->xpath() == nukes + "/payload") {
860 continue; // ignore, container
861 }
862 if (val->xpath() == nukes + "/description") {
863 continue; // unused
864 }
865
Jan Kundrát6ee84792020-01-24 01:43:36 +0100866 if (val->xpath() == nukes + "/payload/kilotons") {
867 kilotons = val->data()->get_uint64();
Jan Kundrát6ee84792020-01-24 01:43:36 +0100868 } else if (std::string_view{val->xpath()}.find(nukes + "/cities") == 0) {
869 hasCities = true;
870 } else {
871 throw std::runtime_error("RPC launch-nukes: unexpected input "s + val->xpath());
872 }
873 }
874 if (kilotons == 333'666) {
875 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
876 return SR_ERR_OK;
877 }
878 auto buf = output->allocate(2);
879 size_t i = 0;
880 buf->val(i++)->set((nukes + "/blast-radius").c_str(), uint32_t{33'666});
881 buf->val(i++)->set((nukes + "/actual-yield").c_str(), static_cast<uint64_t>(1.33 * kilotons));
882 if (hasCities) {
883 buf = output->reallocate(output->val_cnt() + 2);
884 buf->val(i++)->set((nukes + "/damaged-places/targets[city='London']/city").c_str(), "London");
885 buf->val(i++)->set((nukes + "/damaged-places/targets[city='Berlin']/city").c_str(), "Berlin");
886 }
887 return SR_ERR_OK;
888 }
889 throw std::runtime_error("unrecognized RPC");
890 }
891};
892
Václav Kubernáta8789602020-07-20 15:18:19 +0200893TEST_CASE("rpc/action") {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100894 trompeloeil::sequence seq1;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100895
896#ifdef sysrepo_BACKEND
Václav Kubernát654303f2020-07-31 13:16:54 +0200897 auto datastore = std::make_shared<SysrepoAccess>(Datastore::Running);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100898#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200899 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
900 auto datastore = std::make_shared<NetconfAccess>(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200901#elif defined(yang_BACKEND)
Václav Kubernáte7248b22020-06-26 15:38:59 +0200902 auto datastore = std::make_shared<YangAccess>();
903 datastore->addSchemaDir(schemaDir);
904 datastore->addSchemaFile(exampleSchemaFile);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100905#else
906#error "Unknown backend"
907#endif
908
Václav Kubernát654303f2020-07-31 13:16:54 +0200909 auto srConn = std::make_shared<sysrepo::Connection>();
Václav Kubernáta8789602020-07-20 15:18:19 +0200910 auto srSession = std::make_shared<sysrepo::Session>(srConn);
911 auto srSubscription = std::make_shared<sysrepo::Subscribe>(srSession);
Václav Kubernát654303f2020-07-31 13:16:54 +0200912 auto rpcCb = std::make_shared<RpcCb>();
913 auto actionCb = std::make_shared<ActionCb>();
Václav Kubernáta8789602020-07-20 15:18:19 +0200914 sysrepo::Logs{}.set_stderr(SR_LL_INF);
Václav Kubernát654303f2020-07-31 13:16:54 +0200915 SysrepoSubscription subscription("example-schema", nullptr);
Václav Kubernáta8789602020-07-20 15:18:19 +0200916 // careful here, sysrepo insists on module_change CBs being registered before RPC CBs, otherwise there's a memleak
Václav Kubernát654303f2020-07-31 13:16:54 +0200917 srSubscription->rpc_subscribe("/example-schema:noop", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
918 srSubscription->rpc_subscribe("/example-schema:launch-nukes", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
919 srSubscription->rpc_subscribe("/example-schema:fire", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
920 srSubscription->rpc_subscribe("/example-schema:ports/shutdown", ActionCb{}, 0, SR_SUBSCR_CTX_REUSE);
Václav Kubernáte7248b22020-06-26 15:38:59 +0200921
Václav Kubernáta8789602020-07-20 15:18:19 +0200922 SECTION("rpc")
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200923 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200924 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
925 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
926 };
927 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100928
Václav Kubernáta8789602020-07-20 15:18:19 +0200929 // ProxyDatastore cannot easily read DatastoreAccess::Tree, so we need to set the input via create/setLeaf/etc.
930 SECTION("valid")
931 {
932 std::string rpc;
933 DatastoreAccess::Tree input, output;
934
935 SECTION("noop") {
936 rpc = "/example-schema:noop";
937 proxyDatastore.initiateRpc(rpc);
938 }
939
940 SECTION("small nuke") {
941 rpc = "/example-schema:launch-nukes";
942 input = {
943 {"description", "dummy"s},
944 {"payload/kilotons", uint64_t{333'666}},
945 };
946 proxyDatastore.initiateRpc(rpc);
947 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{333'666});
948 // no data are returned
949 }
950
951 SECTION("small nuke") {
952 rpc = "/example-schema:launch-nukes";
953 input = {
954 {"description", "dummy"s},
955 {"payload/kilotons", uint64_t{4}},
956 };
957 proxyDatastore.initiateRpc(rpc);
958 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{4});
959
960 output = {
961 {"blast-radius", uint32_t{33'666}},
962 {"actual-yield", uint64_t{5}},
963 };
964 }
965
966 SECTION("with lists") {
967 rpc = "/example-schema:launch-nukes";
968 input = {
969 {"payload/kilotons", uint64_t{6}},
970 {"cities/targets[city='Prague']/city", "Prague"s},
971 };
972 proxyDatastore.initiateRpc(rpc);
973 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{6});
974 proxyDatastore.createItem("/example-schema:launch-nukes/example-schema:cities/example-schema:targets[city='Prague']");
975 output = {
976 {"blast-radius", uint32_t{33'666}},
977 {"actual-yield", uint64_t{7}},
978 {"damaged-places", special_{SpecialValue::PresenceContainer}},
979 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
980 {"damaged-places/targets[city='London']/city", "London"s},
981 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
982 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
983 };
984 }
985
986 SECTION("with leafref") {
987 datastore->createItem("/example-schema:person[name='Colton']");
988 datastore->commitChanges();
989
990 rpc = "/example-schema:fire";
991 input = {
992 {"whom", "Colton"s}
993 };
994 proxyDatastore.initiateRpc(rpc);
995 proxyDatastore.setLeaf("/example-schema:fire/example-schema:whom", "Colton"s);
996 }
997
998 catching<OnExec>([&] {REQUIRE(datastore->executeRpc(rpc, input) == output);});
Václav Kubernátaa4250a2020-07-22 00:02:23 +0200999 catching<OnExec>([&] {REQUIRE(proxyDatastore.execute() == output);});
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001000 }
1001
Václav Kubernáta8789602020-07-20 15:18:19 +02001002 SECTION("non-existing RPC")
1003 {
1004 catching<OnInvalidRpcPath>([&] {datastore->executeRpc("/example-schema:non-existing", DatastoreAccess::Tree{});});
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001005 }
Jan Kundrát6ee84792020-01-24 01:43:36 +01001006 }
1007
Václav Kubernáta8789602020-07-20 15:18:19 +02001008 SECTION("action")
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001009 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001010 std::string path;
1011 DatastoreAccess::Tree input, output;
1012
1013 output = {
1014#ifdef netconf_BACKEND
1015 {"/example-schema:ports[name='A']", special_{SpecialValue::List}},
1016 {"/example-schema:ports[name='A']/name", enum_{"A"}},
1017#endif
1018 {"success", true}
1019 };
1020 datastore->createItem("/example-schema:ports[name='A']");
1021 datastore->commitChanges();
1022 SECTION("shutdown") {
1023 path = "/example-schema:ports[name='A']/shutdown";
1024 }
1025
1026 catching<OnExec>([&] {REQUIRE(datastore->executeAction(path, input) == output);});
Jan Kundrát6ee84792020-01-24 01:43:36 +01001027 }
1028
Jan Kundrát6ee84792020-01-24 01:43:36 +01001029 waitForCompletionAndBitMore(seq1);
1030}