blob: ececd0c438092038701d2b199b6c24ccb9fd76f7 [file] [log] [blame]
Václav Kubernát73109382018-09-14 19:52:03 +02001/*
2 * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
3 * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
4 *
5 * Written by Václav Kubernát <kubervac@fit.cvut.cz>
6 *
7*/
8
Václav Kubernát26b56082020-02-03 18:28:56 +01009#include "trompeloeil_doctest.hpp"
Jan Kundrát6ee84792020-01-24 01:43:36 +010010#include <sysrepo-cpp/Session.hpp>
Václav Kubernáte7248b22020-06-26 15:38:59 +020011#include "proxy_datastore.hpp"
Václav Kubernátb4e5b182020-11-16 19:55:09 +010012#include "yang_schema.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020013
Václav Kubernátc31bd602019-03-07 11:44:48 +010014#ifdef sysrepo_BACKEND
Václav Kubernát73109382018-09-14 19:52:03 +020015#include "sysrepo_access.hpp"
Jan Kundrát7ec214d2020-06-19 17:05:07 +020016using OnInvalidSchemaPathCreate = DatastoreException;
Václav Kubernát654303f2020-07-31 13:16:54 +020017using OnInvalidSchemaPathDelete = DatastoreException;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020018using OnInvalidSchemaPathMove = sysrepo::sysrepo_exception;
Václav Kubernát74487df2020-06-04 01:29:28 +020019using OnInvalidRpcPath = sysrepo::sysrepo_exception;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020020using OnKeyNotFound = void;
Václav Kubernáta8789602020-07-20 15:18:19 +020021using OnExec = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010022#elif defined(netconf_BACKEND)
Jan Kundrát7ec214d2020-06-19 17:05:07 +020023using OnInvalidSchemaPathCreate = std::runtime_error;
24using OnInvalidSchemaPathDelete = std::runtime_error;
25using OnInvalidSchemaPathMove = std::runtime_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020026using OnInvalidRpcPath = std::runtime_error;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020027using OnKeyNotFound = std::runtime_error;
Václav Kubernáta8789602020-07-20 15:18:19 +020028using OnExec = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010029#include "netconf_access.hpp"
Václav Kubernát74487df2020-06-04 01:29:28 +020030#elif defined(yang_BACKEND)
31#include <fstream>
32#include "yang_access.hpp"
33#include "yang_access_test_vars.hpp"
34using OnInvalidSchemaPathCreate = DatastoreException;
35using OnInvalidSchemaPathDelete = DatastoreException;
36using OnInvalidSchemaPathMove = DatastoreException;
37using OnInvalidRpcPath = DatastoreException;
38using OnKeyNotFound = DatastoreException;
Václav Kubernáta8789602020-07-20 15:18:19 +020039using OnExec = std::logic_error;
Václav Kubernátc31bd602019-03-07 11:44:48 +010040#else
41#error "Unknown backend"
42#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +010043#include "pretty_printers.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020044#include "sysrepo_subscription.hpp"
Václav Kubernát8e121ff2019-10-15 15:47:45 +020045#include "utils.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020046
Jan Kundrát6ee84792020-01-24 01:43:36 +010047using namespace std::literals::string_literals;
48
Václav Kubernát69aabe92020-01-24 16:53:12 +010049class MockRecorder : public trompeloeil::mock_interface<Recorder> {
Václav Kubernát73109382018-09-14 19:52:03 +020050public:
Václav Kubernát69aabe92020-01-24 16:53:12 +010051 IMPLEMENT_MOCK3(write);
Václav Kubernát73109382018-09-14 19:52:03 +020052};
53
Jan Kundrátbb525b42020-02-04 11:56:59 +010054class MockDataSupplier : public trompeloeil::mock_interface<DataSupplier> {
55public:
56 IMPLEMENT_CONST_MOCK1(get_data);
57};
58
Jan Kundrát7ec214d2020-06-19 17:05:07 +020059namespace {
60template <class ...> constexpr std::false_type always_false [[maybe_unused]] {};
61template <class Exception, typename Callable> void catching(const Callable& what) {
62
63 if constexpr (std::is_same_v<Exception, void>) {
Jan Kundrát3867c9e2020-06-18 20:26:45 +020064 what();
Jan Kundrát7ec214d2020-06-19 17:05:07 +020065 } else if constexpr (std::is_same<Exception, std::runtime_error>()) {
66 // cannot use REQUIRE_THROWS_AS(..., Exception) directly because that one
67 // needs an extra `typename` deep in the bowels of doctest
68 REQUIRE_THROWS_AS(what(), std::runtime_error);
Václav Kubernát74487df2020-06-04 01:29:28 +020069 } else if constexpr (std::is_same<Exception, std::logic_error>()) {
70 REQUIRE_THROWS_AS(what(), std::logic_error);
Jan Kundrát7ec214d2020-06-19 17:05:07 +020071 } else if constexpr (std::is_same<Exception, DatastoreException>()) {
72 REQUIRE_THROWS_AS(what(), DatastoreException);
73 } else if constexpr (std::is_same<Exception, sysrepo::sysrepo_exception>()) {
74 REQUIRE_THROWS_AS(what(), sysrepo::sysrepo_exception);
75 } else {
76 static_assert(always_false<Exception>); // https://stackoverflow.com/a/53945549/2245623
Jan Kundrát3867c9e2020-06-18 20:26:45 +020077 }
78}
Jan Kundrát7ec214d2020-06-19 17:05:07 +020079}
Jan Kundrát3867c9e2020-06-18 20:26:45 +020080
Václav Kubernát74487df2020-06-04 01:29:28 +020081#if defined(yang_BACKEND)
82class TestYangAccess : public YangAccess {
83public:
84 void commitChanges() override
85 {
86 YangAccess::commitChanges();
87 dumpToSysrepo();
88 }
89
90 void copyConfig(const Datastore source, const Datastore destination) override
91 {
92 YangAccess::copyConfig(source, destination);
93 dumpToSysrepo();
94 }
95
96private:
97 void dumpToSysrepo()
98 {
99 {
100 std::ofstream of(testConfigFile);
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200101 of << dump(DataFormat::Xml);
Václav Kubernát74487df2020-06-04 01:29:28 +0200102 }
Jan Kundrát86840ec2021-01-27 15:38:08 +0100103 auto command = std::string(sysrepocfgExecutable) + " --import=" + testConfigFile + " --format=xml --datastore=running --module=example-schema";
Václav Kubernát74487df2020-06-04 01:29:28 +0200104 REQUIRE(std::system(command.c_str()) == 0);
105 }
106};
107#endif
108
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200109TEST_CASE("setting/getting values")
Václav Kubernát73109382018-09-14 19:52:03 +0200110{
Václav Kubernát654303f2020-07-31 13:16:54 +0200111 sr_log_stderr(SR_LL_DBG);
Václav Kubernát73109382018-09-14 19:52:03 +0200112 trompeloeil::sequence seq1;
113 MockRecorder mock;
Václav Kubernát654303f2020-07-31 13:16:54 +0200114 {
115 auto conn = std::make_shared<sysrepo::Connection>();
116 auto sess = std::make_shared<sysrepo::Session>(conn);
117 sess->copy_config(SR_DS_STARTUP, "example-schema", 1000, true);
118 }
Václav Kubernátab612e92019-11-26 19:51:31 +0100119 SysrepoSubscription subscription("example-schema", &mock);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100120
121#ifdef sysrepo_BACKEND
Václav Kubernát654303f2020-07-31 13:16:54 +0200122 SysrepoAccess datastore(Datastore::Running);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100123#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200124 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
125 NetconfAccess datastore(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200126#elif defined(yang_BACKEND)
127 TestYangAccess datastore;
128 datastore.addSchemaDir(schemaDir);
129 datastore.addSchemaFile(exampleSchemaFile);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100130#else
131#error "Unknown backend"
132#endif
Václav Kubernát73109382018-09-14 19:52:03 +0200133
Václav Kubernát69aabe92020-01-24 16:53:12 +0100134
Václav Kubernát134d78f2019-09-03 16:42:29 +0200135 SECTION("set leafInt8 to -128")
Václav Kubernát73109382018-09-14 19:52:03 +0200136 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100137 REQUIRE_CALL(mock, write("/example-schema:leafInt8", std::nullopt, "-128"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200138 datastore.setLeaf("/example-schema:leafInt8", int8_t{-128});
139 datastore.commitChanges();
140 }
141
142 SECTION("set leafInt16 to -32768")
143 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100144 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "-32768"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200145 datastore.setLeaf("/example-schema:leafInt16", int16_t{-32768});
146 datastore.commitChanges();
147 }
148
149 SECTION("set leafInt32 to -2147483648")
150 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100151 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "-2147483648"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200152 datastore.setLeaf("/example-schema:leafInt32", int32_t{-2147483648});
153 datastore.commitChanges();
154 }
155
156 SECTION("set leafInt64 to -50000000000")
157 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100158 REQUIRE_CALL(mock, write("/example-schema:leafInt64", std::nullopt, "-50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200159 datastore.setLeaf("/example-schema:leafInt64", int64_t{-50000000000});
160 datastore.commitChanges();
161 }
162
163 SECTION("set leafUInt8 to 255")
164 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100165 REQUIRE_CALL(mock, write("/example-schema:leafUInt8", std::nullopt, "255"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200166 datastore.setLeaf("/example-schema:leafUInt8", uint8_t{255});
167 datastore.commitChanges();
168 }
169
170 SECTION("set leafUInt16 to 65535")
171 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100172 REQUIRE_CALL(mock, write("/example-schema:leafUInt16", std::nullopt, "65535"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200173 datastore.setLeaf("/example-schema:leafUInt16", uint16_t{65535});
174 datastore.commitChanges();
175 }
176
177 SECTION("set leafUInt32 to 4294967295")
178 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100179 REQUIRE_CALL(mock, write("/example-schema:leafUInt32", std::nullopt, "4294967295"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200180 datastore.setLeaf("/example-schema:leafUInt32", uint32_t{4294967295});
181 datastore.commitChanges();
182 }
183
184 SECTION("set leafUInt64 to 50000000000")
185 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100186 REQUIRE_CALL(mock, write("/example-schema:leafUInt64", std::nullopt, "50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200187 datastore.setLeaf("/example-schema:leafUInt64", uint64_t{50000000000});
Václav Kubernát73109382018-09-14 19:52:03 +0200188 datastore.commitChanges();
189 }
190
191 SECTION("set leafEnum to coze")
192 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100193 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "coze"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200194 datastore.setLeaf("/example-schema:leafEnum", enum_{"coze"});
195 datastore.commitChanges();
196 }
197
198 SECTION("set leafDecimal to 123.544")
199 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100200 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.544"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200201 datastore.setLeaf("/example-schema:leafDecimal", 123.544);
202 datastore.commitChanges();
203 }
204
Jan Kundrátd2872862020-06-18 21:02:00 +0200205 SECTION("set a string, then delete it")
206 {
207 REQUIRE_CALL(mock, write("/example-schema:leafString", std::nullopt, "blah"s));
208 datastore.setLeaf("/example-schema:leafString", "blah"s);
209 datastore.commitChanges();
210 DatastoreAccess::Tree expected{{"/example-schema:leafString", "blah"s}};
211 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
212
213 REQUIRE_CALL(mock, write("/example-schema:leafString", "blah"s, std::nullopt));
214 datastore.deleteItem("/example-schema:leafString");
215 datastore.commitChanges();
216 expected.clear();
217 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
218 }
219
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200220 SECTION("set a non-existing leaf")
221 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100222 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200223 datastore.setLeaf("/example-schema:non-existing", "what"s);
224 });
225 }
226
Václav Kubernát73109382018-09-14 19:52:03 +0200227 SECTION("create presence container")
228 {
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200229 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") == std::string::npos);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100230 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200231 datastore.createItem("/example-schema:pContainer");
Václav Kubernát73109382018-09-14 19:52:03 +0200232 datastore.commitChanges();
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200233 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") != std::string::npos);
Václav Kubernát73109382018-09-14 19:52:03 +0200234 }
235
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100236 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200237 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100238 {
239 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", std::nullopt, ""s));
240 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", std::nullopt, "Nguyen"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200241 datastore.createItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100242 datastore.commitChanges();
243 }
244 {
245 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", ""s, std::nullopt));
246 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", "Nguyen"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200247 datastore.deleteItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100248 datastore.commitChanges();
249 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200250 }
251
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200252 SECTION("deleting non-existing list keys")
253 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100254 catching<OnKeyNotFound>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200255 datastore.deleteItem("/example-schema:person[name='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200256 datastore.commitChanges();
257 });
258 }
259
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200260 SECTION("accessing non-existing schema nodes as a list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200261 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100262 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200263 datastore.createItem("/example-schema:non-existing-list[xxx='blah']");
264 datastore.commitChanges();
265 });
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100266 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200267 datastore.deleteItem("/example-schema:non-existing-list[xxx='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200268 datastore.commitChanges();
269 });
270 }
271
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200272 SECTION("leafref pointing to a key of a list")
273 {
274 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100275 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']", std::nullopt, ""s));
276 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s));
277 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']", std::nullopt, ""s));
278 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s));
279 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']", std::nullopt, ""s));
280 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']/name", std::nullopt, "Kolafa"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200281 datastore.createItem("/example-schema:person[name='Dan']");
282 datastore.createItem("/example-schema:person[name='Elfi']");
283 datastore.createItem("/example-schema:person[name='Kolafa']");
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200284 datastore.commitChanges();
285 }
286
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200287 std::string value;
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200288 SECTION("Dan")
289 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200290 value = "Dan";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200291 }
292
293 SECTION("Elfi")
294 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200295 value = "Elfi";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200296 }
297
298 SECTION("Kolafa")
299 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200300 value = "Kolafa";
301 }
302
303 datastore.setLeaf("/example-schema:bossPerson", value);
304 {
305 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, value));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200306 datastore.commitChanges();
307 }
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200308 REQUIRE(datastore.getItems("/example-schema:bossPerson") == DatastoreAccess::Tree{{"/example-schema:bossPerson", value}});
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200309 }
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200310 SECTION("bool values get correctly represented as bools")
311 {
312 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100313 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "true"s));
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200314 datastore.setLeaf("/example-schema:down", bool{true});
315 datastore.commitChanges();
316 }
317
Jan Kundrátb331b552020-01-23 15:25:29 +0100318 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200319 REQUIRE(datastore.getItems("/example-schema:down") == expected);
320 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200321
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200322 SECTION("getting items from the whole module")
323 {
324 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100325 REQUIRE_CALL(mock, write("/example-schema:up", std::nullopt, "true"s));
326 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "false"s));
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200327 datastore.setLeaf("/example-schema:up", bool{true});
328 datastore.setLeaf("/example-schema:down", bool{false});
329 datastore.commitChanges();
330 }
331
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200332 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200333 {"/example-schema:up", bool{true}},
334 {"/example-schema:down", bool{false}}
335 };
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200336 REQUIRE(datastore.getItems("/example-schema:*") == expected);
337 }
338
Václav Kubernát152ce222019-12-19 12:23:32 +0100339 SECTION("getItems returns correct datatypes")
340 {
341 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100342 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "lol"s));
Václav Kubernát152ce222019-12-19 12:23:32 +0100343 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
344 datastore.commitChanges();
345 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100346 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100347
348 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
349 }
350
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100351 SECTION("getItems on a list")
352 {
353 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100354 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']", std::nullopt, ""s));
355 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s));
356 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']", std::nullopt, ""s));
357 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s));
358 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']", std::nullopt, ""s));
359 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']/name", std::nullopt, "Petr"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200360 datastore.createItem("/example-schema:person[name='Jan']");
361 datastore.createItem("/example-schema:person[name='Michal']");
362 datastore.createItem("/example-schema:person[name='Petr']");
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100363 datastore.commitChanges();
364 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100365 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100366 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100367 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100368 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100369 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100370 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100371 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
372 };
373
374 REQUIRE(datastore.getItems("/example-schema:person") == expected);
375 }
376
Václav Kubernát69aabe92020-01-24 16:53:12 +0100377 SECTION("presence containers")
378 {
379 DatastoreAccess::Tree expected;
380 // Make sure it's not there before we create it
381 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
382
383 {
384 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200385 datastore.createItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100386 datastore.commitChanges();
387 }
388 expected = {
389 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
390 };
391 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
392
393 // Make sure it's not there after we delete it
394 {
395 REQUIRE_CALL(mock, write("/example-schema:pContainer", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200396 datastore.deleteItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100397 datastore.commitChanges();
398 }
399 expected = {};
400 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100401 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100402
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200403 SECTION("creating a non-existing schema node as a container")
404 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100405 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200406 datastore.createItem("/example-schema:non-existing-presence-container");
407 datastore.commitChanges();
408 });
409 }
410
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200411 SECTION("deleting a non-existing schema node as a container or leaf")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200412 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100413 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200414 datastore.deleteItem("/example-schema:non-existing-presence-container");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200415 datastore.commitChanges();
416 });
417 }
418
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100419 SECTION("nested presence container")
420 {
421 DatastoreAccess::Tree expected;
422 // Make sure it's not there before we create it
423 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
424 {
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100425 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200426 datastore.createItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100427 datastore.commitChanges();
428 }
429 expected = {
430 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
431 };
432 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
433 {
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100434 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200435 datastore.deleteItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100436 datastore.commitChanges();
437 }
438 expected = {};
439 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100440 }
441
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100442 SECTION("floats")
443 {
444 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
445 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.4"s));
446 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100447 DatastoreAccess::Tree expected{
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100448 {"/example-schema:leafDecimal", 123.4},
449 };
450 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
451 }
452
Jan Kundrát3ff50122020-05-07 00:37:50 +0200453 SECTION("unions")
454 {
455 datastore.setLeaf("/example-schema:unionIntString", int32_t{10});
456 REQUIRE_CALL(mock, write("/example-schema:unionIntString", std::nullopt, "10"s));
457 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100458 DatastoreAccess::Tree expected{
Jan Kundrát3ff50122020-05-07 00:37:50 +0200459 {"/example-schema:unionIntString", int32_t{10}},
460 };
461 REQUIRE(datastore.getItems("/example-schema:unionIntString") == expected);
462 }
463
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100464 SECTION("identityref")
465 {
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200466 datastore.setLeaf("/example-schema:beast", identityRef_{"example-schema", "Mammal"});
467 REQUIRE_CALL(mock, write("/example-schema:beast", std::nullopt, "example-schema:Mammal"s));
468 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100469 DatastoreAccess::Tree expected{
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200470 {"/example-schema:beast", identityRef_{"example-schema", "Mammal"}},
471 };
472 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
473
474 datastore.setLeaf("/example-schema:beast", identityRef_{"Whale"});
475 REQUIRE_CALL(mock, write("/example-schema:beast", "example-schema:Mammal", "example-schema:Whale"s));
476 datastore.commitChanges();
477 expected = {
478 {"/example-schema:beast", identityRef_{"example-schema", "Whale"}},
479 };
480 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
481 }
482
Jan Kundrát68985442020-05-07 02:15:34 +0200483 SECTION("binary")
484 {
485 datastore.setLeaf("/example-schema:blob", binary_{"cHduegByIQ=="s});
486 REQUIRE_CALL(mock, write("/example-schema:blob", std::nullopt, "cHduegByIQ=="s));
487 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100488 DatastoreAccess::Tree expected{
Jan Kundrát68985442020-05-07 02:15:34 +0200489 {"/example-schema:blob", binary_{"cHduegByIQ=="s}},
490 };
491 REQUIRE(datastore.getItems("/example-schema:blob") == expected);
492 }
493
Jan Kundrát379bb572020-05-07 03:23:13 +0200494 SECTION("empty")
495 {
496 datastore.setLeaf("/example-schema:dummy", empty_{});
497 REQUIRE_CALL(mock, write("/example-schema:dummy", std::nullopt, ""s));
498 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100499 DatastoreAccess::Tree expected{
Jan Kundrát379bb572020-05-07 03:23:13 +0200500 {"/example-schema:dummy", empty_{}},
501 };
502 REQUIRE(datastore.getItems("/example-schema:dummy") == expected);
503 }
504
Václav Kubernát19097f32020-10-05 10:08:29 +0200505 SECTION("bits")
506 {
507 datastore.setLeaf("/example-schema:flags", bits_{{"sign", "carry"}});
508 REQUIRE_CALL(mock, write("/example-schema:flags", std::nullopt, "carry sign"s));
509 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100510 DatastoreAccess::Tree expected{
Václav Kubernát19097f32020-10-05 10:08:29 +0200511 {"/example-schema:flags", bits_{{"carry", "sign"}}},
512 };
513 REQUIRE(datastore.getItems("/example-schema:flags") == expected);
514 }
515
Václav Kubernát74487df2020-06-04 01:29:28 +0200516#if not defined(yang_BACKEND)
Jan Kundrátbb525b42020-02-04 11:56:59 +0100517 SECTION("operational data")
518 {
519 MockDataSupplier mockOpsData;
Václav Kubernát654303f2020-07-31 13:16:54 +0200520 OperationalDataSubscription opsDataSub("example-schema", "/example-schema:temperature", mockOpsData);
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100521 OperationalDataSubscription opsDataSub2("example-schema", "/example-schema:users", mockOpsData);
Jan Kundrátbb525b42020-02-04 11:56:59 +0100522 DatastoreAccess::Tree expected;
523 std::string xpath;
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100524
Jan Kundrátbb525b42020-02-04 11:56:59 +0100525 SECTION("temperature")
526 {
527 expected = {{"/example-schema:temperature", int32_t{22}}};
528 xpath = "/example-schema:temperature";
529 }
530
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100531 SECTION("key-less lists")
532 {
533 expected = {
534 {"/example-schema:users/userList[1]", special_{SpecialValue::List}},
535 {"/example-schema:users/userList[1]/name", std::string{"John"}},
536 {"/example-schema:users/userList[1]/otherfield", std::string{"LOL"}},
537 {"/example-schema:users/userList[2]", special_{SpecialValue::List}},
538 {"/example-schema:users/userList[2]/name", std::string{"Foo"}},
539 {"/example-schema:users/userList[2]/otherfield", std::string{"Bar"}},
540 };
541 xpath = "/example-schema:users";
542 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100543 REQUIRE_CALL(mockOpsData, get_data(xpath)).RETURN(expected);
544 REQUIRE(datastore.getItems(xpath) == expected);
545 }
Václav Kubernát74487df2020-06-04 01:29:28 +0200546#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +0100547
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200548 SECTION("leaf list")
549 {
550 DatastoreAccess::Tree expected;
551 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "0.0.0.0"s));
552 REQUIRE_CALL(mock, write("/example-schema:addresses", std::nullopt, "127.0.0.1"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200553 datastore.createItem("/example-schema:addresses[.='0.0.0.0']");
554 datastore.createItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200555 datastore.commitChanges();
556 expected = {
557 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
558 {"/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s},
559 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
560 };
561 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
562
563 REQUIRE_CALL(mock, write("/example-schema:addresses", "0.0.0.0"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200564 datastore.deleteItem("/example-schema:addresses[.='0.0.0.0']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200565 datastore.commitChanges();
566 expected = {
567 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
568 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
569 };
570 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
571
572 REQUIRE_CALL(mock, write("/example-schema:addresses", "127.0.0.1"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200573 datastore.deleteItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200574 datastore.commitChanges();
575 expected = {};
576 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
577 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100578
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200579 SECTION("deleting a non-existing leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200580 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100581 catching<OnKeyNotFound>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200582 datastore.deleteItem("/example-schema:addresses[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200583 datastore.commitChanges();
584 });
585 }
586
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200587 SECTION("accessing a non-existing schema node as a leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200588 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100589 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200590 datastore.createItem("/example-schema:non-existing[.='non-existing']");
591 datastore.commitChanges();
592 });
593
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100594 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200595 datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200596 datastore.commitChanges();
597 });
598 }
599
Václav Kubernát7160a132020-04-03 02:11:01 +0200600 SECTION("copying data from startup refreshes the data")
601 {
602 {
603 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
604 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "123"s));
605 datastore.setLeaf("/example-schema:leafInt16", int16_t{123});
606 datastore.commitChanges();
607 }
608 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{{"/example-schema:leafInt16", int16_t{123}}});
609 REQUIRE_CALL(mock, write("/example-schema:leafInt16", "123"s, std::nullopt));
610 datastore.copyConfig(Datastore::Startup, Datastore::Running);
611 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
612 }
613
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200614 SECTION("moving leaflist instances")
615 {
616 DatastoreAccess::Tree expected;
617 {
Václav Kubernát654303f2020-07-31 13:16:54 +0200618 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "http"s));
619 // FIXME: Why no notifications for these??
620 // ... possibly because my subscription doesn't extract it properly?
621 // REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "ftp"s));
622 // REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200623 REQUIRE_CALL(mock, write("/example-schema:protocols", "http"s, "ftp"s));
624 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "pop3"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200625 datastore.createItem("/example-schema:protocols[.='http']");
626 datastore.createItem("/example-schema:protocols[.='ftp']");
627 datastore.createItem("/example-schema:protocols[.='pop3']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200628 datastore.commitChanges();
629 expected = {
630 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
631 {"/example-schema:protocols[.='http']", "http"s},
632 {"/example-schema:protocols[.='ftp']", "ftp"s},
633 {"/example-schema:protocols[.='pop3']", "pop3"s},
634 };
635 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
636 }
637
638 std::string sourcePath;
639 SECTION("begin")
640 {
641 REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
642 sourcePath = "/example-schema:protocols[.='pop3']";
643 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
644 datastore.commitChanges();
645 expected = {
646 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
647 {"/example-schema:protocols[.='pop3']", "pop3"s},
648 {"/example-schema:protocols[.='http']", "http"s},
649 {"/example-schema:protocols[.='ftp']", "ftp"s},
650 };
651 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
652 }
653
654 SECTION("end")
655 {
656 sourcePath = "/example-schema:protocols[.='http']";
657 REQUIRE_CALL(mock, write("/example-schema:protocols", "pop3"s, "http"s));
658 datastore.moveItem(sourcePath, yang::move::Absolute::End);
659 datastore.commitChanges();
660 expected = {
661 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
662 {"/example-schema:protocols[.='ftp']", "ftp"s},
663 {"/example-schema:protocols[.='pop3']", "pop3"s},
664 {"/example-schema:protocols[.='http']", "http"s},
665 };
666 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
667 }
668
669 SECTION("after")
670 {
671 sourcePath = "/example-schema:protocols[.='http']";
672 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
673 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{".", "ftp"s}}});
674 datastore.commitChanges();
675 expected = {
676 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
677 {"/example-schema:protocols[.='ftp']", "ftp"s},
678 {"/example-schema:protocols[.='http']", "http"s},
679 {"/example-schema:protocols[.='pop3']", "pop3"s},
680 };
681 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
682 }
683
684 SECTION("before")
685 {
686 sourcePath = "/example-schema:protocols[.='http']";
687 REQUIRE_CALL(mock, write("/example-schema:protocols", "ftp"s, "http"s));
688 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{".", "pop3"s}}});
689 datastore.commitChanges();
690 expected = {
691 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
692 {"/example-schema:protocols[.='ftp']", "ftp"s},
693 {"/example-schema:protocols[.='http']", "http"s},
694 {"/example-schema:protocols[.='pop3']", "pop3"s},
695 };
696 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
697 }
698 }
699
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200700 SECTION("moving non-existing schema nodes")
701 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100702 catching<OnInvalidSchemaPathMove>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200703 datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
704 datastore.commitChanges();
705 });
706 }
707
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200708 SECTION("moving list instances")
709 {
710 DatastoreAccess::Tree expected;
711 {
Václav Kubernát654303f2020-07-31 13:16:54 +0200712 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", std::nullopt, ""s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200713 REQUIRE_CALL(mock, write("/example-schema:players[name='John']/name", std::nullopt, "John"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200714 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']", ""s, ""s));
715 REQUIRE_CALL(mock, write("/example-schema:players[name='Eve']/name", std::nullopt, "Eve"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200716 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", ""s, ""s));
Václav Kubernát654303f2020-07-31 13:16:54 +0200717 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']/name", std::nullopt, "Adam"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200718 datastore.createItem("/example-schema:players[name='John']");
719 datastore.createItem("/example-schema:players[name='Eve']");
720 datastore.createItem("/example-schema:players[name='Adam']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200721 datastore.commitChanges();
722 expected = {
723 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
724 {"/example-schema:players[name='John']/name", "John"s},
725 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
726 {"/example-schema:players[name='Eve']/name", "Eve"s},
727 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
728 {"/example-schema:players[name='Adam']/name", "Adam"s},
729 };
730 REQUIRE(datastore.getItems("/example-schema:players") == expected);
731 }
732
733 std::string sourcePath;
734 SECTION("begin")
735 {
736 sourcePath = "/example-schema:players[name='Adam']";
737 REQUIRE_CALL(mock, write("/example-schema:players[name='Adam']", std::nullopt, ""s));
738 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
739 datastore.commitChanges();
740 expected = {
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 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
746 {"/example-schema:players[name='Eve']/name", "Eve"s},
747 };
748 REQUIRE(datastore.getItems("/example-schema:players") == expected);
749 }
750
751 SECTION("end")
752 {
753 sourcePath = "/example-schema:players[name='John']";
754 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
755 datastore.moveItem(sourcePath, yang::move::Absolute::End);
756 datastore.commitChanges();
757 expected = {
758 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
759 {"/example-schema:players[name='Eve']/name", "Eve"s},
760 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
761 {"/example-schema:players[name='Adam']/name", "Adam"s},
762 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
763 {"/example-schema:players[name='John']/name", "John"s},
764 };
765 REQUIRE(datastore.getItems("/example-schema:players") == expected);
766 }
767
768 SECTION("after")
769 {
770 sourcePath = "/example-schema:players[name='John']";
771 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
772 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{"name", "Eve"s}}});
773 datastore.commitChanges();
774 expected = {
775 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
776 {"/example-schema:players[name='Eve']/name", "Eve"s},
777 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
778 {"/example-schema:players[name='John']/name", "John"s},
779 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
780 {"/example-schema:players[name='Adam']/name", "Adam"s},
781 };
782 REQUIRE(datastore.getItems("/example-schema:players") == expected);
783 }
784
785 SECTION("before")
786 {
787 sourcePath = "/example-schema:players[name='John']";
788 REQUIRE_CALL(mock, write("/example-schema:players[name='John']", ""s, ""s));
789 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{"name", "Adam"s}}});
790 datastore.commitChanges();
791 expected = {
792 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
793 {"/example-schema:players[name='Eve']/name", "Eve"s},
794 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
795 {"/example-schema:players[name='John']/name", "John"s},
796 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
797 {"/example-schema:players[name='Adam']/name", "Adam"s},
798 };
799 REQUIRE(datastore.getItems("/example-schema:players") == expected);
800 }
801 }
802
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200803 SECTION("getting /")
804 {
805 {
806 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "64"s));
807 datastore.setLeaf("/example-schema:leafInt32", 64);
808 datastore.commitChanges();
809 }
810
811 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200812 {"/example-schema:leafInt32", 64}
813 };
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200814 // This tests if we at least get the data WE added.
Václav Kubernát654303f2020-07-31 13:16:54 +0200815 REQUIRE(std::all_of(expected.begin(), expected.end(), [items = datastore.getItems("/")] (const auto& item) {
816 return std::find(items.begin(), items.end(), item) != items.end();
817 }));
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200818 }
819
Václav Kubernát36986c52020-06-25 10:30:05 +0200820 SECTION("setting and removing without commit")
821 {
822 datastore.setLeaf("/example-schema:leafInt32", 64);
823 datastore.deleteItem("/example-schema:leafInt32");
824 }
825
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200826 SECTION("two key lists")
827 {
828 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']", std::nullopt, ""s));
829 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']/x", std::nullopt, "12"s));
830 REQUIRE_CALL(mock, write("/example-schema:point[x='12'][y='10']/y", std::nullopt, "10"s));
831 datastore.createItem("/example-schema:point[x='12'][y='10']");
832 datastore.commitChanges();
833 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:point") != std::string::npos);
834 }
835
Václav Kubernát73109382018-09-14 19:52:03 +0200836 waitForCompletionAndBitMore(seq1);
837}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100838
Václav Kubernát654303f2020-07-31 13:16:54 +0200839struct ActionCb {
840 int operator()(sysrepo::S_Session session,
841 const char* xpath,
842 [[maybe_unused]] const sysrepo::S_Vals input,
843 [[maybe_unused]] sr_event_t event,
844 [[maybe_unused]] uint32_t request_id,
845 sysrepo::S_Vals_Holder output)
Václav Kubernáta8789602020-07-20 15:18:19 +0200846 {
Václav Kubernát654303f2020-07-31 13:16:54 +0200847 if (session->get_context()->get_node(nullptr, xpath)->path(LYS_PATH_FIRST_PREFIX) == "/example-schema:ports/shutdown") {
Václav Kubernáta8789602020-07-20 15:18:19 +0200848 auto buf = output->allocate(1);
849 buf->val(0)->set(joinPaths(xpath, "success").c_str(), true);
850 return SR_ERR_OK;
851 }
852 throw std::runtime_error("unrecognized RPC");
853 }
Václav Kubernát654303f2020-07-31 13:16:54 +0200854};
Václav Kubernáta8789602020-07-20 15:18:19 +0200855
Václav Kubernát654303f2020-07-31 13:16:54 +0200856struct RpcCb {
857 int operator()([[maybe_unused]] sysrepo::S_Session session,
858 const char* xpath,
859 const sysrepo::S_Vals input,
860 [[maybe_unused]] sr_event_t event,
861 [[maybe_unused]] uint32_t request_id,
862 sysrepo::S_Vals_Holder output)
Jan Kundrát6ee84792020-01-24 01:43:36 +0100863 {
864 const auto nukes = "/example-schema:launch-nukes"s;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200865 if (xpath == "/example-schema:noop"s || xpath == "/example-schema:fire"s) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100866 return SR_ERR_OK;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200867 }
868
869 if (xpath == nukes) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100870 uint64_t kilotons = 0;
871 bool hasCities = false;
872 for (size_t i = 0; i < input->val_cnt(); ++i) {
873 const auto& val = input->val(i);
Václav Kubernátf4b6a932020-07-09 10:34:19 +0200874 if (val->xpath() == nukes + "/payload") {
875 continue; // ignore, container
876 }
877 if (val->xpath() == nukes + "/description") {
878 continue; // unused
879 }
880
Jan Kundrát6ee84792020-01-24 01:43:36 +0100881 if (val->xpath() == nukes + "/payload/kilotons") {
882 kilotons = val->data()->get_uint64();
Jan Kundrát6ee84792020-01-24 01:43:36 +0100883 } else if (std::string_view{val->xpath()}.find(nukes + "/cities") == 0) {
884 hasCities = true;
885 } else {
886 throw std::runtime_error("RPC launch-nukes: unexpected input "s + val->xpath());
887 }
888 }
889 if (kilotons == 333'666) {
890 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
891 return SR_ERR_OK;
892 }
893 auto buf = output->allocate(2);
894 size_t i = 0;
895 buf->val(i++)->set((nukes + "/blast-radius").c_str(), uint32_t{33'666});
896 buf->val(i++)->set((nukes + "/actual-yield").c_str(), static_cast<uint64_t>(1.33 * kilotons));
897 if (hasCities) {
898 buf = output->reallocate(output->val_cnt() + 2);
899 buf->val(i++)->set((nukes + "/damaged-places/targets[city='London']/city").c_str(), "London");
900 buf->val(i++)->set((nukes + "/damaged-places/targets[city='Berlin']/city").c_str(), "Berlin");
901 }
902 return SR_ERR_OK;
903 }
904 throw std::runtime_error("unrecognized RPC");
905 }
906};
907
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100908TEST_CASE("rpc/action")
909{
Jan Kundrát6ee84792020-01-24 01:43:36 +0100910 trompeloeil::sequence seq1;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100911
912#ifdef sysrepo_BACKEND
Václav Kubernát654303f2020-07-31 13:16:54 +0200913 auto datastore = std::make_shared<SysrepoAccess>(Datastore::Running);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100914#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200915 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
916 auto datastore = std::make_shared<NetconfAccess>(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200917#elif defined(yang_BACKEND)
Václav Kubernáte7248b22020-06-26 15:38:59 +0200918 auto datastore = std::make_shared<YangAccess>();
919 datastore->addSchemaDir(schemaDir);
920 datastore->addSchemaFile(exampleSchemaFile);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100921#else
922#error "Unknown backend"
923#endif
924
Václav Kubernát654303f2020-07-31 13:16:54 +0200925 auto srConn = std::make_shared<sysrepo::Connection>();
Václav Kubernáta8789602020-07-20 15:18:19 +0200926 auto srSession = std::make_shared<sysrepo::Session>(srConn);
927 auto srSubscription = std::make_shared<sysrepo::Subscribe>(srSession);
Václav Kubernát654303f2020-07-31 13:16:54 +0200928 auto rpcCb = std::make_shared<RpcCb>();
929 auto actionCb = std::make_shared<ActionCb>();
Václav Kubernáta8789602020-07-20 15:18:19 +0200930 sysrepo::Logs{}.set_stderr(SR_LL_INF);
Václav Kubernát654303f2020-07-31 13:16:54 +0200931 SysrepoSubscription subscription("example-schema", nullptr);
Václav Kubernáta8789602020-07-20 15:18:19 +0200932 // careful here, sysrepo insists on module_change CBs being registered before RPC CBs, otherwise there's a memleak
Václav Kubernát654303f2020-07-31 13:16:54 +0200933 srSubscription->rpc_subscribe("/example-schema:noop", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
934 srSubscription->rpc_subscribe("/example-schema:launch-nukes", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
935 srSubscription->rpc_subscribe("/example-schema:fire", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
936 srSubscription->rpc_subscribe("/example-schema:ports/shutdown", ActionCb{}, 0, SR_SUBSCR_CTX_REUSE);
Václav Kubernáte7248b22020-06-26 15:38:59 +0200937
Václav Kubernáta8789602020-07-20 15:18:19 +0200938 SECTION("rpc")
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200939 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200940 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
941 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
942 };
943 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100944
Václav Kubernáta8789602020-07-20 15:18:19 +0200945 // ProxyDatastore cannot easily read DatastoreAccess::Tree, so we need to set the input via create/setLeaf/etc.
946 SECTION("valid")
947 {
948 std::string rpc;
949 DatastoreAccess::Tree input, output;
950
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100951 SECTION("noop")
952 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200953 rpc = "/example-schema:noop";
Václav Kubernátb3960f82020-12-01 03:21:48 +0100954 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +0200955 }
956
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100957 SECTION("small nuke")
958 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200959 rpc = "/example-schema:launch-nukes";
960 input = {
961 {"description", "dummy"s},
962 {"payload/kilotons", uint64_t{333'666}},
963 };
Václav Kubernátb3960f82020-12-01 03:21:48 +0100964 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +0200965 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{333'666});
966 // no data are returned
967 }
968
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100969 SECTION("small nuke")
970 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200971 rpc = "/example-schema:launch-nukes";
972 input = {
973 {"description", "dummy"s},
974 {"payload/kilotons", uint64_t{4}},
975 };
Václav Kubernátb3960f82020-12-01 03:21:48 +0100976 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +0200977 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{4});
978
979 output = {
980 {"blast-radius", uint32_t{33'666}},
981 {"actual-yield", uint64_t{5}},
982 };
983 }
984
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100985 SECTION("with lists")
986 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200987 rpc = "/example-schema:launch-nukes";
988 input = {
989 {"payload/kilotons", uint64_t{6}},
990 {"cities/targets[city='Prague']/city", "Prague"s},
991 };
Václav Kubernátb3960f82020-12-01 03:21:48 +0100992 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +0200993 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{6});
994 proxyDatastore.createItem("/example-schema:launch-nukes/example-schema:cities/example-schema:targets[city='Prague']");
995 output = {
996 {"blast-radius", uint32_t{33'666}},
997 {"actual-yield", uint64_t{7}},
998 {"damaged-places", special_{SpecialValue::PresenceContainer}},
999 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
1000 {"damaged-places/targets[city='London']/city", "London"s},
1001 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
1002 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
1003 };
1004 }
1005
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001006 SECTION("with leafref")
1007 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001008 datastore->createItem("/example-schema:person[name='Colton']");
1009 datastore->commitChanges();
1010
1011 rpc = "/example-schema:fire";
1012 input = {
1013 {"whom", "Colton"s}
1014 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001015 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001016 proxyDatastore.setLeaf("/example-schema:fire/example-schema:whom", "Colton"s);
1017 }
1018
Václav Kubernátb3960f82020-12-01 03:21:48 +01001019 catching<OnExec>([&] { REQUIRE(datastore->execute(rpc, input) == output); });
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001020 catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001021 }
1022
Václav Kubernáta8789602020-07-20 15:18:19 +02001023 SECTION("non-existing RPC")
1024 {
Václav Kubernátb3960f82020-12-01 03:21:48 +01001025 catching<OnInvalidRpcPath>([&] { datastore->execute("/example-schema:non-existing", DatastoreAccess::Tree{}); });
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001026 }
Jan Kundrát6ee84792020-01-24 01:43:36 +01001027 }
1028
Václav Kubernáta8789602020-07-20 15:18:19 +02001029 SECTION("action")
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001030 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001031 std::string path;
1032 DatastoreAccess::Tree input, output;
1033
1034 output = {
1035#ifdef netconf_BACKEND
1036 {"/example-schema:ports[name='A']", special_{SpecialValue::List}},
1037 {"/example-schema:ports[name='A']/name", enum_{"A"}},
1038#endif
1039 {"success", true}
1040 };
1041 datastore->createItem("/example-schema:ports[name='A']");
1042 datastore->commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001043 SECTION("shutdown")
1044 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001045 path = "/example-schema:ports[name='A']/shutdown";
1046 }
1047
Václav Kubernátb3960f82020-12-01 03:21:48 +01001048 catching<OnExec>([&] { REQUIRE(datastore->execute(path, input) == output); });
Jan Kundrát6ee84792020-01-24 01:43:36 +01001049 }
1050
Jan Kundrát6ee84792020-01-24 01:43:36 +01001051 waitForCompletionAndBitMore(seq1);
1052}