blob: 2837c84ae096def51c23a88a1d2bdfdb32b52fe9 [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át7ec214d2020-06-19 17:05:07 +0200149 SECTION("set a non-existing leaf")
150 {
151 catching<OnInvalidSchemaPathCreate>([&]{
152 datastore.setLeaf("/example-schema:non-existing", "what"s);
153 });
154 }
155
Václav Kubernát73109382018-09-14 19:52:03 +0200156 SECTION("create presence container")
157 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100158 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200159 datastore.createItem("/example-schema:pContainer");
Václav Kubernát73109382018-09-14 19:52:03 +0200160 datastore.commitChanges();
161 }
162
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100163 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200164 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100165 {
166 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", std::nullopt, ""s));
167 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", std::nullopt, "Nguyen"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200168 datastore.createItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100169 datastore.commitChanges();
170 }
171 {
172 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", ""s, std::nullopt));
173 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", "Nguyen"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200174 datastore.deleteItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100175 datastore.commitChanges();
176 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200177 }
178
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200179 SECTION("deleting non-existing list keys")
180 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200181 catching<OnKeyNotFound>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200182 datastore.deleteItem("/example-schema:person[name='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200183 datastore.commitChanges();
184 });
185 }
186
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200187 SECTION("accessing non-existing schema nodes as a list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200188 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200189 catching<OnInvalidSchemaPathCreate>([&]{
190 datastore.createItem("/example-schema:non-existing-list[xxx='blah']");
191 datastore.commitChanges();
192 });
193 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200194 datastore.deleteItem("/example-schema:non-existing-list[xxx='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200195 datastore.commitChanges();
196 });
197 }
198
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200199 SECTION("leafref pointing to a key of a list")
200 {
201 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100202 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']", std::nullopt, ""s));
203 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s));
204 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']", std::nullopt, ""s));
205 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s));
206 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']", std::nullopt, ""s));
207 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']/name", std::nullopt, "Kolafa"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200208 datastore.createItem("/example-schema:person[name='Dan']");
209 datastore.createItem("/example-schema:person[name='Elfi']");
210 datastore.createItem("/example-schema:person[name='Kolafa']");
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200211 datastore.commitChanges();
212 }
213
214 // The commitChanges method has to be called in each of the
215 // SECTIONs, because the REQUIRE_CALL only works inside the given
216 // SECTION.
217 SECTION("Dan")
218 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100219 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Dan"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200220 datastore.setLeaf("/example-schema:bossPerson", std::string{"Dan"});
221 datastore.commitChanges();
222 }
223
224 SECTION("Elfi")
225 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100226 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Elfi"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200227 datastore.setLeaf("/example-schema:bossPerson", std::string{"Elfi"});
228 datastore.commitChanges();
229 }
230
231 SECTION("Kolafa")
232 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100233 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Kolafa"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200234 datastore.setLeaf("/example-schema:bossPerson", std::string{"Kolafa"});
235 datastore.commitChanges();
236 }
237 }
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200238 SECTION("bool values get correctly represented as bools")
239 {
240 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100241 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "true"s));
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200242 datastore.setLeaf("/example-schema:down", bool{true});
243 datastore.commitChanges();
244 }
245
Jan Kundrátb331b552020-01-23 15:25:29 +0100246 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200247 REQUIRE(datastore.getItems("/example-schema:down") == expected);
248 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200249
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200250 SECTION("getting items from the whole module")
251 {
252 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100253 REQUIRE_CALL(mock, write("/example-schema:up", std::nullopt, "true"s));
254 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "false"s));
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200255 datastore.setLeaf("/example-schema:up", bool{true});
256 datastore.setLeaf("/example-schema:down", bool{false});
257 datastore.commitChanges();
258 }
259
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200260 DatastoreAccess::Tree expected{
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200261 // Sysrepo always returns containers when getting values, but
262 // libnetconf does not. This is fine by the YANG standard:
263 // https://tools.ietf.org/html/rfc7950#section-7.5.7 Furthermore,
264 // NetconfAccess implementation actually only iterates over leafs,
265 // so even if libnetconf did include containers, they wouldn't get
266 // shown here anyway. With sysrepo2, this won't be necessary,
267 // because it'll use the same data structure as libnetconf, so the
268 // results will be consistent.
269#ifdef sysrepo_BACKEND
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100270 {"/example-schema:inventory", special_{SpecialValue::Container}},
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200271 {"/example-schema:lol", special_{SpecialValue::Container}},
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200272#endif
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200273 {"/example-schema:up", bool{true}},
274 {"/example-schema:down", bool{false}}};
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200275 REQUIRE(datastore.getItems("/example-schema:*") == expected);
276 }
277
Václav Kubernát152ce222019-12-19 12:23:32 +0100278 SECTION("getItems returns correct datatypes")
279 {
280 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100281 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "lol"s));
Václav Kubernát152ce222019-12-19 12:23:32 +0100282 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
283 datastore.commitChanges();
284 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100285 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100286
287 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
288 }
289
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100290 SECTION("getItems on a list")
291 {
292 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100293 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']", std::nullopt, ""s));
294 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s));
295 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']", std::nullopt, ""s));
296 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s));
297 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']", std::nullopt, ""s));
298 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']/name", std::nullopt, "Petr"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200299 datastore.createItem("/example-schema:person[name='Jan']");
300 datastore.createItem("/example-schema:person[name='Michal']");
301 datastore.createItem("/example-schema:person[name='Petr']");
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100302 datastore.commitChanges();
303 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100304 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100305 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100306 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100307 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100308 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100309 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100310 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
311 };
312
313 REQUIRE(datastore.getItems("/example-schema:person") == expected);
314 }
315
Václav Kubernát69aabe92020-01-24 16:53:12 +0100316 SECTION("presence containers")
317 {
318 DatastoreAccess::Tree expected;
319 // Make sure it's not there before we create it
320 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
321
322 {
323 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200324 datastore.createItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100325 datastore.commitChanges();
326 }
327 expected = {
328 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
329 };
330 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
331
332 // Make sure it's not there after we delete it
333 {
334 REQUIRE_CALL(mock, write("/example-schema:pContainer", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200335 datastore.deleteItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100336 datastore.commitChanges();
337 }
338 expected = {};
339 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100340 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100341
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200342 SECTION("creating a non-existing schema node as a container")
343 {
344 catching<OnInvalidSchemaPathCreate>([&]{
345 datastore.createItem("/example-schema:non-existing-presence-container");
346 datastore.commitChanges();
347 });
348 }
349
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200350 SECTION("deleting a non-existing schema node as a container or leaf")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200351 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200352 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200353 datastore.deleteItem("/example-schema:non-existing-presence-container");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200354 datastore.commitChanges();
355 });
356 }
357
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100358 SECTION("nested presence container")
359 {
360 DatastoreAccess::Tree expected;
361 // Make sure it's not there before we create it
362 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
363 {
364 REQUIRE_CALL(mock, write("/example-schema:inventory", std::nullopt, ""s));
365 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200366 datastore.createItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100367 datastore.commitChanges();
368 }
369 expected = {
370 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
371 };
372 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
373 {
374 REQUIRE_CALL(mock, write("/example-schema:inventory", ""s, std::nullopt));
375 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200376 datastore.deleteItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100377 datastore.commitChanges();
378 }
379 expected = {};
380 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100381 }
382
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100383 SECTION("floats")
384 {
385 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
386 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.4"s));
387 datastore.commitChanges();
388 DatastoreAccess::Tree expected {
389 {"/example-schema:leafDecimal", 123.4},
390 };
391 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
392 }
393
Jan Kundrát3ff50122020-05-07 00:37:50 +0200394 SECTION("unions")
395 {
396 datastore.setLeaf("/example-schema:unionIntString", int32_t{10});
397 REQUIRE_CALL(mock, write("/example-schema:unionIntString", std::nullopt, "10"s));
398 datastore.commitChanges();
399 DatastoreAccess::Tree expected {
400 {"/example-schema:unionIntString", int32_t{10}},
401 };
402 REQUIRE(datastore.getItems("/example-schema:unionIntString") == expected);
403 }
404
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200405 SECTION("identityref") {
406 datastore.setLeaf("/example-schema:beast", identityRef_{"example-schema", "Mammal"});
407 REQUIRE_CALL(mock, write("/example-schema:beast", std::nullopt, "example-schema:Mammal"s));
408 datastore.commitChanges();
409 DatastoreAccess::Tree expected {
410 {"/example-schema:beast", identityRef_{"example-schema", "Mammal"}},
411 };
412 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
413
414 datastore.setLeaf("/example-schema:beast", identityRef_{"Whale"});
415 REQUIRE_CALL(mock, write("/example-schema:beast", "example-schema:Mammal", "example-schema:Whale"s));
416 datastore.commitChanges();
417 expected = {
418 {"/example-schema:beast", identityRef_{"example-schema", "Whale"}},
419 };
420 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
421 }
422
Jan Kundrát68985442020-05-07 02:15:34 +0200423 SECTION("binary")
424 {
425 datastore.setLeaf("/example-schema:blob", binary_{"cHduegByIQ=="s});
426 REQUIRE_CALL(mock, write("/example-schema:blob", std::nullopt, "cHduegByIQ=="s));
427 datastore.commitChanges();
428 DatastoreAccess::Tree expected {
429 {"/example-schema:blob", binary_{"cHduegByIQ=="s}},
430 };
431 REQUIRE(datastore.getItems("/example-schema:blob") == expected);
432 }
433
Jan Kundrát379bb572020-05-07 03:23:13 +0200434 SECTION("empty")
435 {
436 datastore.setLeaf("/example-schema:dummy", empty_{});
437 REQUIRE_CALL(mock, write("/example-schema:dummy", std::nullopt, ""s));
438 datastore.commitChanges();
439 DatastoreAccess::Tree expected {
440 {"/example-schema:dummy", empty_{}},
441 };
442 REQUIRE(datastore.getItems("/example-schema:dummy") == expected);
443 }
444
Jan Kundrátbb525b42020-02-04 11:56:59 +0100445 SECTION("operational data")
446 {
447 MockDataSupplier mockOpsData;
448 OperationalDataSubscription opsDataSub("/example-schema:temperature", mockOpsData);
449 DatastoreAccess::Tree expected;
450 std::string xpath;
451 SECTION("temperature")
452 {
453 expected = {{"/example-schema:temperature", int32_t{22}}};
454 xpath = "/example-schema:temperature";
455 }
456
457 REQUIRE_CALL(mockOpsData, get_data(xpath)).RETURN(expected);
458 REQUIRE(datastore.getItems(xpath) == expected);
459 }
460
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200461 SECTION("leaf list")
462 {
463 DatastoreAccess::Tree expected;
464 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "0.0.0.0"s));
465 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "127.0.0.1"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200466 datastore.createItem("/example-schema:addresses[.='0.0.0.0']");
467 datastore.createItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200468 datastore.commitChanges();
469 expected = {
470 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
471 {"/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s},
472 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
473 };
474 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
475
476 REQUIRE_CALL(mock, write("/example-schema:addresses", "0.0.0.0"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200477 datastore.deleteItem("/example-schema:addresses[.='0.0.0.0']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200478 datastore.commitChanges();
479 expected = {
480 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
481 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
482 };
483 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
484
485 REQUIRE_CALL(mock, write("/example-schema:addresses", "127.0.0.1"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200486 datastore.deleteItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200487 datastore.commitChanges();
488 expected = {};
489 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
490 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100491
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200492 SECTION("deleting a non-existing leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200493 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200494 catching<OnKeyNotFound>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200495 datastore.deleteItem("/example-schema:addresses[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200496 datastore.commitChanges();
497 });
498 }
499
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200500 SECTION("accessing a non-existing schema node as a leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200501 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200502 catching<OnInvalidSchemaPathCreate>([&]{
503 datastore.createItem("/example-schema:non-existing[.='non-existing']");
504 datastore.commitChanges();
505 });
506
507 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200508 datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200509 datastore.commitChanges();
510 });
511 }
512
Václav Kubernát7160a132020-04-03 02:11:01 +0200513 SECTION("copying data from startup refreshes the data")
514 {
515 {
516 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
517 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "123"s));
518 datastore.setLeaf("/example-schema:leafInt16", int16_t{123});
519 datastore.commitChanges();
520 }
521 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{{"/example-schema:leafInt16", int16_t{123}}});
522 REQUIRE_CALL(mock, write("/example-schema:leafInt16", "123"s, std::nullopt));
523 datastore.copyConfig(Datastore::Startup, Datastore::Running);
524 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
525 }
526
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200527 SECTION("moving leaflist instances")
528 {
529 DatastoreAccess::Tree expected;
530 {
531 // sysrepo does this twice for some reason, it's possibly a bug
532 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "http"s)).TIMES(2);
533 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "ftp"s));
534 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
535 REQUIRE_CALL(mock, write("/example-schema:protocols", "http"s, "ftp"s));
536 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "pop3"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200537 datastore.createItem("/example-schema:protocols[.='http']");
538 datastore.createItem("/example-schema:protocols[.='ftp']");
539 datastore.createItem("/example-schema:protocols[.='pop3']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200540 datastore.commitChanges();
541 expected = {
542 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
543 {"/example-schema:protocols[.='http']", "http"s},
544 {"/example-schema:protocols[.='ftp']", "ftp"s},
545 {"/example-schema:protocols[.='pop3']", "pop3"s},
546 };
547 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
548 }
549
550 std::string sourcePath;
551 SECTION("begin")
552 {
553 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
554 sourcePath = "/example-schema:protocols[.='pop3']";
555 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
556 datastore.commitChanges();
557 expected = {
558 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
559 {"/example-schema:protocols[.='pop3']", "pop3"s},
560 {"/example-schema:protocols[.='http']", "http"s},
561 {"/example-schema:protocols[.='ftp']", "ftp"s},
562 };
563 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
564 }
565
566 SECTION("end")
567 {
568 sourcePath = "/example-schema:protocols[.='http']";
569 REQUIRE_CALL(mock, write("/example-schema:protocols", "pop3"s, "http"s));
570 datastore.moveItem(sourcePath, yang::move::Absolute::End);
571 datastore.commitChanges();
572 expected = {
573 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
574 {"/example-schema:protocols[.='ftp']", "ftp"s},
575 {"/example-schema:protocols[.='pop3']", "pop3"s},
576 {"/example-schema:protocols[.='http']", "http"s},
577 };
578 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
579 }
580
581 SECTION("after")
582 {
583 sourcePath = "/example-schema:protocols[.='http']";
584 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
585 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{".", "ftp"s}}});
586 datastore.commitChanges();
587 expected = {
588 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
589 {"/example-schema:protocols[.='ftp']", "ftp"s},
590 {"/example-schema:protocols[.='http']", "http"s},
591 {"/example-schema:protocols[.='pop3']", "pop3"s},
592 };
593 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
594 }
595
596 SECTION("before")
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::Before, {{".", "pop3"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
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200612 SECTION("moving non-existing schema nodes")
613 {
614 catching<OnInvalidSchemaPathMove>([&]{
615 datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
616 datastore.commitChanges();
617 });
618 }
619
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200620 SECTION("moving list instances")
621 {
622 DatastoreAccess::Tree expected;
623 {
624 // sysrepo does this twice for some reason, it's possibly a bug
625 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", std::nullopt, ""s)).TIMES(2);
626 REQUIRE_CALL(mock, write("/example-schema:players[name='John']/name", std::nullopt, "John"s));
627 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']", std::nullopt, ""s));
628 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']", ""s, ""s));
629 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']/name", std::nullopt, "Eve"s));
630 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", std::nullopt, ""s));
631 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']/name", std::nullopt, "Adam"s));
632 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", ""s, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200633 datastore.createItem("/example-schema:players[name='John']");
634 datastore.createItem("/example-schema:players[name='Eve']");
635 datastore.createItem("/example-schema:players[name='Adam']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200636 datastore.commitChanges();
637 expected = {
638 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
639 {"/example-schema:players[name='John']/name", "John"s},
640 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
641 {"/example-schema:players[name='Eve']/name", "Eve"s},
642 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
643 {"/example-schema:players[name='Adam']/name", "Adam"s},
644 };
645 REQUIRE(datastore.getItems("/example-schema:players") == expected);
646 }
647
648 std::string sourcePath;
649 SECTION("begin")
650 {
651 sourcePath = "/example-schema:players[name='Adam']";
652 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", std::nullopt, ""s));
653 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
654 datastore.commitChanges();
655 expected = {
656 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
657 {"/example-schema:players[name='Adam']/name", "Adam"s},
658 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
659 {"/example-schema:players[name='John']/name", "John"s},
660 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
661 {"/example-schema:players[name='Eve']/name", "Eve"s},
662 };
663 REQUIRE(datastore.getItems("/example-schema:players") == expected);
664 }
665
666 SECTION("end")
667 {
668 sourcePath = "/example-schema:players[name='John']";
669 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
670 datastore.moveItem(sourcePath, yang::move::Absolute::End);
671 datastore.commitChanges();
672 expected = {
673 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
674 {"/example-schema:players[name='Eve']/name", "Eve"s},
675 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
676 {"/example-schema:players[name='Adam']/name", "Adam"s},
677 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
678 {"/example-schema:players[name='John']/name", "John"s},
679 };
680 REQUIRE(datastore.getItems("/example-schema:players") == expected);
681 }
682
683 SECTION("after")
684 {
685 sourcePath = "/example-schema:players[name='John']";
686 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
687 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{"name", "Eve"s}}});
688 datastore.commitChanges();
689 expected = {
690 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
691 {"/example-schema:players[name='Eve']/name", "Eve"s},
692 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
693 {"/example-schema:players[name='John']/name", "John"s},
694 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
695 {"/example-schema:players[name='Adam']/name", "Adam"s},
696 };
697 REQUIRE(datastore.getItems("/example-schema:players") == expected);
698 }
699
700 SECTION("before")
701 {
702 sourcePath = "/example-schema:players[name='John']";
703 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
704 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{"name", "Adam"s}}});
705 datastore.commitChanges();
706 expected = {
707 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
708 {"/example-schema:players[name='Eve']/name", "Eve"s},
709 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
710 {"/example-schema:players[name='John']/name", "John"s},
711 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
712 {"/example-schema:players[name='Adam']/name", "Adam"s},
713 };
714 REQUIRE(datastore.getItems("/example-schema:players") == expected);
715 }
716 }
717
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200718 SECTION("getting /")
719 {
720 {
721 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "64"s));
722 datastore.setLeaf("/example-schema:leafInt32", 64);
723 datastore.commitChanges();
724 }
725
726 DatastoreAccess::Tree expected{
727 // Sysrepo always returns containers when getting values, but
728 // libnetconf does not. This is fine by the YANG standard:
729 // https://tools.ietf.org/html/rfc7950#section-7.5.7 Furthermore,
730 // NetconfAccess implementation actually only iterates over leafs,
731 // so even if libnetconf did include containers, they wouldn't get
732 // shown here anyway. With sysrepo2, this won't be necessary,
733 // because it'll use the same data structure as libnetconf, so the
734 // results will be consistent.
735#ifdef sysrepo_BACKEND
736 {"/example-schema:inventory", special_{SpecialValue::Container}},
737 {"/example-schema:lol", special_{SpecialValue::Container}},
738#endif
739 {"/example-schema:leafInt32", 64}};
740 auto items = datastore.getItems("/");
741 // This tests if we at least get the data WE added.
742 REQUIRE(std::all_of(expected.begin(), expected.end(), [items] (const auto& item) { return std::find(items.begin(), items.end(), item) != items.end(); }));
743 }
744
Václav Kubernát73109382018-09-14 19:52:03 +0200745 waitForCompletionAndBitMore(seq1);
746}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100747
748class RpcCb: public sysrepo::Callback {
749 int rpc(const char *xpath, const ::sysrepo::S_Vals input, ::sysrepo::S_Vals_Holder output, void *) override
750 {
751 const auto nukes = "/example-schema:launch-nukes"s;
752 if (xpath == "/example-schema:noop"s) {
753 return SR_ERR_OK;
754 } else if (xpath == nukes) {
755 uint64_t kilotons = 0;
756 bool hasCities = false;
757 for (size_t i = 0; i < input->val_cnt(); ++i) {
758 const auto& val = input->val(i);
759 if (val->xpath() == nukes + "/payload/kilotons") {
760 kilotons = val->data()->get_uint64();
761 } else if (val->xpath() == nukes + "/payload") {
762 // ignore, container
763 } else if (val->xpath() == nukes + "/description") {
764 // unused
765 } else if (std::string_view{val->xpath()}.find(nukes + "/cities") == 0) {
766 hasCities = true;
767 } else {
768 throw std::runtime_error("RPC launch-nukes: unexpected input "s + val->xpath());
769 }
770 }
771 if (kilotons == 333'666) {
772 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
773 return SR_ERR_OK;
774 }
775 auto buf = output->allocate(2);
776 size_t i = 0;
777 buf->val(i++)->set((nukes + "/blast-radius").c_str(), uint32_t{33'666});
778 buf->val(i++)->set((nukes + "/actual-yield").c_str(), static_cast<uint64_t>(1.33 * kilotons));
779 if (hasCities) {
780 buf = output->reallocate(output->val_cnt() + 2);
781 buf->val(i++)->set((nukes + "/damaged-places/targets[city='London']/city").c_str(), "London");
782 buf->val(i++)->set((nukes + "/damaged-places/targets[city='Berlin']/city").c_str(), "Berlin");
783 }
784 return SR_ERR_OK;
785 }
786 throw std::runtime_error("unrecognized RPC");
787 }
788};
789
790TEST_CASE("rpc") {
791 trompeloeil::sequence seq1;
792 auto srConn = std::make_shared<sysrepo::Connection>("netconf-cli-test-rpc");
793 auto srSession = std::make_shared<sysrepo::Session>(srConn);
794 auto srSubscription = std::make_shared<sysrepo::Subscribe>(srSession);
795 auto cb = std::make_shared<RpcCb>();
796 sysrepo::Logs{}.set_stderr(SR_LL_INF);
797 srSubscription->rpc_subscribe("/example-schema:noop", cb, nullptr, SR_SUBSCR_CTX_REUSE);
798 srSubscription->rpc_subscribe("/example-schema:launch-nukes", cb, nullptr, SR_SUBSCR_CTX_REUSE);
799
800#ifdef sysrepo_BACKEND
Václav Kubernát715c85c2020-04-14 01:46:08 +0200801 SysrepoAccess datastore("netconf-cli-test", Datastore::Running);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100802#elif defined(netconf_BACKEND)
803 NetconfAccess datastore(NETOPEER_SOCKET_PATH);
804#else
805#error "Unknown backend"
806#endif
807
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200808 SECTION("valid")
809 {
810 std::string rpc;
811 DatastoreAccess::Tree input, output;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100812
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200813 SECTION("noop") {
814 rpc = "/example-schema:noop";
815 }
816
817 SECTION("small nuke") {
818 rpc = "/example-schema:launch-nukes";
819 input = {
820 {"description", "dummy"s},
821 {"payload/kilotons", uint64_t{333'666}},
822 };
823 // no data are returned
824 }
825
826 SECTION("small nuke") {
827 rpc = "/example-schema:launch-nukes";
828 input = {
829 {"description", "dummy"s},
830 {"payload/kilotons", uint64_t{4}},
831 };
832 output = {
833 {"blast-radius", uint32_t{33'666}},
834 {"actual-yield", uint64_t{5}},
835 };
836 }
837
838 SECTION("with lists") {
839 rpc = "/example-schema:launch-nukes";
840 input = {
841 {"payload/kilotons", uint64_t{6}},
842 {"cities/targets[city='Prague']/city", "Prague"s},
843 };
844 output = {
845 {"blast-radius", uint32_t{33'666}},
846 {"actual-yield", uint64_t{7}},
847 {"damaged-places", special_{SpecialValue::PresenceContainer}},
848 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
849 {"damaged-places/targets[city='London']/city", "London"s},
850 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
851 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
852 };
853 }
854
855 REQUIRE(datastore.executeRpc(rpc, input) == output);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100856 }
857
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200858 SECTION("non-existing RPC")
859 {
860 REQUIRE_THROWS_AS(datastore.executeRpc("/example-schema:non-existing", DatastoreAccess::Tree{}), std::runtime_error);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100861 }
862
Jan Kundrát6ee84792020-01-24 01:43:36 +0100863 waitForCompletionAndBitMore(seq1);
864}