blob: 613f661b60b4af55b917e20e09fee90222fc2650 [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);
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100520 OperationalDataSubscription opsDataSub2("example-schema", "/example-schema:users", mockOpsData);
Jan Kundrátbb525b42020-02-04 11:56:59 +0100521 DatastoreAccess::Tree expected;
522 std::string xpath;
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100523
Jan Kundrátbb525b42020-02-04 11:56:59 +0100524 SECTION("temperature")
525 {
526 expected = {{"/example-schema:temperature", int32_t{22}}};
527 xpath = "/example-schema:temperature";
528 }
529
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100530 SECTION("key-less lists")
531 {
532 expected = {
533 {"/example-schema:users/userList[1]", special_{SpecialValue::List}},
534 {"/example-schema:users/userList[1]/name", std::string{"John"}},
535 {"/example-schema:users/userList[1]/otherfield", std::string{"LOL"}},
536 {"/example-schema:users/userList[2]", special_{SpecialValue::List}},
537 {"/example-schema:users/userList[2]/name", std::string{"Foo"}},
538 {"/example-schema:users/userList[2]/otherfield", std::string{"Bar"}},
539 };
540 xpath = "/example-schema:users";
541 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100542 REQUIRE_CALL(mockOpsData, get_data(xpath)).RETURN(expected);
543 REQUIRE(datastore.getItems(xpath) == expected);
544 }
Václav Kubernát74487df2020-06-04 01:29:28 +0200545#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +0100546
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200547 SECTION("leaf list")
548 {
549 DatastoreAccess::Tree expected;
550 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "0.0.0.0"s));
551 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "127.0.0.1"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200552 datastore.createItem("/example-schema:addresses[.='0.0.0.0']");
553 datastore.createItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200554 datastore.commitChanges();
555 expected = {
556 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
557 {"/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s},
558 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
559 };
560 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
561
562 REQUIRE_CALL(mock, write("/example-schema:addresses", "0.0.0.0"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200563 datastore.deleteItem("/example-schema:addresses[.='0.0.0.0']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200564 datastore.commitChanges();
565 expected = {
566 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
567 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
568 };
569 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
570
571 REQUIRE_CALL(mock, write("/example-schema:addresses", "127.0.0.1"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200572 datastore.deleteItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200573 datastore.commitChanges();
574 expected = {};
575 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
576 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100577
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200578 SECTION("deleting a non-existing leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200579 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200580 catching<OnKeyNotFound>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200581 datastore.deleteItem("/example-schema:addresses[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200582 datastore.commitChanges();
583 });
584 }
585
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200586 SECTION("accessing a non-existing schema node as a leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200587 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200588 catching<OnInvalidSchemaPathCreate>([&]{
589 datastore.createItem("/example-schema:non-existing[.='non-existing']");
590 datastore.commitChanges();
591 });
592
593 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200594 datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200595 datastore.commitChanges();
596 });
597 }
598
Václav Kubernát7160a132020-04-03 02:11:01 +0200599 SECTION("copying data from startup refreshes the data")
600 {
601 {
602 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
603 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "123"s));
604 datastore.setLeaf("/example-schema:leafInt16", int16_t{123});
605 datastore.commitChanges();
606 }
607 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{{"/example-schema:leafInt16", int16_t{123}}});
608 REQUIRE_CALL(mock, write("/example-schema:leafInt16", "123"s, std::nullopt));
609 datastore.copyConfig(Datastore::Startup, Datastore::Running);
610 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
611 }
612
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200613 SECTION("moving leaflist instances")
614 {
615 DatastoreAccess::Tree expected;
616 {
Václav Kubernát654303f2020-07-31 13:16:54 +0200617 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "http"s));
618 // FIXME: Why no notifications for these??
619 // ... possibly because my subscription doesn't extract it properly?
620 // REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "ftp"s));
621 // REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200622 REQUIRE_CALL(mock, write("/example-schema:protocols", "http"s, "ftp"s));
623 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "pop3"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200624 datastore.createItem("/example-schema:protocols[.='http']");
625 datastore.createItem("/example-schema:protocols[.='ftp']");
626 datastore.createItem("/example-schema:protocols[.='pop3']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200627 datastore.commitChanges();
628 expected = {
629 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
630 {"/example-schema:protocols[.='http']", "http"s},
631 {"/example-schema:protocols[.='ftp']", "ftp"s},
632 {"/example-schema:protocols[.='pop3']", "pop3"s},
633 };
634 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
635 }
636
637 std::string sourcePath;
638 SECTION("begin")
639 {
640 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
641 sourcePath = "/example-schema:protocols[.='pop3']";
642 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
643 datastore.commitChanges();
644 expected = {
645 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
646 {"/example-schema:protocols[.='pop3']", "pop3"s},
647 {"/example-schema:protocols[.='http']", "http"s},
648 {"/example-schema:protocols[.='ftp']", "ftp"s},
649 };
650 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
651 }
652
653 SECTION("end")
654 {
655 sourcePath = "/example-schema:protocols[.='http']";
656 REQUIRE_CALL(mock, write("/example-schema:protocols", "pop3"s, "http"s));
657 datastore.moveItem(sourcePath, yang::move::Absolute::End);
658 datastore.commitChanges();
659 expected = {
660 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
661 {"/example-schema:protocols[.='ftp']", "ftp"s},
662 {"/example-schema:protocols[.='pop3']", "pop3"s},
663 {"/example-schema:protocols[.='http']", "http"s},
664 };
665 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
666 }
667
668 SECTION("after")
669 {
670 sourcePath = "/example-schema:protocols[.='http']";
671 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
672 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{".", "ftp"s}}});
673 datastore.commitChanges();
674 expected = {
675 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
676 {"/example-schema:protocols[.='ftp']", "ftp"s},
677 {"/example-schema:protocols[.='http']", "http"s},
678 {"/example-schema:protocols[.='pop3']", "pop3"s},
679 };
680 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
681 }
682
683 SECTION("before")
684 {
685 sourcePath = "/example-schema:protocols[.='http']";
686 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
687 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{".", "pop3"s}}});
688 datastore.commitChanges();
689 expected = {
690 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
691 {"/example-schema:protocols[.='ftp']", "ftp"s},
692 {"/example-schema:protocols[.='http']", "http"s},
693 {"/example-schema:protocols[.='pop3']", "pop3"s},
694 };
695 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
696 }
697 }
698
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200699 SECTION("moving non-existing schema nodes")
700 {
701 catching<OnInvalidSchemaPathMove>([&]{
702 datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
703 datastore.commitChanges();
704 });
705 }
706
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200707 SECTION("moving list instances")
708 {
709 DatastoreAccess::Tree expected;
710 {
Václav Kubernát654303f2020-07-31 13:16:54 +0200711 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", std::nullopt, ""s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200712 REQUIRE_CALL(mock, write("/example-schema:players[name='John']/name", std::nullopt, "John"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200713 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']", ""s, ""s));
714 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']/name", std::nullopt, "Eve"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200715 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", ""s, ""s));
Václav Kubernát654303f2020-07-31 13:16:54 +0200716 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']/name", std::nullopt, "Adam"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200717 datastore.createItem("/example-schema:players[name='John']");
718 datastore.createItem("/example-schema:players[name='Eve']");
719 datastore.createItem("/example-schema:players[name='Adam']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200720 datastore.commitChanges();
721 expected = {
722 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
723 {"/example-schema:players[name='John']/name", "John"s},
724 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
725 {"/example-schema:players[name='Eve']/name", "Eve"s},
726 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
727 {"/example-schema:players[name='Adam']/name", "Adam"s},
728 };
729 REQUIRE(datastore.getItems("/example-schema:players") == expected);
730 }
731
732 std::string sourcePath;
733 SECTION("begin")
734 {
735 sourcePath = "/example-schema:players[name='Adam']";
736 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", std::nullopt, ""s));
737 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
738 datastore.commitChanges();
739 expected = {
740 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
741 {"/example-schema:players[name='Adam']/name", "Adam"s},
742 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
743 {"/example-schema:players[name='John']/name", "John"s},
744 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
745 {"/example-schema:players[name='Eve']/name", "Eve"s},
746 };
747 REQUIRE(datastore.getItems("/example-schema:players") == expected);
748 }
749
750 SECTION("end")
751 {
752 sourcePath = "/example-schema:players[name='John']";
753 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
754 datastore.moveItem(sourcePath, yang::move::Absolute::End);
755 datastore.commitChanges();
756 expected = {
757 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
758 {"/example-schema:players[name='Eve']/name", "Eve"s},
759 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
760 {"/example-schema:players[name='Adam']/name", "Adam"s},
761 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
762 {"/example-schema:players[name='John']/name", "John"s},
763 };
764 REQUIRE(datastore.getItems("/example-schema:players") == expected);
765 }
766
767 SECTION("after")
768 {
769 sourcePath = "/example-schema:players[name='John']";
770 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
771 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{"name", "Eve"s}}});
772 datastore.commitChanges();
773 expected = {
774 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
775 {"/example-schema:players[name='Eve']/name", "Eve"s},
776 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
777 {"/example-schema:players[name='John']/name", "John"s},
778 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
779 {"/example-schema:players[name='Adam']/name", "Adam"s},
780 };
781 REQUIRE(datastore.getItems("/example-schema:players") == expected);
782 }
783
784 SECTION("before")
785 {
786 sourcePath = "/example-schema:players[name='John']";
787 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
788 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{"name", "Adam"s}}});
789 datastore.commitChanges();
790 expected = {
791 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
792 {"/example-schema:players[name='Eve']/name", "Eve"s},
793 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
794 {"/example-schema:players[name='John']/name", "John"s},
795 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
796 {"/example-schema:players[name='Adam']/name", "Adam"s},
797 };
798 REQUIRE(datastore.getItems("/example-schema:players") == expected);
799 }
800 }
801
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200802 SECTION("getting /")
803 {
804 {
805 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "64"s));
806 datastore.setLeaf("/example-schema:leafInt32", 64);
807 datastore.commitChanges();
808 }
809
810 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200811 {"/example-schema:leafInt32", 64}
812 };
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200813 // This tests if we at least get the data WE added.
Václav Kubernát654303f2020-07-31 13:16:54 +0200814 REQUIRE(std::all_of(expected.begin(), expected.end(), [items = datastore.getItems("/")] (const auto& item) {
815 return std::find(items.begin(), items.end(), item) != items.end();
816 }));
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200817 }
818
Václav Kubernát36986c52020-06-25 10:30:05 +0200819 SECTION("setting and removing without commit")
820 {
821 datastore.setLeaf("/example-schema:leafInt32", 64);
822 datastore.deleteItem("/example-schema:leafInt32");
823 }
824
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200825 SECTION("two key lists")
826 {
827 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']", std::nullopt, ""s));
828 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']/x", std::nullopt, "12"s));
829 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']/y", std::nullopt, "10"s));
830 datastore.createItem("/example-schema:point[x='12'][y='10']");
831 datastore.commitChanges();
832 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:point") != std::string::npos);
833 }
834
Václav Kubernát73109382018-09-14 19:52:03 +0200835 waitForCompletionAndBitMore(seq1);
836}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100837
Václav Kubernát654303f2020-07-31 13:16:54 +0200838struct ActionCb {
839 int operator()(sysrepo::S_Session session,
840 const char* xpath,
841 [[maybe_unused]] const sysrepo::S_Vals input,
842 [[maybe_unused]] sr_event_t event,
843 [[maybe_unused]] uint32_t request_id,
844 sysrepo::S_Vals_Holder output)
Václav Kubernáta8789602020-07-20 15:18:19 +0200845 {
Václav Kubernát654303f2020-07-31 13:16:54 +0200846 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 +0200847 auto buf = output->allocate(1);
848 buf->val(0)->set(joinPaths(xpath, "success").c_str(), true);
849 return SR_ERR_OK;
850 }
851 throw std::runtime_error("unrecognized RPC");
852 }
Václav Kubernát654303f2020-07-31 13:16:54 +0200853};
Václav Kubernáta8789602020-07-20 15:18:19 +0200854
Václav Kubernát654303f2020-07-31 13:16:54 +0200855struct RpcCb {
856 int operator()([[maybe_unused]] sysrepo::S_Session session,
857 const char* xpath,
858 const sysrepo::S_Vals input,
859 [[maybe_unused]] sr_event_t event,
860 [[maybe_unused]] uint32_t request_id,
861 sysrepo::S_Vals_Holder output)
Jan Kundrát6ee84792020-01-24 01:43:36 +0100862 {
863 const auto nukes = "/example-schema:launch-nukes"s;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200864 if (xpath == "/example-schema:noop"s || xpath == "/example-schema:fire"s) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100865 return SR_ERR_OK;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200866 }
867
868 if (xpath == nukes) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100869 uint64_t kilotons = 0;
870 bool hasCities = false;
871 for (size_t i = 0; i < input->val_cnt(); ++i) {
872 const auto& val = input->val(i);
Václav Kubernátf4b6a932020-07-09 10:34:19 +0200873 if (val->xpath() == nukes + "/payload") {
874 continue; // ignore, container
875 }
876 if (val->xpath() == nukes + "/description") {
877 continue; // unused
878 }
879
Jan Kundrát6ee84792020-01-24 01:43:36 +0100880 if (val->xpath() == nukes + "/payload/kilotons") {
881 kilotons = val->data()->get_uint64();
Jan Kundrát6ee84792020-01-24 01:43:36 +0100882 } else if (std::string_view{val->xpath()}.find(nukes + "/cities") == 0) {
883 hasCities = true;
884 } else {
885 throw std::runtime_error("RPC launch-nukes: unexpected input "s + val->xpath());
886 }
887 }
888 if (kilotons == 333'666) {
889 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
890 return SR_ERR_OK;
891 }
892 auto buf = output->allocate(2);
893 size_t i = 0;
894 buf->val(i++)->set((nukes + "/blast-radius").c_str(), uint32_t{33'666});
895 buf->val(i++)->set((nukes + "/actual-yield").c_str(), static_cast<uint64_t>(1.33 * kilotons));
896 if (hasCities) {
897 buf = output->reallocate(output->val_cnt() + 2);
898 buf->val(i++)->set((nukes + "/damaged-places/targets[city='London']/city").c_str(), "London");
899 buf->val(i++)->set((nukes + "/damaged-places/targets[city='Berlin']/city").c_str(), "Berlin");
900 }
901 return SR_ERR_OK;
902 }
903 throw std::runtime_error("unrecognized RPC");
904 }
905};
906
Václav Kubernáta8789602020-07-20 15:18:19 +0200907TEST_CASE("rpc/action") {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100908 trompeloeil::sequence seq1;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100909
910#ifdef sysrepo_BACKEND
Václav Kubernát654303f2020-07-31 13:16:54 +0200911 auto datastore = std::make_shared<SysrepoAccess>(Datastore::Running);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100912#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200913 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
914 auto datastore = std::make_shared<NetconfAccess>(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200915#elif defined(yang_BACKEND)
Václav Kubernáte7248b22020-06-26 15:38:59 +0200916 auto datastore = std::make_shared<YangAccess>();
917 datastore->addSchemaDir(schemaDir);
918 datastore->addSchemaFile(exampleSchemaFile);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100919#else
920#error "Unknown backend"
921#endif
922
Václav Kubernát654303f2020-07-31 13:16:54 +0200923 auto srConn = std::make_shared<sysrepo::Connection>();
Václav Kubernáta8789602020-07-20 15:18:19 +0200924 auto srSession = std::make_shared<sysrepo::Session>(srConn);
925 auto srSubscription = std::make_shared<sysrepo::Subscribe>(srSession);
Václav Kubernát654303f2020-07-31 13:16:54 +0200926 auto rpcCb = std::make_shared<RpcCb>();
927 auto actionCb = std::make_shared<ActionCb>();
Václav Kubernáta8789602020-07-20 15:18:19 +0200928 sysrepo::Logs{}.set_stderr(SR_LL_INF);
Václav Kubernát654303f2020-07-31 13:16:54 +0200929 SysrepoSubscription subscription("example-schema", nullptr);
Václav Kubernáta8789602020-07-20 15:18:19 +0200930 // 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 +0200931 srSubscription->rpc_subscribe("/example-schema:noop", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
932 srSubscription->rpc_subscribe("/example-schema:launch-nukes", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
933 srSubscription->rpc_subscribe("/example-schema:fire", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
934 srSubscription->rpc_subscribe("/example-schema:ports/shutdown", ActionCb{}, 0, SR_SUBSCR_CTX_REUSE);
Václav Kubernáte7248b22020-06-26 15:38:59 +0200935
Václav Kubernáta8789602020-07-20 15:18:19 +0200936 SECTION("rpc")
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200937 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200938 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
939 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
940 };
941 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100942
Václav Kubernáta8789602020-07-20 15:18:19 +0200943 // ProxyDatastore cannot easily read DatastoreAccess::Tree, so we need to set the input via create/setLeaf/etc.
944 SECTION("valid")
945 {
946 std::string rpc;
947 DatastoreAccess::Tree input, output;
948
949 SECTION("noop") {
950 rpc = "/example-schema:noop";
951 proxyDatastore.initiateRpc(rpc);
952 }
953
954 SECTION("small nuke") {
955 rpc = "/example-schema:launch-nukes";
956 input = {
957 {"description", "dummy"s},
958 {"payload/kilotons", uint64_t{333'666}},
959 };
960 proxyDatastore.initiateRpc(rpc);
961 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{333'666});
962 // no data are returned
963 }
964
965 SECTION("small nuke") {
966 rpc = "/example-schema:launch-nukes";
967 input = {
968 {"description", "dummy"s},
969 {"payload/kilotons", uint64_t{4}},
970 };
971 proxyDatastore.initiateRpc(rpc);
972 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{4});
973
974 output = {
975 {"blast-radius", uint32_t{33'666}},
976 {"actual-yield", uint64_t{5}},
977 };
978 }
979
980 SECTION("with lists") {
981 rpc = "/example-schema:launch-nukes";
982 input = {
983 {"payload/kilotons", uint64_t{6}},
984 {"cities/targets[city='Prague']/city", "Prague"s},
985 };
986 proxyDatastore.initiateRpc(rpc);
987 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{6});
988 proxyDatastore.createItem("/example-schema:launch-nukes/example-schema:cities/example-schema:targets[city='Prague']");
989 output = {
990 {"blast-radius", uint32_t{33'666}},
991 {"actual-yield", uint64_t{7}},
992 {"damaged-places", special_{SpecialValue::PresenceContainer}},
993 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
994 {"damaged-places/targets[city='London']/city", "London"s},
995 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
996 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
997 };
998 }
999
1000 SECTION("with leafref") {
1001 datastore->createItem("/example-schema:person[name='Colton']");
1002 datastore->commitChanges();
1003
1004 rpc = "/example-schema:fire";
1005 input = {
1006 {"whom", "Colton"s}
1007 };
1008 proxyDatastore.initiateRpc(rpc);
1009 proxyDatastore.setLeaf("/example-schema:fire/example-schema:whom", "Colton"s);
1010 }
1011
1012 catching<OnExec>([&] {REQUIRE(datastore->executeRpc(rpc, input) == output);});
Václav Kubernátaa4250a2020-07-22 00:02:23 +02001013 catching<OnExec>([&] {REQUIRE(proxyDatastore.execute() == output);});
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001014 }
1015
Václav Kubernáta8789602020-07-20 15:18:19 +02001016 SECTION("non-existing RPC")
1017 {
1018 catching<OnInvalidRpcPath>([&] {datastore->executeRpc("/example-schema:non-existing", DatastoreAccess::Tree{});});
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001019 }
Jan Kundrát6ee84792020-01-24 01:43:36 +01001020 }
1021
Václav Kubernáta8789602020-07-20 15:18:19 +02001022 SECTION("action")
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001023 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001024 std::string path;
1025 DatastoreAccess::Tree input, output;
1026
1027 output = {
1028#ifdef netconf_BACKEND
1029 {"/example-schema:ports[name='A']", special_{SpecialValue::List}},
1030 {"/example-schema:ports[name='A']/name", enum_{"A"}},
1031#endif
1032 {"success", true}
1033 };
1034 datastore->createItem("/example-schema:ports[name='A']");
1035 datastore->commitChanges();
1036 SECTION("shutdown") {
1037 path = "/example-schema:ports[name='A']/shutdown";
1038 }
1039
1040 catching<OnExec>([&] {REQUIRE(datastore->executeAction(path, input) == output);});
Jan Kundrát6ee84792020-01-24 01:43:36 +01001041 }
1042
Jan Kundrát6ee84792020-01-24 01:43:36 +01001043 waitForCompletionAndBitMore(seq1);
1044}