blob: db477e98d017d6184c6cae326475ca7b44536d8f [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átcfdb9222021-07-07 22:36:24 +020011#include <sysrepo-cpp/utils/exception.hpp>
12#include <sysrepo-cpp/utils/utils.hpp>
Václav Kubernáte7248b22020-06-26 15:38:59 +020013#include "proxy_datastore.hpp"
Václav Kubernátb4e5b182020-11-16 19:55:09 +010014#include "yang_schema.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020015
Václav Kubernátc31bd602019-03-07 11:44:48 +010016#ifdef sysrepo_BACKEND
Václav Kubernát73109382018-09-14 19:52:03 +020017#include "sysrepo_access.hpp"
Jan Kundrát7ec214d2020-06-19 17:05:07 +020018using OnInvalidSchemaPathCreate = DatastoreException;
Václav Kubernát654303f2020-07-31 13:16:54 +020019using OnInvalidSchemaPathDelete = DatastoreException;
Václav Kubernátcfdb9222021-07-07 22:36:24 +020020using OnInvalidSchemaPathMove = sysrepo::ErrorWithCode;
Václav Kubernát40776132021-02-03 08:47:33 +010021using OnInvalidRpcPath = std::runtime_error;
Václav Kubernátcfdb9222021-07-07 22:36:24 +020022using OnInvalidRpcInput = sysrepo::ErrorWithCode;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020023using OnKeyNotFound = void;
Václav Kubernáta8789602020-07-20 15:18:19 +020024using OnExec = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010025#elif defined(netconf_BACKEND)
Jan Kundrát7ec214d2020-06-19 17:05:07 +020026using OnInvalidSchemaPathCreate = std::runtime_error;
27using OnInvalidSchemaPathDelete = std::runtime_error;
28using OnInvalidSchemaPathMove = std::runtime_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020029using OnInvalidRpcPath = std::runtime_error;
Václav Kubernát2edfe542021-02-03 08:08:29 +010030using OnInvalidRpcInput = std::runtime_error;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020031using OnKeyNotFound = std::runtime_error;
Václav Kubernáta8789602020-07-20 15:18:19 +020032using OnExec = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010033#include "netconf_access.hpp"
Václav Kubernát74487df2020-06-04 01:29:28 +020034#elif defined(yang_BACKEND)
35#include <fstream>
36#include "yang_access.hpp"
37#include "yang_access_test_vars.hpp"
38using OnInvalidSchemaPathCreate = DatastoreException;
39using OnInvalidSchemaPathDelete = DatastoreException;
40using OnInvalidSchemaPathMove = DatastoreException;
41using OnInvalidRpcPath = DatastoreException;
Václav Kubernát2edfe542021-02-03 08:08:29 +010042using OnInvalidRpcInput = std::logic_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020043using OnKeyNotFound = DatastoreException;
Václav Kubernáta8789602020-07-20 15:18:19 +020044using OnExec = std::logic_error;
Václav Kubernátc31bd602019-03-07 11:44:48 +010045#else
46#error "Unknown backend"
47#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +010048#include "pretty_printers.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020049#include "sysrepo_subscription.hpp"
Václav Kubernát8e121ff2019-10-15 15:47:45 +020050#include "utils.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020051
Jan Kundrát6ee84792020-01-24 01:43:36 +010052using namespace std::literals::string_literals;
53
Václav Kubernát69aabe92020-01-24 16:53:12 +010054class MockRecorder : public trompeloeil::mock_interface<Recorder> {
Václav Kubernát73109382018-09-14 19:52:03 +020055public:
Václav Kubernátcfdb9222021-07-07 22:36:24 +020056 IMPLEMENT_MOCK5(write);
Václav Kubernát73109382018-09-14 19:52:03 +020057};
58
Jan Kundrátbb525b42020-02-04 11:56:59 +010059class MockDataSupplier : public trompeloeil::mock_interface<DataSupplier> {
60public:
61 IMPLEMENT_CONST_MOCK1(get_data);
62};
63
Jan Kundrát7ec214d2020-06-19 17:05:07 +020064namespace {
65template <class ...> constexpr std::false_type always_false [[maybe_unused]] {};
66template <class Exception, typename Callable> void catching(const Callable& what) {
67
68 if constexpr (std::is_same_v<Exception, void>) {
Jan Kundrát3867c9e2020-06-18 20:26:45 +020069 what();
Jan Kundrát7ec214d2020-06-19 17:05:07 +020070 } else if constexpr (std::is_same<Exception, std::runtime_error>()) {
71 // cannot use REQUIRE_THROWS_AS(..., Exception) directly because that one
72 // needs an extra `typename` deep in the bowels of doctest
73 REQUIRE_THROWS_AS(what(), std::runtime_error);
Václav Kubernát74487df2020-06-04 01:29:28 +020074 } else if constexpr (std::is_same<Exception, std::logic_error>()) {
75 REQUIRE_THROWS_AS(what(), std::logic_error);
Jan Kundrát7ec214d2020-06-19 17:05:07 +020076 } else if constexpr (std::is_same<Exception, DatastoreException>()) {
77 REQUIRE_THROWS_AS(what(), DatastoreException);
Václav Kubernátcfdb9222021-07-07 22:36:24 +020078 } else if constexpr (std::is_same<Exception, sysrepo::ErrorWithCode>()) {
79 REQUIRE_THROWS_AS(what(), sysrepo::ErrorWithCode);
Jan Kundrát7ec214d2020-06-19 17:05:07 +020080 } else {
81 static_assert(always_false<Exception>); // https://stackoverflow.com/a/53945549/2245623
Jan Kundrát3867c9e2020-06-18 20:26:45 +020082 }
83}
Jan Kundrát7ec214d2020-06-19 17:05:07 +020084}
Jan Kundrát3867c9e2020-06-18 20:26:45 +020085
Václav Kubernát74487df2020-06-04 01:29:28 +020086#if defined(yang_BACKEND)
87class TestYangAccess : public YangAccess {
88public:
89 void commitChanges() override
90 {
91 YangAccess::commitChanges();
92 dumpToSysrepo();
93 }
94
95 void copyConfig(const Datastore source, const Datastore destination) override
96 {
97 YangAccess::copyConfig(source, destination);
98 dumpToSysrepo();
99 }
100
101private:
102 void dumpToSysrepo()
103 {
104 {
105 std::ofstream of(testConfigFile);
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200106 of << dump(DataFormat::Xml);
Václav Kubernát74487df2020-06-04 01:29:28 +0200107 }
Jan Kundrát86840ec2021-01-27 15:38:08 +0100108 auto command = std::string(sysrepocfgExecutable) + " --import=" + testConfigFile + " --format=xml --datastore=running --module=example-schema";
Václav Kubernát74487df2020-06-04 01:29:28 +0200109 REQUIRE(std::system(command.c_str()) == 0);
110 }
111};
112#endif
113
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200114TEST_CASE("setting/getting values")
Václav Kubernát73109382018-09-14 19:52:03 +0200115{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200116 sysrepo::setLogLevelStderr(sysrepo::LogLevel::Information);
Václav Kubernát73109382018-09-14 19:52:03 +0200117 trompeloeil::sequence seq1;
Václav Kubernáteaf56682021-02-22 17:15:41 +0100118 MockRecorder mockRunning;
119 MockRecorder mockStartup;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200120
121 sysrepo::Connection{}.sessionStart().copyConfig(sysrepo::Datastore::Startup, "example-schema", std::chrono::milliseconds(1000));
122
Václav Kubernáteaf56682021-02-22 17:15:41 +0100123 SysrepoSubscription subRunning("example-schema", &mockRunning);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200124 SysrepoSubscription subStartup("example-schema", &mockStartup, sysrepo::Datastore::Startup);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100125
126#ifdef sysrepo_BACKEND
Václav Kubernátf5d75152020-12-03 03:52:34 +0100127 SysrepoAccess datastore;
Václav Kubernátc31bd602019-03-07 11:44:48 +0100128#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200129 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
130 NetconfAccess datastore(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200131#elif defined(yang_BACKEND)
132 TestYangAccess datastore;
133 datastore.addSchemaDir(schemaDir);
134 datastore.addSchemaFile(exampleSchemaFile);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100135#else
136#error "Unknown backend"
137#endif
Václav Kubernát73109382018-09-14 19:52:03 +0200138
Václav Kubernát69aabe92020-01-24 16:53:12 +0100139
Václav Kubernát134d78f2019-09-03 16:42:29 +0200140 SECTION("set leafInt8 to -128")
Václav Kubernát73109382018-09-14 19:52:03 +0200141 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200142 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt8", std::nullopt, "-128"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200143 datastore.setLeaf("/example-schema:leafInt8", int8_t{-128});
144 datastore.commitChanges();
145 }
146
147 SECTION("set leafInt16 to -32768")
148 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200149 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt16", std::nullopt, "-32768"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200150 datastore.setLeaf("/example-schema:leafInt16", int16_t{-32768});
151 datastore.commitChanges();
152 }
153
154 SECTION("set leafInt32 to -2147483648")
155 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200156 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt32", std::nullopt, "-2147483648"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200157 datastore.setLeaf("/example-schema:leafInt32", int32_t{-2147483648});
158 datastore.commitChanges();
159 }
160
161 SECTION("set leafInt64 to -50000000000")
162 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200163 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt64", std::nullopt, "-50000000000"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200164 datastore.setLeaf("/example-schema:leafInt64", int64_t{-50000000000});
165 datastore.commitChanges();
166 }
167
168 SECTION("set leafUInt8 to 255")
169 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200170 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafUInt8", std::nullopt, "255"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200171 datastore.setLeaf("/example-schema:leafUInt8", uint8_t{255});
172 datastore.commitChanges();
173 }
174
175 SECTION("set leafUInt16 to 65535")
176 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200177 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafUInt16", std::nullopt, "65535"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200178 datastore.setLeaf("/example-schema:leafUInt16", uint16_t{65535});
179 datastore.commitChanges();
180 }
181
182 SECTION("set leafUInt32 to 4294967295")
183 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200184 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafUInt32", std::nullopt, "4294967295"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200185 datastore.setLeaf("/example-schema:leafUInt32", uint32_t{4294967295});
186 datastore.commitChanges();
187 }
188
189 SECTION("set leafUInt64 to 50000000000")
190 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200191 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafUInt64", std::nullopt, "50000000000"s, std::nullopt));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200192 datastore.setLeaf("/example-schema:leafUInt64", uint64_t{50000000000});
Václav Kubernát73109382018-09-14 19:52:03 +0200193 datastore.commitChanges();
194 }
195
196 SECTION("set leafEnum to coze")
197 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200198 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafEnum", std::nullopt, "coze"s, std::nullopt));
Václav Kubernát73109382018-09-14 19:52:03 +0200199 datastore.setLeaf("/example-schema:leafEnum", enum_{"coze"});
200 datastore.commitChanges();
201 }
202
203 SECTION("set leafDecimal to 123.544")
204 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200205 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafDecimal", std::nullopt, "123.544"s, std::nullopt));
Václav Kubernát73109382018-09-14 19:52:03 +0200206 datastore.setLeaf("/example-schema:leafDecimal", 123.544);
207 datastore.commitChanges();
208 }
209
Jan Kundrátd2872862020-06-18 21:02:00 +0200210 SECTION("set a string, then delete it")
211 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200212 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafString", std::nullopt, "blah"s, std::nullopt));
Jan Kundrátd2872862020-06-18 21:02:00 +0200213 datastore.setLeaf("/example-schema:leafString", "blah"s);
214 datastore.commitChanges();
215 DatastoreAccess::Tree expected{{"/example-schema:leafString", "blah"s}};
216 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
217
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200218 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:leafString", "blah"s, std::nullopt, std::nullopt));
Jan Kundrátd2872862020-06-18 21:02:00 +0200219 datastore.deleteItem("/example-schema:leafString");
220 datastore.commitChanges();
221 expected.clear();
222 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
223 }
224
Václav Kubernát8e756642021-11-05 00:04:54 +0100225 SECTION("set a string, then set it to something else without commiting")
226 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200227 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafString", std::nullopt, "oops"s, std::nullopt));
Václav Kubernát8e756642021-11-05 00:04:54 +0100228 datastore.setLeaf("/example-schema:leafString", "blah"s);
229 datastore.setLeaf("/example-schema:leafString", "oops"s);
230 datastore.commitChanges();
231 DatastoreAccess::Tree expected{{"/example-schema:leafString", "oops"s}};
232 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
233
234 }
235
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200236 SECTION("set a non-existing leaf")
237 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100238 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200239 datastore.setLeaf("/example-schema:non-existing", "what"s);
240 });
241 }
242
Václav Kubernát73109382018-09-14 19:52:03 +0200243 SECTION("create presence container")
244 {
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200245 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") == std::string::npos);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200246 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:pContainer", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200247 datastore.createItem("/example-schema:pContainer");
Václav Kubernát73109382018-09-14 19:52:03 +0200248 datastore.commitChanges();
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200249 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") != std::string::npos);
Václav Kubernát73109382018-09-14 19:52:03 +0200250 }
251
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100252 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200253 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100254 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200255 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Nguyen']", std::nullopt, std::nullopt, std::nullopt));
256 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Nguyen']/name", std::nullopt, "Nguyen"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200257 datastore.createItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100258 datastore.commitChanges();
259 }
260 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200261 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:person[name='Nguyen']", std::nullopt, std::nullopt, std::nullopt));
262 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:person[name='Nguyen']/name", "Nguyen"s, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200263 datastore.deleteItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100264 datastore.commitChanges();
265 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200266 }
267
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200268 SECTION("deleting non-existing list keys")
269 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100270 catching<OnKeyNotFound>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200271 datastore.deleteItem("/example-schema:person[name='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200272 datastore.commitChanges();
273 });
274 }
275
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200276 SECTION("accessing non-existing schema nodes as a list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200277 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100278 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200279 datastore.createItem("/example-schema:non-existing-list[xxx='blah']");
280 datastore.commitChanges();
281 });
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100282 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200283 datastore.deleteItem("/example-schema:non-existing-list[xxx='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200284 datastore.commitChanges();
285 });
286 }
287
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200288 SECTION("leafref pointing to a key of a list")
289 {
290 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200291 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Dan']", std::nullopt, std::nullopt, std::nullopt));
292 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s, std::nullopt));
293 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Elfi']", std::nullopt, std::nullopt, std::nullopt));
294 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s, std::nullopt));
295 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Kolafa']", std::nullopt, std::nullopt, std::nullopt));
296 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Kolafa']/name", std::nullopt, "Kolafa"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200297 datastore.createItem("/example-schema:person[name='Dan']");
298 datastore.createItem("/example-schema:person[name='Elfi']");
299 datastore.createItem("/example-schema:person[name='Kolafa']");
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200300 datastore.commitChanges();
301 }
302
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200303 std::string value;
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200304 SECTION("Dan")
305 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200306 value = "Dan";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200307 }
308
309 SECTION("Elfi")
310 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200311 value = "Elfi";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200312 }
313
314 SECTION("Kolafa")
315 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200316 value = "Kolafa";
317 }
318
319 datastore.setLeaf("/example-schema:bossPerson", value);
320 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200321 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:bossPerson", std::nullopt, value, std::nullopt));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200322 datastore.commitChanges();
323 }
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200324 REQUIRE(datastore.getItems("/example-schema:bossPerson") == DatastoreAccess::Tree{{"/example-schema:bossPerson", value}});
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200325 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200326
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200327 SECTION("bool values get correctly represented as bools")
328 {
329 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200330 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:down", std::nullopt, "true"s, std::nullopt));
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200331 datastore.setLeaf("/example-schema:down", bool{true});
332 datastore.commitChanges();
333 }
334
Jan Kundrátb331b552020-01-23 15:25:29 +0100335 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200336 REQUIRE(datastore.getItems("/example-schema:down") == expected);
337 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200338
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200339 SECTION("getting items from the whole module")
340 {
341 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200342 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:up", std::nullopt, "true"s, std::nullopt));
343 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:down", std::nullopt, "false"s, std::nullopt));
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200344 datastore.setLeaf("/example-schema:up", bool{true});
345 datastore.setLeaf("/example-schema:down", bool{false});
346 datastore.commitChanges();
347 }
348
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200349 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200350 {"/example-schema:up", bool{true}},
351 {"/example-schema:down", bool{false}}
352 };
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200353 REQUIRE(datastore.getItems("/example-schema:*") == expected);
354 }
355
Václav Kubernát152ce222019-12-19 12:23:32 +0100356 SECTION("getItems returns correct datatypes")
357 {
358 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200359 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafEnum", std::nullopt, "lol"s, std::nullopt));
Václav Kubernát152ce222019-12-19 12:23:32 +0100360 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
361 datastore.commitChanges();
362 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100363 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100364
365 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
366 }
367
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100368 SECTION("getItems on a list")
369 {
370 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200371 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Jan']", std::nullopt, std::nullopt, std::nullopt));
372 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s, std::nullopt));
373 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Michal']", std::nullopt, std::nullopt, std::nullopt));
374 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s, std::nullopt));
375 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Petr']", std::nullopt, std::nullopt, std::nullopt));
376 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Petr']/name", std::nullopt, "Petr"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200377 datastore.createItem("/example-schema:person[name='Jan']");
378 datastore.createItem("/example-schema:person[name='Michal']");
379 datastore.createItem("/example-schema:person[name='Petr']");
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100380 datastore.commitChanges();
381 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100382 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100383 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100384 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100385 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100386 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100387 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100388 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
389 };
390
391 REQUIRE(datastore.getItems("/example-schema:person") == expected);
392 }
393
Václav Kubernát69aabe92020-01-24 16:53:12 +0100394 SECTION("presence containers")
395 {
396 DatastoreAccess::Tree expected;
397 // Make sure it's not there before we create it
398 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
399
400 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200401 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:pContainer", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200402 datastore.createItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100403 datastore.commitChanges();
404 }
405 expected = {
406 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
407 };
408 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
409
410 // Make sure it's not there after we delete it
411 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200412 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:pContainer", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200413 datastore.deleteItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100414 datastore.commitChanges();
415 }
416 expected = {};
417 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100418 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100419
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200420 SECTION("creating a non-existing schema node as a container")
421 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100422 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200423 datastore.createItem("/example-schema:non-existing-presence-container");
424 datastore.commitChanges();
425 });
426 }
427
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200428 SECTION("deleting a non-existing schema node as a container or leaf")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200429 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100430 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200431 datastore.deleteItem("/example-schema:non-existing-presence-container");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200432 datastore.commitChanges();
433 });
434 }
435
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100436 SECTION("nested presence container")
437 {
438 DatastoreAccess::Tree expected;
439 // Make sure it's not there before we create it
440 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
441 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200442 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:inventory/stuff", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200443 datastore.createItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100444 datastore.commitChanges();
445 }
446 expected = {
447 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
448 };
449 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
450 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200451 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:inventory/stuff", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200452 datastore.deleteItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100453 datastore.commitChanges();
454 }
455 expected = {};
456 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100457 }
458
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100459 SECTION("floats")
460 {
461 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200462 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafDecimal", std::nullopt, "123.4"s, std::nullopt));
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100463 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100464 DatastoreAccess::Tree expected{
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100465 {"/example-schema:leafDecimal", 123.4},
466 };
467 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
468 }
469
Jan Kundrát3ff50122020-05-07 00:37:50 +0200470 SECTION("unions")
471 {
472 datastore.setLeaf("/example-schema:unionIntString", int32_t{10});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200473 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:unionIntString", std::nullopt, "10"s, std::nullopt));
Jan Kundrát3ff50122020-05-07 00:37:50 +0200474 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100475 DatastoreAccess::Tree expected{
Jan Kundrát3ff50122020-05-07 00:37:50 +0200476 {"/example-schema:unionIntString", int32_t{10}},
477 };
478 REQUIRE(datastore.getItems("/example-schema:unionIntString") == expected);
479 }
480
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100481 SECTION("identityref")
482 {
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200483 datastore.setLeaf("/example-schema:beast", identityRef_{"example-schema", "Mammal"});
Jan Kundrátdfcc4852023-06-05 18:26:36 +0200484 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:beast", std::nullopt, "Mammal"s, std::nullopt));
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200485 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100486 DatastoreAccess::Tree expected{
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200487 {"/example-schema:beast", identityRef_{"example-schema", "Mammal"}},
488 };
489 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
490
491 datastore.setLeaf("/example-schema:beast", identityRef_{"Whale"});
Jan Kundrátdfcc4852023-06-05 18:26:36 +0200492 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Modified, "/example-schema:beast", "Mammal", "Whale"s, std::nullopt));
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200493 datastore.commitChanges();
494 expected = {
495 {"/example-schema:beast", identityRef_{"example-schema", "Whale"}},
496 };
497 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
498 }
499
Jan Kundrát68985442020-05-07 02:15:34 +0200500 SECTION("binary")
501 {
502 datastore.setLeaf("/example-schema:blob", binary_{"cHduegByIQ=="s});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200503 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:blob", std::nullopt, "cHduegByIQ=="s, std::nullopt));
Jan Kundrát68985442020-05-07 02:15:34 +0200504 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100505 DatastoreAccess::Tree expected{
Jan Kundrát68985442020-05-07 02:15:34 +0200506 {"/example-schema:blob", binary_{"cHduegByIQ=="s}},
507 };
508 REQUIRE(datastore.getItems("/example-schema:blob") == expected);
509 }
510
Jan Kundrát379bb572020-05-07 03:23:13 +0200511 SECTION("empty")
512 {
513 datastore.setLeaf("/example-schema:dummy", empty_{});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200514 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:dummy", std::nullopt, ""s, std::nullopt));
Jan Kundrát379bb572020-05-07 03:23:13 +0200515 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100516 DatastoreAccess::Tree expected{
Jan Kundrát379bb572020-05-07 03:23:13 +0200517 {"/example-schema:dummy", empty_{}},
518 };
519 REQUIRE(datastore.getItems("/example-schema:dummy") == expected);
520 }
521
Václav Kubernát19097f32020-10-05 10:08:29 +0200522 SECTION("bits")
523 {
524 datastore.setLeaf("/example-schema:flags", bits_{{"sign", "carry"}});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200525 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:flags", std::nullopt, "carry sign"s, std::nullopt));
Václav Kubernát19097f32020-10-05 10:08:29 +0200526 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100527 DatastoreAccess::Tree expected{
Václav Kubernát19097f32020-10-05 10:08:29 +0200528 {"/example-schema:flags", bits_{{"carry", "sign"}}},
529 };
530 REQUIRE(datastore.getItems("/example-schema:flags") == expected);
531 }
532
Václav Kubernát74487df2020-06-04 01:29:28 +0200533#if not defined(yang_BACKEND)
Jan Kundrátbb525b42020-02-04 11:56:59 +0100534 SECTION("operational data")
535 {
536 MockDataSupplier mockOpsData;
Václav Kubernát654303f2020-07-31 13:16:54 +0200537 OperationalDataSubscription opsDataSub("example-schema", "/example-schema:temperature", mockOpsData);
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100538 OperationalDataSubscription opsDataSub2("example-schema", "/example-schema:users", mockOpsData);
Jan Kundrátbb525b42020-02-04 11:56:59 +0100539 DatastoreAccess::Tree expected;
540 std::string xpath;
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100541
Jan Kundrátbb525b42020-02-04 11:56:59 +0100542 SECTION("temperature")
543 {
544 expected = {{"/example-schema:temperature", int32_t{22}}};
545 xpath = "/example-schema:temperature";
546 }
547
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100548 SECTION("key-less lists")
549 {
550 expected = {
551 {"/example-schema:users/userList[1]", special_{SpecialValue::List}},
552 {"/example-schema:users/userList[1]/name", std::string{"John"}},
553 {"/example-schema:users/userList[1]/otherfield", std::string{"LOL"}},
554 {"/example-schema:users/userList[2]", special_{SpecialValue::List}},
555 {"/example-schema:users/userList[2]/name", std::string{"Foo"}},
556 {"/example-schema:users/userList[2]/otherfield", std::string{"Bar"}},
557 };
558 xpath = "/example-schema:users";
559 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100560 REQUIRE_CALL(mockOpsData, get_data(xpath)).RETURN(expected);
561 REQUIRE(datastore.getItems(xpath) == expected);
562 }
Václav Kubernát74487df2020-06-04 01:29:28 +0200563#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +0100564
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200565 SECTION("leaf list")
566 {
567 DatastoreAccess::Tree expected;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200568 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:addresses[.='0.0.0.0']", std::nullopt, "0.0.0.0"s, std::nullopt));
569 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:addresses[.='127.0.0.1']", std::nullopt, "127.0.0.1"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200570 datastore.createItem("/example-schema:addresses[.='0.0.0.0']");
571 datastore.createItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200572 datastore.commitChanges();
573 expected = {
574 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
575 {"/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s},
576 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
577 };
578 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
579
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200580 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200581 datastore.deleteItem("/example-schema:addresses[.='0.0.0.0']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200582 datastore.commitChanges();
583 expected = {
584 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
585 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
586 };
587 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
588
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200589 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200590 datastore.deleteItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200591 datastore.commitChanges();
592 expected = {};
593 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
594 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100595
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200596 SECTION("deleting a non-existing leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200597 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100598 catching<OnKeyNotFound>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200599 datastore.deleteItem("/example-schema:addresses[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200600 datastore.commitChanges();
601 });
602 }
603
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200604 SECTION("accessing a non-existing schema node as a leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200605 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100606 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200607 datastore.createItem("/example-schema:non-existing[.='non-existing']");
608 datastore.commitChanges();
609 });
610
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100611 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200612 datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200613 datastore.commitChanges();
614 });
615 }
616
Václav Kubernát7160a132020-04-03 02:11:01 +0200617 SECTION("copying data from startup refreshes the data")
618 {
619 {
620 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200621 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt16", std::nullopt, "123"s, std::nullopt));
Václav Kubernát7160a132020-04-03 02:11:01 +0200622 datastore.setLeaf("/example-schema:leafInt16", int16_t{123});
623 datastore.commitChanges();
624 }
625 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{{"/example-schema:leafInt16", int16_t{123}}});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200626 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:leafInt16", "123"s, std::nullopt, std::nullopt));
Václav Kubernát7160a132020-04-03 02:11:01 +0200627 datastore.copyConfig(Datastore::Startup, Datastore::Running);
628 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
629 }
630
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200631 SECTION("moving leaflist instances")
632 {
633 DatastoreAccess::Tree expected;
634 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200635 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:protocols[.='http']", "", "http"s, std::nullopt));
636 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:protocols[.='ftp']", "http"s, "ftp"s, std::nullopt));
637 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:protocols[.='pop3']", "ftp"s, "pop3"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200638 datastore.createItem("/example-schema:protocols[.='http']");
639 datastore.createItem("/example-schema:protocols[.='ftp']");
640 datastore.createItem("/example-schema:protocols[.='pop3']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200641 datastore.commitChanges();
642 expected = {
643 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
644 {"/example-schema:protocols[.='http']", "http"s},
645 {"/example-schema:protocols[.='ftp']", "ftp"s},
646 {"/example-schema:protocols[.='pop3']", "pop3"s},
647 };
648 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
649 }
650
651 std::string sourcePath;
652 SECTION("begin")
653 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200654 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='pop3']", ""s, "pop3"s, std::nullopt));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200655 sourcePath = "/example-schema:protocols[.='pop3']";
656 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
657 datastore.commitChanges();
658 expected = {
659 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
660 {"/example-schema:protocols[.='pop3']", "pop3"s},
661 {"/example-schema:protocols[.='http']", "http"s},
662 {"/example-schema:protocols[.='ftp']", "ftp"s},
663 };
664 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
665 }
666
667 SECTION("end")
668 {
669 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200670
671#if defined(yang_BACKEND) || defined(netconf_BACKEND)
672 // Due to the libyang diff algorithm being imperfect, the move operations differ between backends.
673 // The same applies for the stuff below.
674 // https://github.com/sysrepo/sysrepo/issues/2732
675 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='ftp']", ""s, "ftp"s, std::nullopt));
676 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='pop3']", "ftp"s, "pop3"s, std::nullopt));
677#else
678 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='http']", "pop3"s, "http"s, std::nullopt));
679#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200680 datastore.moveItem(sourcePath, yang::move::Absolute::End);
681 datastore.commitChanges();
682 expected = {
683 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
684 {"/example-schema:protocols[.='ftp']", "ftp"s},
685 {"/example-schema:protocols[.='pop3']", "pop3"s},
686 {"/example-schema:protocols[.='http']", "http"s},
687 };
688 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
689 }
690
691 SECTION("after")
692 {
693 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200694#if defined(yang_BACKEND) || defined(netconf_BACKEND)
695 // see the test for "end" for explanation if this #ifdef
696 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='ftp']", ""s, "ftp"s, std::nullopt));
697#else
698 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='http']", "ftp"s, "http"s, std::nullopt));
699#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200700 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{".", "ftp"s}}});
701 datastore.commitChanges();
702 expected = {
703 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
704 {"/example-schema:protocols[.='ftp']", "ftp"s},
705 {"/example-schema:protocols[.='http']", "http"s},
706 {"/example-schema:protocols[.='pop3']", "pop3"s},
707 };
708 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
709 }
710
711 SECTION("before")
712 {
713 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200714#if defined(yang_BACKEND) || defined(netconf_BACKEND)
715 // see the test for "end" for explanation if this #ifdef
716 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='ftp']", ""s, "ftp"s, std::nullopt));
717#else
718 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='http']", "ftp"s, "http"s, std::nullopt));
719#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200720 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{".", "pop3"s}}});
721 datastore.commitChanges();
722 expected = {
723 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
724 {"/example-schema:protocols[.='ftp']", "ftp"s},
725 {"/example-schema:protocols[.='http']", "http"s},
726 {"/example-schema:protocols[.='pop3']", "pop3"s},
727 };
728 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
729 }
730 }
731
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200732 SECTION("moving non-existing schema nodes")
733 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100734 catching<OnInvalidSchemaPathMove>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200735 datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
736 datastore.commitChanges();
737 });
738 }
739
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200740 SECTION("moving list instances")
741 {
742 DatastoreAccess::Tree expected;
743 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200744 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='John']", std::nullopt, std::nullopt, ""s));
745 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='John']/name", std::nullopt, "John"s, std::nullopt));
746 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, "[name='John']"));
747 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Eve']/name", std::nullopt, "Eve"s, std::nullopt));
748 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Adam']", std::nullopt, std::nullopt, "[name='Eve']"));
749 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Adam']/name", std::nullopt, "Adam"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200750 datastore.createItem("/example-schema:players[name='John']");
751 datastore.createItem("/example-schema:players[name='Eve']");
752 datastore.createItem("/example-schema:players[name='Adam']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200753 datastore.commitChanges();
754 expected = {
755 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
756 {"/example-schema:players[name='John']/name", "John"s},
757 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
758 {"/example-schema:players[name='Eve']/name", "Eve"s},
759 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
760 {"/example-schema:players[name='Adam']/name", "Adam"s},
761 };
762 REQUIRE(datastore.getItems("/example-schema:players") == expected);
763 }
764
765 std::string sourcePath;
766 SECTION("begin")
767 {
768 sourcePath = "/example-schema:players[name='Adam']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200769 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Adam']", std::nullopt, std::nullopt, ""s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200770 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
771 datastore.commitChanges();
772 expected = {
773 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
774 {"/example-schema:players[name='Adam']/name", "Adam"s},
775 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
776 {"/example-schema:players[name='John']/name", "John"s},
777 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
778 {"/example-schema:players[name='Eve']/name", "Eve"s},
779 };
780 REQUIRE(datastore.getItems("/example-schema:players") == expected);
781 }
782
783 SECTION("end")
784 {
785 sourcePath = "/example-schema:players[name='John']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200786#if defined(yang_BACKEND) || defined(netconf_BACKEND)
787 // TODO: see TODO comment in leaflist/end
788 // Although these make much less sense
789 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, ""));
790 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Adam']", std::nullopt, std::nullopt, "[name='Eve']"));
791#else
792 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='John']", std::nullopt, std::nullopt, "[name='Adam']"));
793#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200794 datastore.moveItem(sourcePath, yang::move::Absolute::End);
795 datastore.commitChanges();
796 expected = {
797 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
798 {"/example-schema:players[name='Eve']/name", "Eve"s},
799 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
800 {"/example-schema:players[name='Adam']/name", "Adam"s},
801 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
802 {"/example-schema:players[name='John']/name", "John"s},
803 };
804 REQUIRE(datastore.getItems("/example-schema:players") == expected);
805 }
806
807 SECTION("after")
808 {
809 sourcePath = "/example-schema:players[name='John']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200810#if defined(yang_BACKEND) || defined(netconf_BACKEND)
811 // TODO: see TODO comment in leaflist/end
812 // Although these make much less sense
813 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, ""));
814#else
815 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='John']", std::nullopt, std::nullopt, "[name='Eve']"));
816#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200817 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{"name", "Eve"s}}});
818 datastore.commitChanges();
819 expected = {
820 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
821 {"/example-schema:players[name='Eve']/name", "Eve"s},
822 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
823 {"/example-schema:players[name='John']/name", "John"s},
824 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
825 {"/example-schema:players[name='Adam']/name", "Adam"s},
826 };
827 REQUIRE(datastore.getItems("/example-schema:players") == expected);
828 }
829
830 SECTION("before")
831 {
832 sourcePath = "/example-schema:players[name='John']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200833#if defined(yang_BACKEND) || defined(netconf_BACKEND)
834 // TODO: see TODO comment in leaflist/end
835 // Although these make much less sense
836 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, ""));
837#else
838 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='John']", std::nullopt, std::nullopt, "[name='Eve']"));
839#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200840 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{"name", "Adam"s}}});
841 datastore.commitChanges();
842 expected = {
843 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
844 {"/example-schema:players[name='Eve']/name", "Eve"s},
845 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
846 {"/example-schema:players[name='John']/name", "John"s},
847 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
848 {"/example-schema:players[name='Adam']/name", "Adam"s},
849 };
850 REQUIRE(datastore.getItems("/example-schema:players") == expected);
851 }
852 }
853
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200854 SECTION("getting /")
855 {
856 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200857 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafInt32", std::nullopt, "64"s, std::nullopt));
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200858 datastore.setLeaf("/example-schema:leafInt32", 64);
859 datastore.commitChanges();
860 }
861
862 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200863 {"/example-schema:leafInt32", 64}
864 };
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200865 // This tests if we at least get the data WE added.
Václav Kubernát654303f2020-07-31 13:16:54 +0200866 REQUIRE(std::all_of(expected.begin(), expected.end(), [items = datastore.getItems("/")] (const auto& item) {
867 return std::find(items.begin(), items.end(), item) != items.end();
868 }));
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200869 }
870
Václav Kubernát36986c52020-06-25 10:30:05 +0200871 SECTION("setting and removing without commit")
872 {
873 datastore.setLeaf("/example-schema:leafInt32", 64);
874 datastore.deleteItem("/example-schema:leafInt32");
875 }
876
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200877 SECTION("two key lists")
878 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200879 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:point[x='12'][y='10']", std::nullopt, std::nullopt, std::nullopt));
880 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:point[x='12'][y='10']/x", std::nullopt, "12"s, std::nullopt));
881 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:point[x='12'][y='10']/y", std::nullopt, "10"s, std::nullopt));
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200882 datastore.createItem("/example-schema:point[x='12'][y='10']");
883 datastore.commitChanges();
884 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:point") != std::string::npos);
885 }
886
Václav Kubernát73109382018-09-14 19:52:03 +0200887 waitForCompletionAndBitMore(seq1);
888}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100889
Václav Kubernát654303f2020-07-31 13:16:54 +0200890struct ActionCb {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200891 sysrepo::ErrorCode operator()(
892 [[maybe_unused]] sysrepo::Session session,
893 [[maybe_unused]] uint32_t subscriptionId,
894 std::string_view xpath,
895 [[maybe_unused]] const libyang::DataNode input,
896 [[maybe_unused]] sysrepo::Event event,
897 [[maybe_unused]] uint32_t requestId,
898 libyang::DataNode output)
Václav Kubernáta8789602020-07-20 15:18:19 +0200899 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200900 if (session.getContext().findPath(xpath.data()).path() == "/example-schema:ports/shutdown") {
901 // `xpath` holds the subscription xpath which won't have list keys. We need the path with list keys and
902 // we'll find that in the input.
903 auto inputPath = input.findXPath("/example-schema:ports/shutdown").front().path();
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100904 output.newPath(joinPaths(inputPath, "success"), "true", libyang::CreationOptions::Output);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200905 return sysrepo::ErrorCode::Ok;
Václav Kubernáta8789602020-07-20 15:18:19 +0200906 }
907 throw std::runtime_error("unrecognized RPC");
908 }
Václav Kubernát654303f2020-07-31 13:16:54 +0200909};
Václav Kubernáta8789602020-07-20 15:18:19 +0200910
Václav Kubernát654303f2020-07-31 13:16:54 +0200911struct RpcCb {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200912 sysrepo::ErrorCode operator()(
913 [[maybe_unused]] sysrepo::Session session,
914 [[maybe_unused]] uint32_t subscriptionId,
915 std::string_view xpath,
916 const libyang::DataNode input,
917 [[maybe_unused]] sysrepo::Event event,
918 [[maybe_unused]] uint32_t requestId,
919 libyang::DataNode output)
Jan Kundrát6ee84792020-01-24 01:43:36 +0100920 {
921 const auto nukes = "/example-schema:launch-nukes"s;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200922 if (xpath == "/example-schema:noop"s || xpath == "/example-schema:fire"s) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200923 return sysrepo::ErrorCode::Ok;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200924 }
925
926 if (xpath == nukes) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100927 uint64_t kilotons = 0;
928 bool hasCities = false;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200929 for (const auto& inputNode : input.childrenDfs()) {
930 if (inputNode.path() == nukes) {
931 continue; // ignore, top-level RPC
932 }
933 if (inputNode.path() == nukes + "/payload") {
Václav Kubernátf4b6a932020-07-09 10:34:19 +0200934 continue; // ignore, container
935 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200936 if (inputNode.path() == nukes + "/description") {
Václav Kubernátf4b6a932020-07-09 10:34:19 +0200937 continue; // unused
938 }
939
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200940 if (inputNode.path() == nukes + "/payload/kilotons") {
941 kilotons = std::get<uint64_t>(inputNode.asTerm().value());
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100942 } else if (inputNode.path().find(nukes + "/cities") == 0) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100943 hasCities = true;
944 } else {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100945 throw std::runtime_error("RPC launch-nukes: unexpected input "s + inputNode.path());
Jan Kundrát6ee84792020-01-24 01:43:36 +0100946 }
947 }
948 if (kilotons == 333'666) {
949 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200950 return sysrepo::ErrorCode::Ok;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100951 }
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100952 output.newPath(nukes + "/blast-radius", "33666", libyang::CreationOptions::Output);
953 output.newPath(nukes + "/actual-yield", std::to_string(static_cast<uint64_t>(1.33 * kilotons)), libyang::CreationOptions::Output);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100954 if (hasCities) {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100955 output.newPath(nukes + "/damaged-places/targets[city='London']/city", "London", libyang::CreationOptions::Output);
956 output.newPath(nukes + "/damaged-places/targets[city='Berlin']/city", "Berlin", libyang::CreationOptions::Output);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100957 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200958 return sysrepo::ErrorCode::Ok;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100959 }
960 throw std::runtime_error("unrecognized RPC");
961 }
962};
963
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100964TEST_CASE("rpc/action")
965{
Jan Kundrát6ee84792020-01-24 01:43:36 +0100966 trompeloeil::sequence seq1;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100967
968#ifdef sysrepo_BACKEND
Václav Kubernátf5d75152020-12-03 03:52:34 +0100969 auto datastore = std::make_shared<SysrepoAccess>();
Jan Kundrát6ee84792020-01-24 01:43:36 +0100970#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200971 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
972 auto datastore = std::make_shared<NetconfAccess>(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200973#elif defined(yang_BACKEND)
Václav Kubernáte7248b22020-06-26 15:38:59 +0200974 auto datastore = std::make_shared<YangAccess>();
975 datastore->addSchemaDir(schemaDir);
976 datastore->addSchemaFile(exampleSchemaFile);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100977#else
978#error "Unknown backend"
979#endif
980
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200981 sysrepo::setLogLevelStderr(sysrepo::LogLevel::Information);
982
983 auto srSubscription = sysrepo::Connection{}.sessionStart().onRPCAction("/example-schema:noop", RpcCb{});
984 srSubscription.onRPCAction("/example-schema:launch-nukes", RpcCb{});
985 srSubscription.onRPCAction("/example-schema:fire", RpcCb{});
986 srSubscription.onRPCAction("/example-schema:ports/shutdown", ActionCb{});
987
Václav Kubernát654303f2020-07-31 13:16:54 +0200988 SysrepoSubscription subscription("example-schema", nullptr);
Václav Kubernáte7248b22020-06-26 15:38:59 +0200989
Václav Kubernáta8789602020-07-20 15:18:19 +0200990 SECTION("rpc")
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200991 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200992 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
993 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
994 };
995 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100996
Václav Kubernáta8789602020-07-20 15:18:19 +0200997 // ProxyDatastore cannot easily read DatastoreAccess::Tree, so we need to set the input via create/setLeaf/etc.
998 SECTION("valid")
999 {
1000 std::string rpc;
1001 DatastoreAccess::Tree input, output;
1002
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001003 SECTION("noop")
1004 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001005 rpc = "/example-schema:noop";
Václav Kubernátb3960f82020-12-01 03:21:48 +01001006 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001007 }
1008
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001009 SECTION("small nuke")
1010 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001011 rpc = "/example-schema:launch-nukes";
1012 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001013 {joinPaths(rpc, "description"), "dummy"s},
1014 {joinPaths(rpc, "payload/kilotons"), uint64_t{333'666}},
Václav Kubernáta8789602020-07-20 15:18:19 +02001015 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001016 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001017 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{333'666});
1018 // no data are returned
1019 }
1020
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001021 SECTION("small nuke")
1022 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001023 rpc = "/example-schema:launch-nukes";
1024 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001025 {joinPaths(rpc, "description"), "dummy"s},
1026 {joinPaths(rpc, "payload/kilotons"), uint64_t{4}},
Václav Kubernáta8789602020-07-20 15:18:19 +02001027 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001028 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001029 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{4});
1030
1031 output = {
1032 {"blast-radius", uint32_t{33'666}},
1033 {"actual-yield", uint64_t{5}},
1034 };
1035 }
1036
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001037 SECTION("with lists")
1038 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001039 rpc = "/example-schema:launch-nukes";
1040 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001041 {joinPaths(rpc, "payload/kilotons"), uint64_t{6}},
1042 {joinPaths(rpc, "cities/targets[city='Prague']/city"), "Prague"s},
Václav Kubernáta8789602020-07-20 15:18:19 +02001043 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001044 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001045 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{6});
1046 proxyDatastore.createItem("/example-schema:launch-nukes/example-schema:cities/example-schema:targets[city='Prague']");
1047 output = {
1048 {"blast-radius", uint32_t{33'666}},
1049 {"actual-yield", uint64_t{7}},
1050 {"damaged-places", special_{SpecialValue::PresenceContainer}},
1051 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
1052 {"damaged-places/targets[city='London']/city", "London"s},
1053 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
1054 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
1055 };
1056 }
1057
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001058 SECTION("with leafref")
1059 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001060 datastore->createItem("/example-schema:person[name='Colton']");
1061 datastore->commitChanges();
1062
1063 rpc = "/example-schema:fire";
1064 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001065 {joinPaths(rpc, "whom"), "Colton"s}
Václav Kubernáta8789602020-07-20 15:18:19 +02001066 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001067 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001068 proxyDatastore.setLeaf("/example-schema:fire/example-schema:whom", "Colton"s);
1069 }
1070
Václav Kubernátb3960f82020-12-01 03:21:48 +01001071 catching<OnExec>([&] { REQUIRE(datastore->execute(rpc, input) == output); });
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001072 catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001073 }
1074
Václav Kubernáta8789602020-07-20 15:18:19 +02001075 SECTION("non-existing RPC")
1076 {
Václav Kubernátb3960f82020-12-01 03:21:48 +01001077 catching<OnInvalidRpcPath>([&] { datastore->execute("/example-schema:non-existing", DatastoreAccess::Tree{}); });
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001078 }
Václav Kubernát2edfe542021-02-03 08:08:29 +01001079
1080 SECTION("invalid RPC exec resets temporary datastore")
1081 {
1082 proxyDatastore.initiate("/example-schema:setIp");
1083 catching<OnInvalidRpcInput>([&] { auto output = proxyDatastore.execute(); });
1084 REQUIRE(proxyDatastore.inputDatastorePath() == std::nullopt);
1085 }
Jan Kundrát6ee84792020-01-24 01:43:36 +01001086 }
1087
Václav Kubernáta8789602020-07-20 15:18:19 +02001088 SECTION("action")
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001089 {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001090 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
1091 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
1092 };
1093 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Václav Kubernáta8789602020-07-20 15:18:19 +02001094 std::string path;
1095 DatastoreAccess::Tree input, output;
1096
1097 output = {
Václav Kubernáta8789602020-07-20 15:18:19 +02001098 {"success", true}
1099 };
1100 datastore->createItem("/example-schema:ports[name='A']");
1101 datastore->commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001102 SECTION("shutdown")
1103 {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001104 path = "/example-schema:ports[name='A']/example-schema:shutdown";
1105 input = {
1106 {"/example-schema:ports[name='A']/shutdown/force", true}
1107 };
1108 proxyDatastore.initiate(path);
1109 proxyDatastore.setLeaf("/example-schema:ports[name='A']/example-schema:shutdown/example-schema:force", true);
1110
Václav Kubernáta8789602020-07-20 15:18:19 +02001111 }
1112
Václav Kubernátb3960f82020-12-01 03:21:48 +01001113 catching<OnExec>([&] { REQUIRE(datastore->execute(path, input) == output); });
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001114 catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
Jan Kundrát6ee84792020-01-24 01:43:36 +01001115 }
1116
Václav Kubernáted4e3782022-03-02 23:57:33 +01001117 SECTION("parsed info in yang context")
1118 {
1119 auto schema = datastore->schema();
1120 auto leafTypeName = schema->leafTypeName("/example-schema:typedefedLeaf");
1121
1122#if defined(sysrepo_BACKEND)
1123 // Sysrepo is not available yet, with libyang parsed info context
1124 REQUIRE(leafTypeName == std::nullopt);
1125#else
1126 REQUIRE(leafTypeName == "myType");
1127#endif
1128 }
1129
Jan Kundrát6ee84792020-01-24 01:43:36 +01001130 waitForCompletionAndBitMore(seq1);
1131}
Václav Kubernátf5d75152020-12-03 03:52:34 +01001132
1133#if not defined(yang_BACKEND)
1134TEST_CASE("datastore targets")
1135{
1136 const auto testNode = "/example-schema:leafInt32";
1137 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +02001138 auto sess = sysrepo::Connection{}.sessionStart();
1139 sess.deleteItem(testNode);
1140 sess.applyChanges(std::chrono::milliseconds{1000});
1141 sess.switchDatastore(sysrepo::Datastore::Startup);
1142 sess.deleteItem(testNode);
1143 sess.applyChanges(std::chrono::milliseconds{1000});
Václav Kubernátf5d75152020-12-03 03:52:34 +01001144 }
1145 MockRecorder mockRunning;
1146 MockRecorder mockStartup;
1147
1148#ifdef sysrepo_BACKEND
1149 SysrepoAccess datastore;
1150#elif defined(netconf_BACKEND)
1151 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
1152 NetconfAccess datastore(NETOPEER_SOCKET);
1153#else
1154#error "Unknown backend"
1155#endif
1156
1157 auto testGetItems = [&datastore] (const auto& path, const DatastoreAccess::Tree& expected) {
1158 REQUIRE(datastore.getItems(path) == expected);
1159 };
1160
1161 SECTION("subscriptions change operational target")
1162 {
1163 // Default target is operational, so setting a value and reading it should return no values as there are no
1164 // subscriptions yet.
1165 datastore.setLeaf(testNode, 10);
1166 datastore.commitChanges();
1167 testGetItems(testNode, {});
1168
1169 // Now we create a subscription and try again.
1170 SysrepoSubscription subRunning("example-schema", &mockRunning);
1171 testGetItems(testNode, {{testNode, 10}});
1172 }
1173
1174 SECTION("running shows stuff even without subscriptions")
1175 {
1176 datastore.setTarget(DatastoreTarget::Running);
1177 datastore.setLeaf(testNode, 10);
1178 datastore.commitChanges();
1179 testGetItems(testNode, {{testNode, 10}});
1180
1181 // Actually creating a subscription shouldn't make a difference.
1182 SysrepoSubscription subRunning("example-schema", &mockRunning);
1183 testGetItems(testNode, {{testNode, 10}});
1184 }
1185
1186 SECTION("startup changes only affect startup")
1187 {
1188 datastore.setTarget(DatastoreTarget::Startup);
1189 datastore.setLeaf(testNode, 10);
1190 datastore.commitChanges();
1191 testGetItems(testNode, {{testNode, 10}});
1192 datastore.setTarget(DatastoreTarget::Running);
1193 testGetItems(testNode, {});
1194 datastore.setTarget(DatastoreTarget::Operational);
1195 testGetItems(testNode, {});
1196 }
1197
1198}
1199#endif