blob: 48b8f78a586fed272806c468876dd53942eeac6a [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át73109382018-09-14 19:52:03 +020011
Václav Kubernátc31bd602019-03-07 11:44:48 +010012#ifdef sysrepo_BACKEND
Václav Kubernát73109382018-09-14 19:52:03 +020013#include "sysrepo_access.hpp"
Jan Kundrát7ec214d2020-06-19 17:05:07 +020014using OnInvalidSchemaPathCreate = DatastoreException;
15using OnInvalidSchemaPathDelete = void;
16using OnInvalidSchemaPathMove = sysrepo::sysrepo_exception;
17using OnKeyNotFound = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010018#elif defined(netconf_BACKEND)
Jan Kundrát7ec214d2020-06-19 17:05:07 +020019using OnInvalidSchemaPathCreate = std::runtime_error;
20using OnInvalidSchemaPathDelete = std::runtime_error;
21using OnInvalidSchemaPathMove = std::runtime_error;
22using OnKeyNotFound = std::runtime_error;
Václav Kubernátc31bd602019-03-07 11:44:48 +010023#include "netconf_access.hpp"
24#include "netopeer_vars.hpp"
25#else
26#error "Unknown backend"
27#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +010028#include "pretty_printers.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020029#include "sysrepo_subscription.hpp"
Václav Kubernát8e121ff2019-10-15 15:47:45 +020030#include "utils.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020031
Jan Kundrát6ee84792020-01-24 01:43:36 +010032using namespace std::literals::string_literals;
33
Václav Kubernát69aabe92020-01-24 16:53:12 +010034class MockRecorder : public trompeloeil::mock_interface<Recorder> {
Václav Kubernát73109382018-09-14 19:52:03 +020035public:
Václav Kubernát69aabe92020-01-24 16:53:12 +010036 IMPLEMENT_MOCK3(write);
Václav Kubernát73109382018-09-14 19:52:03 +020037};
38
Jan Kundrátbb525b42020-02-04 11:56:59 +010039class MockDataSupplier : public trompeloeil::mock_interface<DataSupplier> {
40public:
41 IMPLEMENT_CONST_MOCK1(get_data);
42};
43
Jan Kundrát7ec214d2020-06-19 17:05:07 +020044namespace {
45template <class ...> constexpr std::false_type always_false [[maybe_unused]] {};
46template <class Exception, typename Callable> void catching(const Callable& what) {
47
48 if constexpr (std::is_same_v<Exception, void>) {
Jan Kundrát3867c9e2020-06-18 20:26:45 +020049 what();
Jan Kundrát7ec214d2020-06-19 17:05:07 +020050 } else if constexpr (std::is_same<Exception, std::runtime_error>()) {
51 // cannot use REQUIRE_THROWS_AS(..., Exception) directly because that one
52 // needs an extra `typename` deep in the bowels of doctest
53 REQUIRE_THROWS_AS(what(), std::runtime_error);
54 } else if constexpr (std::is_same<Exception, DatastoreException>()) {
55 REQUIRE_THROWS_AS(what(), DatastoreException);
56 } else if constexpr (std::is_same<Exception, sysrepo::sysrepo_exception>()) {
57 REQUIRE_THROWS_AS(what(), sysrepo::sysrepo_exception);
58 } else {
59 static_assert(always_false<Exception>); // https://stackoverflow.com/a/53945549/2245623
Jan Kundrát3867c9e2020-06-18 20:26:45 +020060 }
61}
Jan Kundrát7ec214d2020-06-19 17:05:07 +020062}
Jan Kundrát3867c9e2020-06-18 20:26:45 +020063
Václav Kubernát8e121ff2019-10-15 15:47:45 +020064TEST_CASE("setting/getting values")
Václav Kubernát73109382018-09-14 19:52:03 +020065{
Václav Kubernát73109382018-09-14 19:52:03 +020066 trompeloeil::sequence seq1;
67 MockRecorder mock;
Václav Kubernátab612e92019-11-26 19:51:31 +010068 SysrepoSubscription subscription("example-schema", &mock);
Václav Kubernátc31bd602019-03-07 11:44:48 +010069
70#ifdef sysrepo_BACKEND
Václav Kubernát715c85c2020-04-14 01:46:08 +020071 SysrepoAccess datastore("netconf-cli-test", Datastore::Running);
Václav Kubernátc31bd602019-03-07 11:44:48 +010072#elif defined(netconf_BACKEND)
73 NetconfAccess datastore(NETOPEER_SOCKET_PATH);
74#else
75#error "Unknown backend"
76#endif
Václav Kubernát73109382018-09-14 19:52:03 +020077
Václav Kubernát69aabe92020-01-24 16:53:12 +010078
Václav Kubernát134d78f2019-09-03 16:42:29 +020079 SECTION("set leafInt8 to -128")
Václav Kubernát73109382018-09-14 19:52:03 +020080 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010081 REQUIRE_CALL(mock, write("/example-schema:leafInt8", std::nullopt, "-128"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020082 datastore.setLeaf("/example-schema:leafInt8", int8_t{-128});
83 datastore.commitChanges();
84 }
85
86 SECTION("set leafInt16 to -32768")
87 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010088 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "-32768"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020089 datastore.setLeaf("/example-schema:leafInt16", int16_t{-32768});
90 datastore.commitChanges();
91 }
92
93 SECTION("set leafInt32 to -2147483648")
94 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010095 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "-2147483648"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020096 datastore.setLeaf("/example-schema:leafInt32", int32_t{-2147483648});
97 datastore.commitChanges();
98 }
99
100 SECTION("set leafInt64 to -50000000000")
101 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100102 REQUIRE_CALL(mock, write("/example-schema:leafInt64", std::nullopt, "-50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200103 datastore.setLeaf("/example-schema:leafInt64", int64_t{-50000000000});
104 datastore.commitChanges();
105 }
106
107 SECTION("set leafUInt8 to 255")
108 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100109 REQUIRE_CALL(mock, write("/example-schema:leafUInt8", std::nullopt, "255"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200110 datastore.setLeaf("/example-schema:leafUInt8", uint8_t{255});
111 datastore.commitChanges();
112 }
113
114 SECTION("set leafUInt16 to 65535")
115 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100116 REQUIRE_CALL(mock, write("/example-schema:leafUInt16", std::nullopt, "65535"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200117 datastore.setLeaf("/example-schema:leafUInt16", uint16_t{65535});
118 datastore.commitChanges();
119 }
120
121 SECTION("set leafUInt32 to 4294967295")
122 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100123 REQUIRE_CALL(mock, write("/example-schema:leafUInt32", std::nullopt, "4294967295"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200124 datastore.setLeaf("/example-schema:leafUInt32", uint32_t{4294967295});
125 datastore.commitChanges();
126 }
127
128 SECTION("set leafUInt64 to 50000000000")
129 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100130 REQUIRE_CALL(mock, write("/example-schema:leafUInt64", std::nullopt, "50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200131 datastore.setLeaf("/example-schema:leafUInt64", uint64_t{50000000000});
Václav Kubernát73109382018-09-14 19:52:03 +0200132 datastore.commitChanges();
133 }
134
135 SECTION("set leafEnum to coze")
136 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100137 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "coze"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200138 datastore.setLeaf("/example-schema:leafEnum", enum_{"coze"});
139 datastore.commitChanges();
140 }
141
142 SECTION("set leafDecimal to 123.544")
143 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100144 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.544"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200145 datastore.setLeaf("/example-schema:leafDecimal", 123.544);
146 datastore.commitChanges();
147 }
148
Jan Kundrátd2872862020-06-18 21:02:00 +0200149 SECTION("set a string, then delete it")
150 {
151 REQUIRE_CALL(mock, write("/example-schema:leafString", std::nullopt, "blah"s));
152 datastore.setLeaf("/example-schema:leafString", "blah"s);
153 datastore.commitChanges();
154 DatastoreAccess::Tree expected{{"/example-schema:leafString", "blah"s}};
155 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
156
157 REQUIRE_CALL(mock, write("/example-schema:leafString", "blah"s, std::nullopt));
158 datastore.deleteItem("/example-schema:leafString");
159 datastore.commitChanges();
160 expected.clear();
161 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
162 }
163
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200164 SECTION("set a non-existing leaf")
165 {
166 catching<OnInvalidSchemaPathCreate>([&]{
167 datastore.setLeaf("/example-schema:non-existing", "what"s);
168 });
169 }
170
Václav Kubernát73109382018-09-14 19:52:03 +0200171 SECTION("create presence container")
172 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100173 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200174 datastore.createItem("/example-schema:pContainer");
Václav Kubernát73109382018-09-14 19:52:03 +0200175 datastore.commitChanges();
176 }
177
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100178 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200179 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100180 {
181 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", std::nullopt, ""s));
182 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", std::nullopt, "Nguyen"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200183 datastore.createItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100184 datastore.commitChanges();
185 }
186 {
187 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", ""s, std::nullopt));
188 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", "Nguyen"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200189 datastore.deleteItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100190 datastore.commitChanges();
191 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200192 }
193
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200194 SECTION("deleting non-existing list keys")
195 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200196 catching<OnKeyNotFound>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200197 datastore.deleteItem("/example-schema:person[name='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200198 datastore.commitChanges();
199 });
200 }
201
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200202 SECTION("accessing non-existing schema nodes as a list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200203 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200204 catching<OnInvalidSchemaPathCreate>([&]{
205 datastore.createItem("/example-schema:non-existing-list[xxx='blah']");
206 datastore.commitChanges();
207 });
208 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200209 datastore.deleteItem("/example-schema:non-existing-list[xxx='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200210 datastore.commitChanges();
211 });
212 }
213
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200214 SECTION("leafref pointing to a key of a list")
215 {
216 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100217 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']", std::nullopt, ""s));
218 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s));
219 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']", std::nullopt, ""s));
220 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s));
221 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']", std::nullopt, ""s));
222 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']/name", std::nullopt, "Kolafa"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200223 datastore.createItem("/example-schema:person[name='Dan']");
224 datastore.createItem("/example-schema:person[name='Elfi']");
225 datastore.createItem("/example-schema:person[name='Kolafa']");
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200226 datastore.commitChanges();
227 }
228
229 // The commitChanges method has to be called in each of the
230 // SECTIONs, because the REQUIRE_CALL only works inside the given
231 // SECTION.
232 SECTION("Dan")
233 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100234 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Dan"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200235 datastore.setLeaf("/example-schema:bossPerson", std::string{"Dan"});
236 datastore.commitChanges();
237 }
238
239 SECTION("Elfi")
240 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100241 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Elfi"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200242 datastore.setLeaf("/example-schema:bossPerson", std::string{"Elfi"});
243 datastore.commitChanges();
244 }
245
246 SECTION("Kolafa")
247 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100248 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Kolafa"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200249 datastore.setLeaf("/example-schema:bossPerson", std::string{"Kolafa"});
250 datastore.commitChanges();
251 }
252 }
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200253 SECTION("bool values get correctly represented as bools")
254 {
255 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100256 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "true"s));
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200257 datastore.setLeaf("/example-schema:down", bool{true});
258 datastore.commitChanges();
259 }
260
Jan Kundrátb331b552020-01-23 15:25:29 +0100261 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200262 REQUIRE(datastore.getItems("/example-schema:down") == expected);
263 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200264
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200265 SECTION("getting items from the whole module")
266 {
267 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100268 REQUIRE_CALL(mock, write("/example-schema:up", std::nullopt, "true"s));
269 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "false"s));
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200270 datastore.setLeaf("/example-schema:up", bool{true});
271 datastore.setLeaf("/example-schema:down", bool{false});
272 datastore.commitChanges();
273 }
274
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200275 DatastoreAccess::Tree expected{
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200276 // Sysrepo always returns containers when getting values, but
277 // libnetconf does not. This is fine by the YANG standard:
278 // https://tools.ietf.org/html/rfc7950#section-7.5.7 Furthermore,
279 // NetconfAccess implementation actually only iterates over leafs,
280 // so even if libnetconf did include containers, they wouldn't get
281 // shown here anyway. With sysrepo2, this won't be necessary,
282 // because it'll use the same data structure as libnetconf, so the
283 // results will be consistent.
284#ifdef sysrepo_BACKEND
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100285 {"/example-schema:inventory", special_{SpecialValue::Container}},
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200286 {"/example-schema:lol", special_{SpecialValue::Container}},
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200287#endif
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200288 {"/example-schema:up", bool{true}},
289 {"/example-schema:down", bool{false}}};
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200290 REQUIRE(datastore.getItems("/example-schema:*") == expected);
291 }
292
Václav Kubernát152ce222019-12-19 12:23:32 +0100293 SECTION("getItems returns correct datatypes")
294 {
295 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100296 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "lol"s));
Václav Kubernát152ce222019-12-19 12:23:32 +0100297 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
298 datastore.commitChanges();
299 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100300 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100301
302 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
303 }
304
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100305 SECTION("getItems on a list")
306 {
307 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100308 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']", std::nullopt, ""s));
309 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s));
310 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']", std::nullopt, ""s));
311 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s));
312 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']", std::nullopt, ""s));
313 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']/name", std::nullopt, "Petr"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200314 datastore.createItem("/example-schema:person[name='Jan']");
315 datastore.createItem("/example-schema:person[name='Michal']");
316 datastore.createItem("/example-schema:person[name='Petr']");
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100317 datastore.commitChanges();
318 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100319 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100320 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100321 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100322 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100323 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100324 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100325 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
326 };
327
328 REQUIRE(datastore.getItems("/example-schema:person") == expected);
329 }
330
Václav Kubernát69aabe92020-01-24 16:53:12 +0100331 SECTION("presence containers")
332 {
333 DatastoreAccess::Tree expected;
334 // Make sure it's not there before we create it
335 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
336
337 {
338 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200339 datastore.createItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100340 datastore.commitChanges();
341 }
342 expected = {
343 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
344 };
345 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
346
347 // Make sure it's not there after we delete it
348 {
349 REQUIRE_CALL(mock, write("/example-schema:pContainer", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200350 datastore.deleteItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100351 datastore.commitChanges();
352 }
353 expected = {};
354 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100355 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100356
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200357 SECTION("creating a non-existing schema node as a container")
358 {
359 catching<OnInvalidSchemaPathCreate>([&]{
360 datastore.createItem("/example-schema:non-existing-presence-container");
361 datastore.commitChanges();
362 });
363 }
364
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200365 SECTION("deleting a non-existing schema node as a container or leaf")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200366 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200367 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200368 datastore.deleteItem("/example-schema:non-existing-presence-container");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200369 datastore.commitChanges();
370 });
371 }
372
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100373 SECTION("nested presence container")
374 {
375 DatastoreAccess::Tree expected;
376 // Make sure it's not there before we create it
377 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
378 {
379 REQUIRE_CALL(mock, write("/example-schema:inventory", std::nullopt, ""s));
380 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200381 datastore.createItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100382 datastore.commitChanges();
383 }
384 expected = {
385 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
386 };
387 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
388 {
389 REQUIRE_CALL(mock, write("/example-schema:inventory", ""s, std::nullopt));
390 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200391 datastore.deleteItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100392 datastore.commitChanges();
393 }
394 expected = {};
395 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100396 }
397
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100398 SECTION("floats")
399 {
400 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
401 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.4"s));
402 datastore.commitChanges();
403 DatastoreAccess::Tree expected {
404 {"/example-schema:leafDecimal", 123.4},
405 };
406 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
407 }
408
Jan Kundrát3ff50122020-05-07 00:37:50 +0200409 SECTION("unions")
410 {
411 datastore.setLeaf("/example-schema:unionIntString", int32_t{10});
412 REQUIRE_CALL(mock, write("/example-schema:unionIntString", std::nullopt, "10"s));
413 datastore.commitChanges();
414 DatastoreAccess::Tree expected {
415 {"/example-schema:unionIntString", int32_t{10}},
416 };
417 REQUIRE(datastore.getItems("/example-schema:unionIntString") == expected);
418 }
419
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200420 SECTION("identityref") {
421 datastore.setLeaf("/example-schema:beast", identityRef_{"example-schema", "Mammal"});
422 REQUIRE_CALL(mock, write("/example-schema:beast", std::nullopt, "example-schema:Mammal"s));
423 datastore.commitChanges();
424 DatastoreAccess::Tree expected {
425 {"/example-schema:beast", identityRef_{"example-schema", "Mammal"}},
426 };
427 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
428
429 datastore.setLeaf("/example-schema:beast", identityRef_{"Whale"});
430 REQUIRE_CALL(mock, write("/example-schema:beast", "example-schema:Mammal", "example-schema:Whale"s));
431 datastore.commitChanges();
432 expected = {
433 {"/example-schema:beast", identityRef_{"example-schema", "Whale"}},
434 };
435 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
436 }
437
Jan Kundrát68985442020-05-07 02:15:34 +0200438 SECTION("binary")
439 {
440 datastore.setLeaf("/example-schema:blob", binary_{"cHduegByIQ=="s});
441 REQUIRE_CALL(mock, write("/example-schema:blob", std::nullopt, "cHduegByIQ=="s));
442 datastore.commitChanges();
443 DatastoreAccess::Tree expected {
444 {"/example-schema:blob", binary_{"cHduegByIQ=="s}},
445 };
446 REQUIRE(datastore.getItems("/example-schema:blob") == expected);
447 }
448
Jan Kundrát379bb572020-05-07 03:23:13 +0200449 SECTION("empty")
450 {
451 datastore.setLeaf("/example-schema:dummy", empty_{});
452 REQUIRE_CALL(mock, write("/example-schema:dummy", std::nullopt, ""s));
453 datastore.commitChanges();
454 DatastoreAccess::Tree expected {
455 {"/example-schema:dummy", empty_{}},
456 };
457 REQUIRE(datastore.getItems("/example-schema:dummy") == expected);
458 }
459
Jan Kundrátbb525b42020-02-04 11:56:59 +0100460 SECTION("operational data")
461 {
462 MockDataSupplier mockOpsData;
463 OperationalDataSubscription opsDataSub("/example-schema:temperature", mockOpsData);
464 DatastoreAccess::Tree expected;
465 std::string xpath;
466 SECTION("temperature")
467 {
468 expected = {{"/example-schema:temperature", int32_t{22}}};
469 xpath = "/example-schema:temperature";
470 }
471
472 REQUIRE_CALL(mockOpsData, get_data(xpath)).RETURN(expected);
473 REQUIRE(datastore.getItems(xpath) == expected);
474 }
475
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200476 SECTION("leaf list")
477 {
478 DatastoreAccess::Tree expected;
479 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "0.0.0.0"s));
480 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "127.0.0.1"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200481 datastore.createItem("/example-schema:addresses[.='0.0.0.0']");
482 datastore.createItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200483 datastore.commitChanges();
484 expected = {
485 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
486 {"/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s},
487 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
488 };
489 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
490
491 REQUIRE_CALL(mock, write("/example-schema:addresses", "0.0.0.0"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200492 datastore.deleteItem("/example-schema:addresses[.='0.0.0.0']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200493 datastore.commitChanges();
494 expected = {
495 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
496 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
497 };
498 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
499
500 REQUIRE_CALL(mock, write("/example-schema:addresses", "127.0.0.1"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200501 datastore.deleteItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200502 datastore.commitChanges();
503 expected = {};
504 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
505 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100506
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200507 SECTION("deleting a non-existing leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200508 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200509 catching<OnKeyNotFound>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200510 datastore.deleteItem("/example-schema:addresses[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200511 datastore.commitChanges();
512 });
513 }
514
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200515 SECTION("accessing a non-existing schema node as a leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200516 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200517 catching<OnInvalidSchemaPathCreate>([&]{
518 datastore.createItem("/example-schema:non-existing[.='non-existing']");
519 datastore.commitChanges();
520 });
521
522 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200523 datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200524 datastore.commitChanges();
525 });
526 }
527
Václav Kubernát7160a132020-04-03 02:11:01 +0200528 SECTION("copying data from startup refreshes the data")
529 {
530 {
531 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
532 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "123"s));
533 datastore.setLeaf("/example-schema:leafInt16", int16_t{123});
534 datastore.commitChanges();
535 }
536 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{{"/example-schema:leafInt16", int16_t{123}}});
537 REQUIRE_CALL(mock, write("/example-schema:leafInt16", "123"s, std::nullopt));
538 datastore.copyConfig(Datastore::Startup, Datastore::Running);
539 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
540 }
541
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200542 SECTION("moving leaflist instances")
543 {
544 DatastoreAccess::Tree expected;
545 {
546 // sysrepo does this twice for some reason, it's possibly a bug
547 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "http"s)).TIMES(2);
548 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "ftp"s));
549 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
550 REQUIRE_CALL(mock, write("/example-schema:protocols", "http"s, "ftp"s));
551 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "pop3"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200552 datastore.createItem("/example-schema:protocols[.='http']");
553 datastore.createItem("/example-schema:protocols[.='ftp']");
554 datastore.createItem("/example-schema:protocols[.='pop3']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200555 datastore.commitChanges();
556 expected = {
557 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
558 {"/example-schema:protocols[.='http']", "http"s},
559 {"/example-schema:protocols[.='ftp']", "ftp"s},
560 {"/example-schema:protocols[.='pop3']", "pop3"s},
561 };
562 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
563 }
564
565 std::string sourcePath;
566 SECTION("begin")
567 {
568 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
569 sourcePath = "/example-schema:protocols[.='pop3']";
570 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
571 datastore.commitChanges();
572 expected = {
573 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
574 {"/example-schema:protocols[.='pop3']", "pop3"s},
575 {"/example-schema:protocols[.='http']", "http"s},
576 {"/example-schema:protocols[.='ftp']", "ftp"s},
577 };
578 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
579 }
580
581 SECTION("end")
582 {
583 sourcePath = "/example-schema:protocols[.='http']";
584 REQUIRE_CALL(mock, write("/example-schema:protocols", "pop3"s, "http"s));
585 datastore.moveItem(sourcePath, yang::move::Absolute::End);
586 datastore.commitChanges();
587 expected = {
588 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
589 {"/example-schema:protocols[.='ftp']", "ftp"s},
590 {"/example-schema:protocols[.='pop3']", "pop3"s},
591 {"/example-schema:protocols[.='http']", "http"s},
592 };
593 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
594 }
595
596 SECTION("after")
597 {
598 sourcePath = "/example-schema:protocols[.='http']";
599 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
600 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{".", "ftp"s}}});
601 datastore.commitChanges();
602 expected = {
603 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
604 {"/example-schema:protocols[.='ftp']", "ftp"s},
605 {"/example-schema:protocols[.='http']", "http"s},
606 {"/example-schema:protocols[.='pop3']", "pop3"s},
607 };
608 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
609 }
610
611 SECTION("before")
612 {
613 sourcePath = "/example-schema:protocols[.='http']";
614 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
615 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{".", "pop3"s}}});
616 datastore.commitChanges();
617 expected = {
618 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
619 {"/example-schema:protocols[.='ftp']", "ftp"s},
620 {"/example-schema:protocols[.='http']", "http"s},
621 {"/example-schema:protocols[.='pop3']", "pop3"s},
622 };
623 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
624 }
625 }
626
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200627 SECTION("moving non-existing schema nodes")
628 {
629 catching<OnInvalidSchemaPathMove>([&]{
630 datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
631 datastore.commitChanges();
632 });
633 }
634
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200635 SECTION("moving list instances")
636 {
637 DatastoreAccess::Tree expected;
638 {
639 // sysrepo does this twice for some reason, it's possibly a bug
640 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", std::nullopt, ""s)).TIMES(2);
641 REQUIRE_CALL(mock, write("/example-schema:players[name='John']/name", std::nullopt, "John"s));
642 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']", std::nullopt, ""s));
643 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']", ""s, ""s));
644 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']/name", std::nullopt, "Eve"s));
645 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", std::nullopt, ""s));
646 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']/name", std::nullopt, "Adam"s));
647 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", ""s, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200648 datastore.createItem("/example-schema:players[name='John']");
649 datastore.createItem("/example-schema:players[name='Eve']");
650 datastore.createItem("/example-schema:players[name='Adam']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200651 datastore.commitChanges();
652 expected = {
653 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
654 {"/example-schema:players[name='John']/name", "John"s},
655 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
656 {"/example-schema:players[name='Eve']/name", "Eve"s},
657 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
658 {"/example-schema:players[name='Adam']/name", "Adam"s},
659 };
660 REQUIRE(datastore.getItems("/example-schema:players") == expected);
661 }
662
663 std::string sourcePath;
664 SECTION("begin")
665 {
666 sourcePath = "/example-schema:players[name='Adam']";
667 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", std::nullopt, ""s));
668 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
669 datastore.commitChanges();
670 expected = {
671 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
672 {"/example-schema:players[name='Adam']/name", "Adam"s},
673 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
674 {"/example-schema:players[name='John']/name", "John"s},
675 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
676 {"/example-schema:players[name='Eve']/name", "Eve"s},
677 };
678 REQUIRE(datastore.getItems("/example-schema:players") == expected);
679 }
680
681 SECTION("end")
682 {
683 sourcePath = "/example-schema:players[name='John']";
684 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
685 datastore.moveItem(sourcePath, yang::move::Absolute::End);
686 datastore.commitChanges();
687 expected = {
688 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
689 {"/example-schema:players[name='Eve']/name", "Eve"s},
690 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
691 {"/example-schema:players[name='Adam']/name", "Adam"s},
692 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
693 {"/example-schema:players[name='John']/name", "John"s},
694 };
695 REQUIRE(datastore.getItems("/example-schema:players") == expected);
696 }
697
698 SECTION("after")
699 {
700 sourcePath = "/example-schema:players[name='John']";
701 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
702 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{"name", "Eve"s}}});
703 datastore.commitChanges();
704 expected = {
705 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
706 {"/example-schema:players[name='Eve']/name", "Eve"s},
707 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
708 {"/example-schema:players[name='John']/name", "John"s},
709 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
710 {"/example-schema:players[name='Adam']/name", "Adam"s},
711 };
712 REQUIRE(datastore.getItems("/example-schema:players") == expected);
713 }
714
715 SECTION("before")
716 {
717 sourcePath = "/example-schema:players[name='John']";
718 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
719 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{"name", "Adam"s}}});
720 datastore.commitChanges();
721 expected = {
722 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
723 {"/example-schema:players[name='Eve']/name", "Eve"s},
724 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
725 {"/example-schema:players[name='John']/name", "John"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
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200733 SECTION("getting /")
734 {
735 {
736 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "64"s));
737 datastore.setLeaf("/example-schema:leafInt32", 64);
738 datastore.commitChanges();
739 }
740
741 DatastoreAccess::Tree expected{
742 // Sysrepo always returns containers when getting values, but
743 // libnetconf does not. This is fine by the YANG standard:
744 // https://tools.ietf.org/html/rfc7950#section-7.5.7 Furthermore,
745 // NetconfAccess implementation actually only iterates over leafs,
746 // so even if libnetconf did include containers, they wouldn't get
747 // shown here anyway. With sysrepo2, this won't be necessary,
748 // because it'll use the same data structure as libnetconf, so the
749 // results will be consistent.
750#ifdef sysrepo_BACKEND
751 {"/example-schema:inventory", special_{SpecialValue::Container}},
752 {"/example-schema:lol", special_{SpecialValue::Container}},
753#endif
754 {"/example-schema:leafInt32", 64}};
755 auto items = datastore.getItems("/");
756 // This tests if we at least get the data WE added.
757 REQUIRE(std::all_of(expected.begin(), expected.end(), [items] (const auto& item) { return std::find(items.begin(), items.end(), item) != items.end(); }));
758 }
759
Václav Kubernát36986c52020-06-25 10:30:05 +0200760 SECTION("setting and removing without commit")
761 {
762 datastore.setLeaf("/example-schema:leafInt32", 64);
763 datastore.deleteItem("/example-schema:leafInt32");
764 }
765
Václav Kubernát73109382018-09-14 19:52:03 +0200766 waitForCompletionAndBitMore(seq1);
767}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100768
769class RpcCb: public sysrepo::Callback {
770 int rpc(const char *xpath, const ::sysrepo::S_Vals input, ::sysrepo::S_Vals_Holder output, void *) override
771 {
772 const auto nukes = "/example-schema:launch-nukes"s;
773 if (xpath == "/example-schema:noop"s) {
774 return SR_ERR_OK;
775 } else if (xpath == nukes) {
776 uint64_t kilotons = 0;
777 bool hasCities = false;
778 for (size_t i = 0; i < input->val_cnt(); ++i) {
779 const auto& val = input->val(i);
780 if (val->xpath() == nukes + "/payload/kilotons") {
781 kilotons = val->data()->get_uint64();
782 } else if (val->xpath() == nukes + "/payload") {
783 // ignore, container
784 } else if (val->xpath() == nukes + "/description") {
785 // unused
786 } else if (std::string_view{val->xpath()}.find(nukes + "/cities") == 0) {
787 hasCities = true;
788 } else {
789 throw std::runtime_error("RPC launch-nukes: unexpected input "s + val->xpath());
790 }
791 }
792 if (kilotons == 333'666) {
793 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
794 return SR_ERR_OK;
795 }
796 auto buf = output->allocate(2);
797 size_t i = 0;
798 buf->val(i++)->set((nukes + "/blast-radius").c_str(), uint32_t{33'666});
799 buf->val(i++)->set((nukes + "/actual-yield").c_str(), static_cast<uint64_t>(1.33 * kilotons));
800 if (hasCities) {
801 buf = output->reallocate(output->val_cnt() + 2);
802 buf->val(i++)->set((nukes + "/damaged-places/targets[city='London']/city").c_str(), "London");
803 buf->val(i++)->set((nukes + "/damaged-places/targets[city='Berlin']/city").c_str(), "Berlin");
804 }
805 return SR_ERR_OK;
806 }
807 throw std::runtime_error("unrecognized RPC");
808 }
809};
810
811TEST_CASE("rpc") {
812 trompeloeil::sequence seq1;
813 auto srConn = std::make_shared<sysrepo::Connection>("netconf-cli-test-rpc");
814 auto srSession = std::make_shared<sysrepo::Session>(srConn);
815 auto srSubscription = std::make_shared<sysrepo::Subscribe>(srSession);
816 auto cb = std::make_shared<RpcCb>();
817 sysrepo::Logs{}.set_stderr(SR_LL_INF);
818 srSubscription->rpc_subscribe("/example-schema:noop", cb, nullptr, SR_SUBSCR_CTX_REUSE);
819 srSubscription->rpc_subscribe("/example-schema:launch-nukes", cb, nullptr, SR_SUBSCR_CTX_REUSE);
820
821#ifdef sysrepo_BACKEND
Václav Kubernát715c85c2020-04-14 01:46:08 +0200822 SysrepoAccess datastore("netconf-cli-test", Datastore::Running);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100823#elif defined(netconf_BACKEND)
824 NetconfAccess datastore(NETOPEER_SOCKET_PATH);
825#else
826#error "Unknown backend"
827#endif
828
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200829 SECTION("valid")
830 {
831 std::string rpc;
832 DatastoreAccess::Tree input, output;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100833
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200834 SECTION("noop") {
835 rpc = "/example-schema:noop";
836 }
837
838 SECTION("small nuke") {
839 rpc = "/example-schema:launch-nukes";
840 input = {
841 {"description", "dummy"s},
842 {"payload/kilotons", uint64_t{333'666}},
843 };
844 // no data are returned
845 }
846
847 SECTION("small nuke") {
848 rpc = "/example-schema:launch-nukes";
849 input = {
850 {"description", "dummy"s},
851 {"payload/kilotons", uint64_t{4}},
852 };
853 output = {
854 {"blast-radius", uint32_t{33'666}},
855 {"actual-yield", uint64_t{5}},
856 };
857 }
858
859 SECTION("with lists") {
860 rpc = "/example-schema:launch-nukes";
861 input = {
862 {"payload/kilotons", uint64_t{6}},
863 {"cities/targets[city='Prague']/city", "Prague"s},
864 };
865 output = {
866 {"blast-radius", uint32_t{33'666}},
867 {"actual-yield", uint64_t{7}},
868 {"damaged-places", special_{SpecialValue::PresenceContainer}},
869 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
870 {"damaged-places/targets[city='London']/city", "London"s},
871 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
872 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
873 };
874 }
875
876 REQUIRE(datastore.executeRpc(rpc, input) == output);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100877 }
878
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200879 SECTION("non-existing RPC")
880 {
881 REQUIRE_THROWS_AS(datastore.executeRpc("/example-schema:non-existing", DatastoreAccess::Tree{}), std::runtime_error);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100882 }
883
Jan Kundrát6ee84792020-01-24 01:43:36 +0100884 waitForCompletionAndBitMore(seq1);
885}