blob: 10b6a6f664d499d74a4f4c3cd641b0e64e3cd68a [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;
Václav Kubernát74487df2020-06-04 01:29:28 +020017using OnInvalidRpcPath = sysrepo::sysrepo_exception;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020018using OnKeyNotFound = void;
Václav Kubernát74487df2020-06-04 01:29:28 +020019using OnRPC = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010020#elif defined(netconf_BACKEND)
Jan Kundrát7ec214d2020-06-19 17:05:07 +020021using OnInvalidSchemaPathCreate = std::runtime_error;
22using OnInvalidSchemaPathDelete = std::runtime_error;
23using OnInvalidSchemaPathMove = std::runtime_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020024using OnInvalidRpcPath = std::runtime_error;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020025using OnKeyNotFound = std::runtime_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020026using OnRPC = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010027#include "netconf_access.hpp"
28#include "netopeer_vars.hpp"
Václav Kubernát74487df2020-06-04 01:29:28 +020029#elif defined(yang_BACKEND)
30#include <fstream>
31#include "yang_access.hpp"
32#include "yang_access_test_vars.hpp"
33using OnInvalidSchemaPathCreate = DatastoreException;
34using OnInvalidSchemaPathDelete = DatastoreException;
35using OnInvalidSchemaPathMove = DatastoreException;
36using OnInvalidRpcPath = DatastoreException;
37using OnKeyNotFound = DatastoreException;
38using OnRPC = std::logic_error;
Václav Kubernátc31bd602019-03-07 11:44:48 +010039#else
40#error "Unknown backend"
41#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +010042#include "pretty_printers.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020043#include "sysrepo_subscription.hpp"
Václav Kubernát8e121ff2019-10-15 15:47:45 +020044#include "utils.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020045
Jan Kundrát6ee84792020-01-24 01:43:36 +010046using namespace std::literals::string_literals;
47
Václav Kubernát69aabe92020-01-24 16:53:12 +010048class MockRecorder : public trompeloeil::mock_interface<Recorder> {
Václav Kubernát73109382018-09-14 19:52:03 +020049public:
Václav Kubernát69aabe92020-01-24 16:53:12 +010050 IMPLEMENT_MOCK3(write);
Václav Kubernát73109382018-09-14 19:52:03 +020051};
52
Jan Kundrátbb525b42020-02-04 11:56:59 +010053class MockDataSupplier : public trompeloeil::mock_interface<DataSupplier> {
54public:
55 IMPLEMENT_CONST_MOCK1(get_data);
56};
57
Jan Kundrát7ec214d2020-06-19 17:05:07 +020058namespace {
59template <class ...> constexpr std::false_type always_false [[maybe_unused]] {};
60template <class Exception, typename Callable> void catching(const Callable& what) {
61
62 if constexpr (std::is_same_v<Exception, void>) {
Jan Kundrát3867c9e2020-06-18 20:26:45 +020063 what();
Jan Kundrát7ec214d2020-06-19 17:05:07 +020064 } else if constexpr (std::is_same<Exception, std::runtime_error>()) {
65 // cannot use REQUIRE_THROWS_AS(..., Exception) directly because that one
66 // needs an extra `typename` deep in the bowels of doctest
67 REQUIRE_THROWS_AS(what(), std::runtime_error);
Václav Kubernát74487df2020-06-04 01:29:28 +020068 } else if constexpr (std::is_same<Exception, std::logic_error>()) {
69 REQUIRE_THROWS_AS(what(), std::logic_error);
Jan Kundrát7ec214d2020-06-19 17:05:07 +020070 } else if constexpr (std::is_same<Exception, DatastoreException>()) {
71 REQUIRE_THROWS_AS(what(), DatastoreException);
72 } else if constexpr (std::is_same<Exception, sysrepo::sysrepo_exception>()) {
73 REQUIRE_THROWS_AS(what(), sysrepo::sysrepo_exception);
74 } else {
75 static_assert(always_false<Exception>); // https://stackoverflow.com/a/53945549/2245623
Jan Kundrát3867c9e2020-06-18 20:26:45 +020076 }
77}
Jan Kundrát7ec214d2020-06-19 17:05:07 +020078}
Jan Kundrát3867c9e2020-06-18 20:26:45 +020079
Václav Kubernát74487df2020-06-04 01:29:28 +020080#if defined(yang_BACKEND)
81class TestYangAccess : public YangAccess {
82public:
83 void commitChanges() override
84 {
85 YangAccess::commitChanges();
86 dumpToSysrepo();
87 }
88
89 void copyConfig(const Datastore source, const Datastore destination) override
90 {
91 YangAccess::copyConfig(source, destination);
92 dumpToSysrepo();
93 }
94
95private:
96 void dumpToSysrepo()
97 {
98 {
99 std::ofstream of(testConfigFile);
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200100 of << dump(DataFormat::Xml);
Václav Kubernát74487df2020-06-04 01:29:28 +0200101 }
102 auto command = std::string(sysrepocfgExecutable) + " --import=" + testConfigFile + " --format=xml --datastore=running example-schema";
103 REQUIRE(std::system(command.c_str()) == 0);
104 }
105};
106#endif
107
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200108TEST_CASE("setting/getting values")
Václav Kubernát73109382018-09-14 19:52:03 +0200109{
Václav Kubernát73109382018-09-14 19:52:03 +0200110 trompeloeil::sequence seq1;
111 MockRecorder mock;
Václav Kubernátab612e92019-11-26 19:51:31 +0100112 SysrepoSubscription subscription("example-schema", &mock);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100113
114#ifdef sysrepo_BACKEND
Václav Kubernát715c85c2020-04-14 01:46:08 +0200115 SysrepoAccess datastore("netconf-cli-test", Datastore::Running);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100116#elif defined(netconf_BACKEND)
117 NetconfAccess datastore(NETOPEER_SOCKET_PATH);
Václav Kubernát74487df2020-06-04 01:29:28 +0200118#elif defined(yang_BACKEND)
119 TestYangAccess datastore;
120 datastore.addSchemaDir(schemaDir);
121 datastore.addSchemaFile(exampleSchemaFile);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100122#else
123#error "Unknown backend"
124#endif
Václav Kubernát73109382018-09-14 19:52:03 +0200125
Václav Kubernát69aabe92020-01-24 16:53:12 +0100126
Václav Kubernát134d78f2019-09-03 16:42:29 +0200127 SECTION("set leafInt8 to -128")
Václav Kubernát73109382018-09-14 19:52:03 +0200128 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100129 REQUIRE_CALL(mock, write("/example-schema:leafInt8", std::nullopt, "-128"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200130 datastore.setLeaf("/example-schema:leafInt8", int8_t{-128});
131 datastore.commitChanges();
132 }
133
134 SECTION("set leafInt16 to -32768")
135 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100136 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "-32768"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200137 datastore.setLeaf("/example-schema:leafInt16", int16_t{-32768});
138 datastore.commitChanges();
139 }
140
141 SECTION("set leafInt32 to -2147483648")
142 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100143 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "-2147483648"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200144 datastore.setLeaf("/example-schema:leafInt32", int32_t{-2147483648});
145 datastore.commitChanges();
146 }
147
148 SECTION("set leafInt64 to -50000000000")
149 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100150 REQUIRE_CALL(mock, write("/example-schema:leafInt64", std::nullopt, "-50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200151 datastore.setLeaf("/example-schema:leafInt64", int64_t{-50000000000});
152 datastore.commitChanges();
153 }
154
155 SECTION("set leafUInt8 to 255")
156 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100157 REQUIRE_CALL(mock, write("/example-schema:leafUInt8", std::nullopt, "255"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200158 datastore.setLeaf("/example-schema:leafUInt8", uint8_t{255});
159 datastore.commitChanges();
160 }
161
162 SECTION("set leafUInt16 to 65535")
163 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100164 REQUIRE_CALL(mock, write("/example-schema:leafUInt16", std::nullopt, "65535"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200165 datastore.setLeaf("/example-schema:leafUInt16", uint16_t{65535});
166 datastore.commitChanges();
167 }
168
169 SECTION("set leafUInt32 to 4294967295")
170 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100171 REQUIRE_CALL(mock, write("/example-schema:leafUInt32", std::nullopt, "4294967295"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200172 datastore.setLeaf("/example-schema:leafUInt32", uint32_t{4294967295});
173 datastore.commitChanges();
174 }
175
176 SECTION("set leafUInt64 to 50000000000")
177 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100178 REQUIRE_CALL(mock, write("/example-schema:leafUInt64", std::nullopt, "50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200179 datastore.setLeaf("/example-schema:leafUInt64", uint64_t{50000000000});
Václav Kubernát73109382018-09-14 19:52:03 +0200180 datastore.commitChanges();
181 }
182
183 SECTION("set leafEnum to coze")
184 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100185 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "coze"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200186 datastore.setLeaf("/example-schema:leafEnum", enum_{"coze"});
187 datastore.commitChanges();
188 }
189
190 SECTION("set leafDecimal to 123.544")
191 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100192 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.544"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200193 datastore.setLeaf("/example-schema:leafDecimal", 123.544);
194 datastore.commitChanges();
195 }
196
Jan Kundrátd2872862020-06-18 21:02:00 +0200197 SECTION("set a string, then delete it")
198 {
199 REQUIRE_CALL(mock, write("/example-schema:leafString", std::nullopt, "blah"s));
200 datastore.setLeaf("/example-schema:leafString", "blah"s);
201 datastore.commitChanges();
202 DatastoreAccess::Tree expected{{"/example-schema:leafString", "blah"s}};
203 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
204
205 REQUIRE_CALL(mock, write("/example-schema:leafString", "blah"s, std::nullopt));
206 datastore.deleteItem("/example-schema:leafString");
207 datastore.commitChanges();
208 expected.clear();
209 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
210 }
211
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200212 SECTION("set a non-existing leaf")
213 {
214 catching<OnInvalidSchemaPathCreate>([&]{
215 datastore.setLeaf("/example-schema:non-existing", "what"s);
216 });
217 }
218
Václav Kubernát73109382018-09-14 19:52:03 +0200219 SECTION("create presence container")
220 {
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200221 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") == std::string::npos);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100222 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200223 datastore.createItem("/example-schema:pContainer");
Václav Kubernát73109382018-09-14 19:52:03 +0200224 datastore.commitChanges();
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200225 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") != std::string::npos);
Václav Kubernát73109382018-09-14 19:52:03 +0200226 }
227
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100228 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200229 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100230 {
231 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", std::nullopt, ""s));
232 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", std::nullopt, "Nguyen"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200233 datastore.createItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100234 datastore.commitChanges();
235 }
236 {
237 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", ""s, std::nullopt));
238 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", "Nguyen"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200239 datastore.deleteItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100240 datastore.commitChanges();
241 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200242 }
243
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200244 SECTION("deleting non-existing list keys")
245 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200246 catching<OnKeyNotFound>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200247 datastore.deleteItem("/example-schema:person[name='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200248 datastore.commitChanges();
249 });
250 }
251
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200252 SECTION("accessing non-existing schema nodes as a list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200253 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200254 catching<OnInvalidSchemaPathCreate>([&]{
255 datastore.createItem("/example-schema:non-existing-list[xxx='blah']");
256 datastore.commitChanges();
257 });
258 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200259 datastore.deleteItem("/example-schema:non-existing-list[xxx='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200260 datastore.commitChanges();
261 });
262 }
263
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200264 SECTION("leafref pointing to a key of a list")
265 {
266 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100267 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']", std::nullopt, ""s));
268 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s));
269 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']", std::nullopt, ""s));
270 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s));
271 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']", std::nullopt, ""s));
272 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']/name", std::nullopt, "Kolafa"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200273 datastore.createItem("/example-schema:person[name='Dan']");
274 datastore.createItem("/example-schema:person[name='Elfi']");
275 datastore.createItem("/example-schema:person[name='Kolafa']");
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200276 datastore.commitChanges();
277 }
278
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200279 std::string value;
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200280 SECTION("Dan")
281 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200282 value = "Dan";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200283 }
284
285 SECTION("Elfi")
286 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200287 value = "Elfi";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200288 }
289
290 SECTION("Kolafa")
291 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200292 value = "Kolafa";
293 }
294
295 datastore.setLeaf("/example-schema:bossPerson", value);
296 {
297 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, value));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200298 datastore.commitChanges();
299 }
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200300 REQUIRE(datastore.getItems("/example-schema:bossPerson") == DatastoreAccess::Tree{{"/example-schema:bossPerson", value}});
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200301 }
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200302 SECTION("bool values get correctly represented as bools")
303 {
304 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100305 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "true"s));
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200306 datastore.setLeaf("/example-schema:down", bool{true});
307 datastore.commitChanges();
308 }
309
Jan Kundrátb331b552020-01-23 15:25:29 +0100310 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200311 REQUIRE(datastore.getItems("/example-schema:down") == expected);
312 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200313
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200314 SECTION("getting items from the whole module")
315 {
316 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100317 REQUIRE_CALL(mock, write("/example-schema:up", std::nullopt, "true"s));
318 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "false"s));
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200319 datastore.setLeaf("/example-schema:up", bool{true});
320 datastore.setLeaf("/example-schema:down", bool{false});
321 datastore.commitChanges();
322 }
323
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200324 DatastoreAccess::Tree expected{
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200325 // Sysrepo always returns containers when getting values, but
326 // libnetconf does not. This is fine by the YANG standard:
327 // https://tools.ietf.org/html/rfc7950#section-7.5.7 Furthermore,
328 // NetconfAccess implementation actually only iterates over leafs,
329 // so even if libnetconf did include containers, they wouldn't get
330 // shown here anyway. With sysrepo2, this won't be necessary,
331 // because it'll use the same data structure as libnetconf, so the
332 // results will be consistent.
333#ifdef sysrepo_BACKEND
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100334 {"/example-schema:inventory", special_{SpecialValue::Container}},
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200335 {"/example-schema:lol", special_{SpecialValue::Container}},
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200336#endif
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200337 {"/example-schema:up", bool{true}},
338 {"/example-schema:down", bool{false}}};
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200339 REQUIRE(datastore.getItems("/example-schema:*") == expected);
340 }
341
Václav Kubernát152ce222019-12-19 12:23:32 +0100342 SECTION("getItems returns correct datatypes")
343 {
344 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100345 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "lol"s));
Václav Kubernát152ce222019-12-19 12:23:32 +0100346 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
347 datastore.commitChanges();
348 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100349 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100350
351 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
352 }
353
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100354 SECTION("getItems on a list")
355 {
356 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100357 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']", std::nullopt, ""s));
358 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s));
359 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']", std::nullopt, ""s));
360 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s));
361 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']", std::nullopt, ""s));
362 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']/name", std::nullopt, "Petr"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200363 datastore.createItem("/example-schema:person[name='Jan']");
364 datastore.createItem("/example-schema:person[name='Michal']");
365 datastore.createItem("/example-schema:person[name='Petr']");
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100366 datastore.commitChanges();
367 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100368 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100369 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100370 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100371 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100372 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100373 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100374 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
375 };
376
377 REQUIRE(datastore.getItems("/example-schema:person") == expected);
378 }
379
Václav Kubernát69aabe92020-01-24 16:53:12 +0100380 SECTION("presence containers")
381 {
382 DatastoreAccess::Tree expected;
383 // Make sure it's not there before we create it
384 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
385
386 {
387 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200388 datastore.createItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100389 datastore.commitChanges();
390 }
391 expected = {
392 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
393 };
394 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
395
396 // Make sure it's not there after we delete it
397 {
398 REQUIRE_CALL(mock, write("/example-schema:pContainer", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200399 datastore.deleteItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100400 datastore.commitChanges();
401 }
402 expected = {};
403 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100404 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100405
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200406 SECTION("creating a non-existing schema node as a container")
407 {
408 catching<OnInvalidSchemaPathCreate>([&]{
409 datastore.createItem("/example-schema:non-existing-presence-container");
410 datastore.commitChanges();
411 });
412 }
413
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200414 SECTION("deleting a non-existing schema node as a container or leaf")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200415 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200416 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200417 datastore.deleteItem("/example-schema:non-existing-presence-container");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200418 datastore.commitChanges();
419 });
420 }
421
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100422 SECTION("nested presence container")
423 {
424 DatastoreAccess::Tree expected;
425 // Make sure it's not there before we create it
426 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
427 {
428 REQUIRE_CALL(mock, write("/example-schema:inventory", std::nullopt, ""s));
429 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200430 datastore.createItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100431 datastore.commitChanges();
432 }
433 expected = {
434 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
435 };
436 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
437 {
438 REQUIRE_CALL(mock, write("/example-schema:inventory", ""s, std::nullopt));
439 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200440 datastore.deleteItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100441 datastore.commitChanges();
442 }
443 expected = {};
444 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100445 }
446
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100447 SECTION("floats")
448 {
449 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
450 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.4"s));
451 datastore.commitChanges();
452 DatastoreAccess::Tree expected {
453 {"/example-schema:leafDecimal", 123.4},
454 };
455 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
456 }
457
Jan Kundrát3ff50122020-05-07 00:37:50 +0200458 SECTION("unions")
459 {
460 datastore.setLeaf("/example-schema:unionIntString", int32_t{10});
461 REQUIRE_CALL(mock, write("/example-schema:unionIntString", std::nullopt, "10"s));
462 datastore.commitChanges();
463 DatastoreAccess::Tree expected {
464 {"/example-schema:unionIntString", int32_t{10}},
465 };
466 REQUIRE(datastore.getItems("/example-schema:unionIntString") == expected);
467 }
468
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200469 SECTION("identityref") {
470 datastore.setLeaf("/example-schema:beast", identityRef_{"example-schema", "Mammal"});
471 REQUIRE_CALL(mock, write("/example-schema:beast", std::nullopt, "example-schema:Mammal"s));
472 datastore.commitChanges();
473 DatastoreAccess::Tree expected {
474 {"/example-schema:beast", identityRef_{"example-schema", "Mammal"}},
475 };
476 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
477
478 datastore.setLeaf("/example-schema:beast", identityRef_{"Whale"});
479 REQUIRE_CALL(mock, write("/example-schema:beast", "example-schema:Mammal", "example-schema:Whale"s));
480 datastore.commitChanges();
481 expected = {
482 {"/example-schema:beast", identityRef_{"example-schema", "Whale"}},
483 };
484 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
485 }
486
Jan Kundrát68985442020-05-07 02:15:34 +0200487 SECTION("binary")
488 {
489 datastore.setLeaf("/example-schema:blob", binary_{"cHduegByIQ=="s});
490 REQUIRE_CALL(mock, write("/example-schema:blob", std::nullopt, "cHduegByIQ=="s));
491 datastore.commitChanges();
492 DatastoreAccess::Tree expected {
493 {"/example-schema:blob", binary_{"cHduegByIQ=="s}},
494 };
495 REQUIRE(datastore.getItems("/example-schema:blob") == expected);
496 }
497
Jan Kundrát379bb572020-05-07 03:23:13 +0200498 SECTION("empty")
499 {
500 datastore.setLeaf("/example-schema:dummy", empty_{});
501 REQUIRE_CALL(mock, write("/example-schema:dummy", std::nullopt, ""s));
502 datastore.commitChanges();
503 DatastoreAccess::Tree expected {
504 {"/example-schema:dummy", empty_{}},
505 };
506 REQUIRE(datastore.getItems("/example-schema:dummy") == expected);
507 }
508
Václav Kubernát74487df2020-06-04 01:29:28 +0200509#if not defined(yang_BACKEND)
Jan Kundrátbb525b42020-02-04 11:56:59 +0100510 SECTION("operational data")
511 {
512 MockDataSupplier mockOpsData;
513 OperationalDataSubscription opsDataSub("/example-schema:temperature", mockOpsData);
514 DatastoreAccess::Tree expected;
515 std::string xpath;
516 SECTION("temperature")
517 {
518 expected = {{"/example-schema:temperature", int32_t{22}}};
519 xpath = "/example-schema:temperature";
520 }
521
522 REQUIRE_CALL(mockOpsData, get_data(xpath)).RETURN(expected);
523 REQUIRE(datastore.getItems(xpath) == expected);
524 }
Václav Kubernát74487df2020-06-04 01:29:28 +0200525#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +0100526
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200527 SECTION("leaf list")
528 {
529 DatastoreAccess::Tree expected;
530 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "0.0.0.0"s));
531 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "127.0.0.1"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200532 datastore.createItem("/example-schema:addresses[.='0.0.0.0']");
533 datastore.createItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200534 datastore.commitChanges();
535 expected = {
536 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
537 {"/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s},
538 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
539 };
540 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
541
542 REQUIRE_CALL(mock, write("/example-schema:addresses", "0.0.0.0"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200543 datastore.deleteItem("/example-schema:addresses[.='0.0.0.0']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200544 datastore.commitChanges();
545 expected = {
546 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
547 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
548 };
549 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
550
551 REQUIRE_CALL(mock, write("/example-schema:addresses", "127.0.0.1"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200552 datastore.deleteItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200553 datastore.commitChanges();
554 expected = {};
555 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
556 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100557
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200558 SECTION("deleting a non-existing leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200559 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200560 catching<OnKeyNotFound>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200561 datastore.deleteItem("/example-schema:addresses[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200562 datastore.commitChanges();
563 });
564 }
565
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200566 SECTION("accessing a non-existing schema node as a leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200567 {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200568 catching<OnInvalidSchemaPathCreate>([&]{
569 datastore.createItem("/example-schema:non-existing[.='non-existing']");
570 datastore.commitChanges();
571 });
572
573 catching<OnInvalidSchemaPathDelete>([&]{
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200574 datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200575 datastore.commitChanges();
576 });
577 }
578
Václav Kubernát7160a132020-04-03 02:11:01 +0200579 SECTION("copying data from startup refreshes the data")
580 {
581 {
582 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
583 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "123"s));
584 datastore.setLeaf("/example-schema:leafInt16", int16_t{123});
585 datastore.commitChanges();
586 }
587 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{{"/example-schema:leafInt16", int16_t{123}}});
588 REQUIRE_CALL(mock, write("/example-schema:leafInt16", "123"s, std::nullopt));
589 datastore.copyConfig(Datastore::Startup, Datastore::Running);
590 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
591 }
592
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200593 SECTION("moving leaflist instances")
594 {
595 DatastoreAccess::Tree expected;
596 {
597 // sysrepo does this twice for some reason, it's possibly a bug
598 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "http"s)).TIMES(2);
599 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "ftp"s));
600 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
601 REQUIRE_CALL(mock, write("/example-schema:protocols", "http"s, "ftp"s));
602 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "pop3"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200603 datastore.createItem("/example-schema:protocols[.='http']");
604 datastore.createItem("/example-schema:protocols[.='ftp']");
605 datastore.createItem("/example-schema:protocols[.='pop3']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200606 datastore.commitChanges();
607 expected = {
608 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
609 {"/example-schema:protocols[.='http']", "http"s},
610 {"/example-schema:protocols[.='ftp']", "ftp"s},
611 {"/example-schema:protocols[.='pop3']", "pop3"s},
612 };
613 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
614 }
615
616 std::string sourcePath;
617 SECTION("begin")
618 {
619 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
620 sourcePath = "/example-schema:protocols[.='pop3']";
621 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
622 datastore.commitChanges();
623 expected = {
624 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
625 {"/example-schema:protocols[.='pop3']", "pop3"s},
626 {"/example-schema:protocols[.='http']", "http"s},
627 {"/example-schema:protocols[.='ftp']", "ftp"s},
628 };
629 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
630 }
631
632 SECTION("end")
633 {
634 sourcePath = "/example-schema:protocols[.='http']";
635 REQUIRE_CALL(mock, write("/example-schema:protocols", "pop3"s, "http"s));
636 datastore.moveItem(sourcePath, yang::move::Absolute::End);
637 datastore.commitChanges();
638 expected = {
639 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
640 {"/example-schema:protocols[.='ftp']", "ftp"s},
641 {"/example-schema:protocols[.='pop3']", "pop3"s},
642 {"/example-schema:protocols[.='http']", "http"s},
643 };
644 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
645 }
646
647 SECTION("after")
648 {
649 sourcePath = "/example-schema:protocols[.='http']";
650 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
651 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{".", "ftp"s}}});
652 datastore.commitChanges();
653 expected = {
654 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
655 {"/example-schema:protocols[.='ftp']", "ftp"s},
656 {"/example-schema:protocols[.='http']", "http"s},
657 {"/example-schema:protocols[.='pop3']", "pop3"s},
658 };
659 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
660 }
661
662 SECTION("before")
663 {
664 sourcePath = "/example-schema:protocols[.='http']";
665 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
666 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{".", "pop3"s}}});
667 datastore.commitChanges();
668 expected = {
669 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
670 {"/example-schema:protocols[.='ftp']", "ftp"s},
671 {"/example-schema:protocols[.='http']", "http"s},
672 {"/example-schema:protocols[.='pop3']", "pop3"s},
673 };
674 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
675 }
676 }
677
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200678 SECTION("moving non-existing schema nodes")
679 {
680 catching<OnInvalidSchemaPathMove>([&]{
681 datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
682 datastore.commitChanges();
683 });
684 }
685
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200686 SECTION("moving list instances")
687 {
688 DatastoreAccess::Tree expected;
689 {
690 // sysrepo does this twice for some reason, it's possibly a bug
691 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", std::nullopt, ""s)).TIMES(2);
692 REQUIRE_CALL(mock, write("/example-schema:players[name='John']/name", std::nullopt, "John"s));
693 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']", std::nullopt, ""s));
694 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']", ""s, ""s));
695 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']/name", std::nullopt, "Eve"s));
696 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", std::nullopt, ""s));
697 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']/name", std::nullopt, "Adam"s));
698 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", ""s, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200699 datastore.createItem("/example-schema:players[name='John']");
700 datastore.createItem("/example-schema:players[name='Eve']");
701 datastore.createItem("/example-schema:players[name='Adam']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200702 datastore.commitChanges();
703 expected = {
704 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
705 {"/example-schema:players[name='John']/name", "John"s},
706 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
707 {"/example-schema:players[name='Eve']/name", "Eve"s},
708 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
709 {"/example-schema:players[name='Adam']/name", "Adam"s},
710 };
711 REQUIRE(datastore.getItems("/example-schema:players") == expected);
712 }
713
714 std::string sourcePath;
715 SECTION("begin")
716 {
717 sourcePath = "/example-schema:players[name='Adam']";
718 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", std::nullopt, ""s));
719 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
720 datastore.commitChanges();
721 expected = {
722 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
723 {"/example-schema:players[name='Adam']/name", "Adam"s},
724 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
725 {"/example-schema:players[name='John']/name", "John"s},
726 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
727 {"/example-schema:players[name='Eve']/name", "Eve"s},
728 };
729 REQUIRE(datastore.getItems("/example-schema:players") == expected);
730 }
731
732 SECTION("end")
733 {
734 sourcePath = "/example-schema:players[name='John']";
735 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
736 datastore.moveItem(sourcePath, yang::move::Absolute::End);
737 datastore.commitChanges();
738 expected = {
739 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
740 {"/example-schema:players[name='Eve']/name", "Eve"s},
741 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
742 {"/example-schema:players[name='Adam']/name", "Adam"s},
743 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
744 {"/example-schema:players[name='John']/name", "John"s},
745 };
746 REQUIRE(datastore.getItems("/example-schema:players") == expected);
747 }
748
749 SECTION("after")
750 {
751 sourcePath = "/example-schema:players[name='John']";
752 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
753 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{"name", "Eve"s}}});
754 datastore.commitChanges();
755 expected = {
756 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
757 {"/example-schema:players[name='Eve']/name", "Eve"s},
758 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
759 {"/example-schema:players[name='John']/name", "John"s},
760 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
761 {"/example-schema:players[name='Adam']/name", "Adam"s},
762 };
763 REQUIRE(datastore.getItems("/example-schema:players") == expected);
764 }
765
766 SECTION("before")
767 {
768 sourcePath = "/example-schema:players[name='John']";
769 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
770 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{"name", "Adam"s}}});
771 datastore.commitChanges();
772 expected = {
773 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
774 {"/example-schema:players[name='Eve']/name", "Eve"s},
775 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
776 {"/example-schema:players[name='John']/name", "John"s},
777 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
778 {"/example-schema:players[name='Adam']/name", "Adam"s},
779 };
780 REQUIRE(datastore.getItems("/example-schema:players") == expected);
781 }
782 }
783
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200784 SECTION("getting /")
785 {
786 {
787 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "64"s));
788 datastore.setLeaf("/example-schema:leafInt32", 64);
789 datastore.commitChanges();
790 }
791
792 DatastoreAccess::Tree expected{
793 // Sysrepo always returns containers when getting values, but
794 // libnetconf does not. This is fine by the YANG standard:
795 // https://tools.ietf.org/html/rfc7950#section-7.5.7 Furthermore,
796 // NetconfAccess implementation actually only iterates over leafs,
797 // so even if libnetconf did include containers, they wouldn't get
798 // shown here anyway. With sysrepo2, this won't be necessary,
799 // because it'll use the same data structure as libnetconf, so the
800 // results will be consistent.
801#ifdef sysrepo_BACKEND
802 {"/example-schema:inventory", special_{SpecialValue::Container}},
803 {"/example-schema:lol", special_{SpecialValue::Container}},
804#endif
805 {"/example-schema:leafInt32", 64}};
806 auto items = datastore.getItems("/");
807 // This tests if we at least get the data WE added.
808 REQUIRE(std::all_of(expected.begin(), expected.end(), [items] (const auto& item) { return std::find(items.begin(), items.end(), item) != items.end(); }));
809 }
810
Václav Kubernát36986c52020-06-25 10:30:05 +0200811 SECTION("setting and removing without commit")
812 {
813 datastore.setLeaf("/example-schema:leafInt32", 64);
814 datastore.deleteItem("/example-schema:leafInt32");
815 }
816
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200817 SECTION("two key lists")
818 {
819 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']", std::nullopt, ""s));
820 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']/x", std::nullopt, "12"s));
821 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']/y", std::nullopt, "10"s));
822 datastore.createItem("/example-schema:point[x='12'][y='10']");
823 datastore.commitChanges();
824 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:point") != std::string::npos);
825 }
826
Václav Kubernát73109382018-09-14 19:52:03 +0200827 waitForCompletionAndBitMore(seq1);
828}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100829
830class RpcCb: public sysrepo::Callback {
831 int rpc(const char *xpath, const ::sysrepo::S_Vals input, ::sysrepo::S_Vals_Holder output, void *) override
832 {
833 const auto nukes = "/example-schema:launch-nukes"s;
834 if (xpath == "/example-schema:noop"s) {
835 return SR_ERR_OK;
836 } else if (xpath == nukes) {
837 uint64_t kilotons = 0;
838 bool hasCities = false;
839 for (size_t i = 0; i < input->val_cnt(); ++i) {
840 const auto& val = input->val(i);
841 if (val->xpath() == nukes + "/payload/kilotons") {
842 kilotons = val->data()->get_uint64();
843 } else if (val->xpath() == nukes + "/payload") {
844 // ignore, container
845 } else if (val->xpath() == nukes + "/description") {
846 // unused
847 } else if (std::string_view{val->xpath()}.find(nukes + "/cities") == 0) {
848 hasCities = true;
849 } else {
850 throw std::runtime_error("RPC launch-nukes: unexpected input "s + val->xpath());
851 }
852 }
853 if (kilotons == 333'666) {
854 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
855 return SR_ERR_OK;
856 }
857 auto buf = output->allocate(2);
858 size_t i = 0;
859 buf->val(i++)->set((nukes + "/blast-radius").c_str(), uint32_t{33'666});
860 buf->val(i++)->set((nukes + "/actual-yield").c_str(), static_cast<uint64_t>(1.33 * kilotons));
861 if (hasCities) {
862 buf = output->reallocate(output->val_cnt() + 2);
863 buf->val(i++)->set((nukes + "/damaged-places/targets[city='London']/city").c_str(), "London");
864 buf->val(i++)->set((nukes + "/damaged-places/targets[city='Berlin']/city").c_str(), "Berlin");
865 }
866 return SR_ERR_OK;
867 }
868 throw std::runtime_error("unrecognized RPC");
869 }
870};
871
872TEST_CASE("rpc") {
873 trompeloeil::sequence seq1;
874 auto srConn = std::make_shared<sysrepo::Connection>("netconf-cli-test-rpc");
875 auto srSession = std::make_shared<sysrepo::Session>(srConn);
876 auto srSubscription = std::make_shared<sysrepo::Subscribe>(srSession);
877 auto cb = std::make_shared<RpcCb>();
878 sysrepo::Logs{}.set_stderr(SR_LL_INF);
879 srSubscription->rpc_subscribe("/example-schema:noop", cb, nullptr, SR_SUBSCR_CTX_REUSE);
880 srSubscription->rpc_subscribe("/example-schema:launch-nukes", cb, nullptr, SR_SUBSCR_CTX_REUSE);
881
882#ifdef sysrepo_BACKEND
Václav Kubernát715c85c2020-04-14 01:46:08 +0200883 SysrepoAccess datastore("netconf-cli-test", Datastore::Running);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100884#elif defined(netconf_BACKEND)
885 NetconfAccess datastore(NETOPEER_SOCKET_PATH);
Václav Kubernát74487df2020-06-04 01:29:28 +0200886#elif defined(yang_BACKEND)
887 YangAccess datastore;
888 datastore.addSchemaDir(schemaDir);
889 datastore.addSchemaFile(exampleSchemaFile);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100890#else
891#error "Unknown backend"
892#endif
893
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200894 SECTION("valid")
895 {
896 std::string rpc;
897 DatastoreAccess::Tree input, output;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100898
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200899 SECTION("noop") {
900 rpc = "/example-schema:noop";
901 }
902
903 SECTION("small nuke") {
904 rpc = "/example-schema:launch-nukes";
905 input = {
906 {"description", "dummy"s},
907 {"payload/kilotons", uint64_t{333'666}},
908 };
909 // no data are returned
910 }
911
912 SECTION("small nuke") {
913 rpc = "/example-schema:launch-nukes";
914 input = {
915 {"description", "dummy"s},
916 {"payload/kilotons", uint64_t{4}},
917 };
918 output = {
919 {"blast-radius", uint32_t{33'666}},
920 {"actual-yield", uint64_t{5}},
921 };
922 }
923
924 SECTION("with lists") {
925 rpc = "/example-schema:launch-nukes";
926 input = {
927 {"payload/kilotons", uint64_t{6}},
928 {"cities/targets[city='Prague']/city", "Prague"s},
929 };
930 output = {
931 {"blast-radius", uint32_t{33'666}},
932 {"actual-yield", uint64_t{7}},
933 {"damaged-places", special_{SpecialValue::PresenceContainer}},
934 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
935 {"damaged-places/targets[city='London']/city", "London"s},
936 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
937 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
938 };
939 }
940
Václav Kubernát74487df2020-06-04 01:29:28 +0200941 catching<OnRPC>([&] {REQUIRE(datastore.executeRpc(rpc, input) == output);});
Jan Kundrát6ee84792020-01-24 01:43:36 +0100942 }
943
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200944 SECTION("non-existing RPC")
945 {
Václav Kubernát74487df2020-06-04 01:29:28 +0200946 catching<OnInvalidRpcPath>([&] {datastore.executeRpc("/example-schema:non-existing", DatastoreAccess::Tree{});});
Jan Kundrát6ee84792020-01-24 01:43:36 +0100947 }
948
Jan Kundrát6ee84792020-01-24 01:43:36 +0100949 waitForCompletionAndBitMore(seq1);
950}