blob: b7c4944f3e786fcbbb95aeb43b19ca87e0db603e [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;
Jan Kundrátbb7aa852023-08-30 11:51:43 +020025using OnInvalidRequireInstance = DatastoreException;
Václav Kubernátc31bd602019-03-07 11:44:48 +010026#elif defined(netconf_BACKEND)
Jan Kundrát7ec214d2020-06-19 17:05:07 +020027using OnInvalidSchemaPathCreate = std::runtime_error;
28using OnInvalidSchemaPathDelete = std::runtime_error;
29using OnInvalidSchemaPathMove = std::runtime_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020030using OnInvalidRpcPath = std::runtime_error;
Václav Kubernát2edfe542021-02-03 08:08:29 +010031using OnInvalidRpcInput = std::runtime_error;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020032using OnKeyNotFound = std::runtime_error;
Václav Kubernáta8789602020-07-20 15:18:19 +020033using OnExec = void;
Jan Kundrátbb7aa852023-08-30 11:51:43 +020034using OnInvalidRequireInstance = std::runtime_error;
Václav Kubernátc31bd602019-03-07 11:44:48 +010035#include "netconf_access.hpp"
Václav Kubernát74487df2020-06-04 01:29:28 +020036#elif defined(yang_BACKEND)
37#include <fstream>
38#include "yang_access.hpp"
39#include "yang_access_test_vars.hpp"
40using OnInvalidSchemaPathCreate = DatastoreException;
41using OnInvalidSchemaPathDelete = DatastoreException;
42using OnInvalidSchemaPathMove = DatastoreException;
43using OnInvalidRpcPath = DatastoreException;
Václav Kubernát2edfe542021-02-03 08:08:29 +010044using OnInvalidRpcInput = std::logic_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020045using OnKeyNotFound = DatastoreException;
Václav Kubernáta8789602020-07-20 15:18:19 +020046using OnExec = std::logic_error;
Jan Kundrátbb7aa852023-08-30 11:51:43 +020047using OnInvalidRequireInstance = std::runtime_error;
Václav Kubernátc31bd602019-03-07 11:44:48 +010048#else
49#error "Unknown backend"
50#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +010051#include "pretty_printers.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020052#include "sysrepo_subscription.hpp"
Václav Kubernát8e121ff2019-10-15 15:47:45 +020053#include "utils.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020054
Jan Kundrát6ee84792020-01-24 01:43:36 +010055using namespace std::literals::string_literals;
56
Václav Kubernát69aabe92020-01-24 16:53:12 +010057class MockRecorder : public trompeloeil::mock_interface<Recorder> {
Václav Kubernát73109382018-09-14 19:52:03 +020058public:
Václav Kubernátcfdb9222021-07-07 22:36:24 +020059 IMPLEMENT_MOCK5(write);
Václav Kubernát73109382018-09-14 19:52:03 +020060};
61
Jan Kundrátbb525b42020-02-04 11:56:59 +010062class MockDataSupplier : public trompeloeil::mock_interface<DataSupplier> {
63public:
64 IMPLEMENT_CONST_MOCK1(get_data);
65};
66
Jan Kundrát7ec214d2020-06-19 17:05:07 +020067namespace {
68template <class ...> constexpr std::false_type always_false [[maybe_unused]] {};
69template <class Exception, typename Callable> void catching(const Callable& what) {
70
71 if constexpr (std::is_same_v<Exception, void>) {
Jan Kundrát3867c9e2020-06-18 20:26:45 +020072 what();
Jan Kundrát7ec214d2020-06-19 17:05:07 +020073 } else if constexpr (std::is_same<Exception, std::runtime_error>()) {
74 // cannot use REQUIRE_THROWS_AS(..., Exception) directly because that one
75 // needs an extra `typename` deep in the bowels of doctest
76 REQUIRE_THROWS_AS(what(), std::runtime_error);
Václav Kubernát74487df2020-06-04 01:29:28 +020077 } else if constexpr (std::is_same<Exception, std::logic_error>()) {
78 REQUIRE_THROWS_AS(what(), std::logic_error);
Jan Kundrát7ec214d2020-06-19 17:05:07 +020079 } else if constexpr (std::is_same<Exception, DatastoreException>()) {
80 REQUIRE_THROWS_AS(what(), DatastoreException);
Václav Kubernátcfdb9222021-07-07 22:36:24 +020081 } else if constexpr (std::is_same<Exception, sysrepo::ErrorWithCode>()) {
82 REQUIRE_THROWS_AS(what(), sysrepo::ErrorWithCode);
Jan Kundrát7ec214d2020-06-19 17:05:07 +020083 } else {
84 static_assert(always_false<Exception>); // https://stackoverflow.com/a/53945549/2245623
Jan Kundrát3867c9e2020-06-18 20:26:45 +020085 }
86}
Jan Kundrát7ec214d2020-06-19 17:05:07 +020087}
Jan Kundrát3867c9e2020-06-18 20:26:45 +020088
Václav Kubernát74487df2020-06-04 01:29:28 +020089#if defined(yang_BACKEND)
90class TestYangAccess : public YangAccess {
91public:
92 void commitChanges() override
93 {
94 YangAccess::commitChanges();
95 dumpToSysrepo();
96 }
97
98 void copyConfig(const Datastore source, const Datastore destination) override
99 {
100 YangAccess::copyConfig(source, destination);
101 dumpToSysrepo();
102 }
103
104private:
105 void dumpToSysrepo()
106 {
107 {
108 std::ofstream of(testConfigFile);
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200109 of << dump(DataFormat::Xml);
Václav Kubernát74487df2020-06-04 01:29:28 +0200110 }
Jan Kundrát86840ec2021-01-27 15:38:08 +0100111 auto command = std::string(sysrepocfgExecutable) + " --import=" + testConfigFile + " --format=xml --datastore=running --module=example-schema";
Jan Kundrát685c60a2023-08-28 19:13:27 +0200112 if (std::system(command.c_str())) {
113 throw std::runtime_error{"sysrepocfg import failed"};
114 }
Václav Kubernát74487df2020-06-04 01:29:28 +0200115 }
116};
117#endif
118
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200119TEST_CASE("setting/getting values")
Václav Kubernát73109382018-09-14 19:52:03 +0200120{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200121 sysrepo::setLogLevelStderr(sysrepo::LogLevel::Information);
Václav Kubernát73109382018-09-14 19:52:03 +0200122 trompeloeil::sequence seq1;
Václav Kubernáteaf56682021-02-22 17:15:41 +0100123 MockRecorder mockRunning;
124 MockRecorder mockStartup;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200125
126 sysrepo::Connection{}.sessionStart().copyConfig(sysrepo::Datastore::Startup, "example-schema", std::chrono::milliseconds(1000));
127
Václav Kubernáteaf56682021-02-22 17:15:41 +0100128 SysrepoSubscription subRunning("example-schema", &mockRunning);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200129 SysrepoSubscription subStartup("example-schema", &mockStartup, sysrepo::Datastore::Startup);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100130
131#ifdef sysrepo_BACKEND
Václav Kubernátf5d75152020-12-03 03:52:34 +0100132 SysrepoAccess datastore;
Václav Kubernátc31bd602019-03-07 11:44:48 +0100133#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200134 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
135 NetconfAccess datastore(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200136#elif defined(yang_BACKEND)
137 TestYangAccess datastore;
138 datastore.addSchemaDir(schemaDir);
139 datastore.addSchemaFile(exampleSchemaFile);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100140#else
141#error "Unknown backend"
142#endif
Václav Kubernát73109382018-09-14 19:52:03 +0200143
Václav Kubernát69aabe92020-01-24 16:53:12 +0100144
Václav Kubernát134d78f2019-09-03 16:42:29 +0200145 SECTION("set leafInt8 to -128")
Václav Kubernát73109382018-09-14 19:52:03 +0200146 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200147 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 +0200148 datastore.setLeaf("/example-schema:leafInt8", int8_t{-128});
149 datastore.commitChanges();
150 }
151
152 SECTION("set leafInt16 to -32768")
153 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200154 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 +0200155 datastore.setLeaf("/example-schema:leafInt16", int16_t{-32768});
156 datastore.commitChanges();
157 }
158
159 SECTION("set leafInt32 to -2147483648")
160 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200161 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 +0200162 datastore.setLeaf("/example-schema:leafInt32", int32_t{-2147483648});
163 datastore.commitChanges();
164 }
165
166 SECTION("set leafInt64 to -50000000000")
167 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200168 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 +0200169 datastore.setLeaf("/example-schema:leafInt64", int64_t{-50000000000});
170 datastore.commitChanges();
171 }
172
173 SECTION("set leafUInt8 to 255")
174 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200175 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 +0200176 datastore.setLeaf("/example-schema:leafUInt8", uint8_t{255});
177 datastore.commitChanges();
178 }
179
180 SECTION("set leafUInt16 to 65535")
181 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200182 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 +0200183 datastore.setLeaf("/example-schema:leafUInt16", uint16_t{65535});
184 datastore.commitChanges();
185 }
186
187 SECTION("set leafUInt32 to 4294967295")
188 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200189 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 +0200190 datastore.setLeaf("/example-schema:leafUInt32", uint32_t{4294967295});
191 datastore.commitChanges();
192 }
193
194 SECTION("set leafUInt64 to 50000000000")
195 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200196 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 +0200197 datastore.setLeaf("/example-schema:leafUInt64", uint64_t{50000000000});
Václav Kubernát73109382018-09-14 19:52:03 +0200198 datastore.commitChanges();
199 }
200
201 SECTION("set leafEnum to coze")
202 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200203 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 +0200204 datastore.setLeaf("/example-schema:leafEnum", enum_{"coze"});
205 datastore.commitChanges();
206 }
207
208 SECTION("set leafDecimal to 123.544")
209 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200210 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 +0200211 datastore.setLeaf("/example-schema:leafDecimal", 123.544);
212 datastore.commitChanges();
213 }
214
Jan Kundrátd2872862020-06-18 21:02:00 +0200215 SECTION("set a string, then delete it")
216 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200217 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:leafString", std::nullopt, "blah"s, std::nullopt));
Jan Kundrátd2872862020-06-18 21:02:00 +0200218 datastore.setLeaf("/example-schema:leafString", "blah"s);
219 datastore.commitChanges();
220 DatastoreAccess::Tree expected{{"/example-schema:leafString", "blah"s}};
221 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
222
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200223 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:leafString", "blah"s, std::nullopt, std::nullopt));
Jan Kundrátd2872862020-06-18 21:02:00 +0200224 datastore.deleteItem("/example-schema:leafString");
225 datastore.commitChanges();
226 expected.clear();
227 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
228 }
229
Václav Kubernát8e756642021-11-05 00:04:54 +0100230 SECTION("set a string, then set it to something else without commiting")
231 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200232 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 +0100233 datastore.setLeaf("/example-schema:leafString", "blah"s);
234 datastore.setLeaf("/example-schema:leafString", "oops"s);
235 datastore.commitChanges();
236 DatastoreAccess::Tree expected{{"/example-schema:leafString", "oops"s}};
237 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
238
239 }
240
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200241 SECTION("set a non-existing leaf")
242 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100243 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200244 datastore.setLeaf("/example-schema:non-existing", "what"s);
245 });
246 }
247
Václav Kubernát73109382018-09-14 19:52:03 +0200248 SECTION("create presence container")
249 {
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200250 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") == std::string::npos);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200251 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:pContainer", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200252 datastore.createItem("/example-schema:pContainer");
Václav Kubernát73109382018-09-14 19:52:03 +0200253 datastore.commitChanges();
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200254 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") != std::string::npos);
Václav Kubernát73109382018-09-14 19:52:03 +0200255 }
256
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100257 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200258 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100259 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200260 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Nguyen']", std::nullopt, std::nullopt, std::nullopt));
261 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 +0200262 datastore.createItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100263 datastore.commitChanges();
264 }
265 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200266 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:person[name='Nguyen']", std::nullopt, std::nullopt, std::nullopt));
267 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 +0200268 datastore.deleteItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100269 datastore.commitChanges();
270 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200271 }
272
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200273 SECTION("deleting non-existing list keys")
274 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100275 catching<OnKeyNotFound>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200276 datastore.deleteItem("/example-schema:person[name='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200277 datastore.commitChanges();
278 });
279 }
280
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200281 SECTION("accessing non-existing schema nodes as a list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200282 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100283 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200284 datastore.createItem("/example-schema:non-existing-list[xxx='blah']");
285 datastore.commitChanges();
286 });
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100287 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200288 datastore.deleteItem("/example-schema:non-existing-list[xxx='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200289 datastore.commitChanges();
290 });
291 }
292
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200293 SECTION("leafref pointing to a key of a list")
294 {
295 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200296 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Dan']", std::nullopt, std::nullopt, std::nullopt));
297 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s, std::nullopt));
298 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Elfi']", std::nullopt, std::nullopt, std::nullopt));
299 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s, std::nullopt));
300 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Kolafa']", std::nullopt, std::nullopt, std::nullopt));
301 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 +0200302 datastore.createItem("/example-schema:person[name='Dan']");
303 datastore.createItem("/example-schema:person[name='Elfi']");
304 datastore.createItem("/example-schema:person[name='Kolafa']");
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200305 datastore.commitChanges();
306 }
307
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200308 std::string value;
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200309 SECTION("Dan")
310 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200311 value = "Dan";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200312 }
313
314 SECTION("Elfi")
315 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200316 value = "Elfi";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200317 }
318
319 SECTION("Kolafa")
320 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200321 value = "Kolafa";
322 }
323
324 datastore.setLeaf("/example-schema:bossPerson", value);
325 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200326 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:bossPerson", std::nullopt, value, std::nullopt));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200327 datastore.commitChanges();
328 }
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200329 REQUIRE(datastore.getItems("/example-schema:bossPerson") == DatastoreAccess::Tree{{"/example-schema:bossPerson", value}});
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200330 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200331
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200332 SECTION("bool values get correctly represented as bools")
333 {
334 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200335 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 +0200336 datastore.setLeaf("/example-schema:down", bool{true});
337 datastore.commitChanges();
338 }
339
Jan Kundrátb331b552020-01-23 15:25:29 +0100340 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200341 REQUIRE(datastore.getItems("/example-schema:down") == expected);
342 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200343
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200344 SECTION("getting items from the whole module")
345 {
346 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200347 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:up", std::nullopt, "true"s, std::nullopt));
348 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 +0200349 datastore.setLeaf("/example-schema:up", bool{true});
350 datastore.setLeaf("/example-schema:down", bool{false});
351 datastore.commitChanges();
352 }
353
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200354 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200355 {"/example-schema:up", bool{true}},
356 {"/example-schema:down", bool{false}}
357 };
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200358 REQUIRE(datastore.getItems("/example-schema:*") == expected);
359 }
360
Václav Kubernát152ce222019-12-19 12:23:32 +0100361 SECTION("getItems returns correct datatypes")
362 {
363 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200364 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 +0100365 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
366 datastore.commitChanges();
367 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100368 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100369
370 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
371 }
372
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100373 SECTION("getItems on a list")
374 {
375 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200376 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Jan']", std::nullopt, std::nullopt, std::nullopt));
377 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s, std::nullopt));
378 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Michal']", std::nullopt, std::nullopt, std::nullopt));
379 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s, std::nullopt));
380 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:person[name='Petr']", std::nullopt, std::nullopt, std::nullopt));
381 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 +0200382 datastore.createItem("/example-schema:person[name='Jan']");
383 datastore.createItem("/example-schema:person[name='Michal']");
384 datastore.createItem("/example-schema:person[name='Petr']");
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100385 datastore.commitChanges();
386 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100387 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100388 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100389 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100390 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100391 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100392 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100393 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
394 };
395
396 REQUIRE(datastore.getItems("/example-schema:person") == expected);
397 }
398
Václav Kubernát69aabe92020-01-24 16:53:12 +0100399 SECTION("presence containers")
400 {
401 DatastoreAccess::Tree expected;
402 // Make sure it's not there before we create it
403 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
404
405 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200406 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:pContainer", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200407 datastore.createItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100408 datastore.commitChanges();
409 }
410 expected = {
411 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
412 };
413 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
414
415 // Make sure it's not there after we delete it
416 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200417 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Deleted, "/example-schema:pContainer", std::nullopt, std::nullopt, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200418 datastore.deleteItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100419 datastore.commitChanges();
420 }
421 expected = {};
422 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100423 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100424
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200425 SECTION("creating a non-existing schema node as a container")
426 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100427 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200428 datastore.createItem("/example-schema:non-existing-presence-container");
429 datastore.commitChanges();
430 });
431 }
432
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200433 SECTION("deleting a non-existing schema node as a container or leaf")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200434 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100435 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200436 datastore.deleteItem("/example-schema:non-existing-presence-container");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200437 datastore.commitChanges();
438 });
439 }
440
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100441 SECTION("nested presence container")
442 {
443 DatastoreAccess::Tree expected;
444 // Make sure it's not there before we create it
445 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
446 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200447 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 +0200448 datastore.createItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100449 datastore.commitChanges();
450 }
451 expected = {
452 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
453 };
454 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
455 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200456 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 +0200457 datastore.deleteItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100458 datastore.commitChanges();
459 }
460 expected = {};
461 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100462 }
463
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100464 SECTION("floats")
465 {
466 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200467 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 +0100468 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100469 DatastoreAccess::Tree expected{
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100470 {"/example-schema:leafDecimal", 123.4},
471 };
472 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
473 }
474
Jan Kundrát3ff50122020-05-07 00:37:50 +0200475 SECTION("unions")
476 {
477 datastore.setLeaf("/example-schema:unionIntString", int32_t{10});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200478 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:unionIntString", std::nullopt, "10"s, std::nullopt));
Jan Kundrát3ff50122020-05-07 00:37:50 +0200479 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100480 DatastoreAccess::Tree expected{
Jan Kundrát3ff50122020-05-07 00:37:50 +0200481 {"/example-schema:unionIntString", int32_t{10}},
482 };
483 REQUIRE(datastore.getItems("/example-schema:unionIntString") == expected);
484 }
485
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100486 SECTION("identityref")
487 {
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200488 datastore.setLeaf("/example-schema:beast", identityRef_{"example-schema", "Mammal"});
Jan Kundrátdfcc4852023-06-05 18:26:36 +0200489 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:beast", std::nullopt, "Mammal"s, std::nullopt));
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200490 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100491 DatastoreAccess::Tree expected{
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200492 {"/example-schema:beast", identityRef_{"example-schema", "Mammal"}},
493 };
494 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
495
496 datastore.setLeaf("/example-schema:beast", identityRef_{"Whale"});
Jan Kundrátdfcc4852023-06-05 18:26:36 +0200497 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Modified, "/example-schema:beast", "Mammal", "Whale"s, std::nullopt));
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200498 datastore.commitChanges();
499 expected = {
500 {"/example-schema:beast", identityRef_{"example-schema", "Whale"}},
501 };
502 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
503 }
504
Jan Kundrát68985442020-05-07 02:15:34 +0200505 SECTION("binary")
506 {
507 datastore.setLeaf("/example-schema:blob", binary_{"cHduegByIQ=="s});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200508 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:blob", std::nullopt, "cHduegByIQ=="s, std::nullopt));
Jan Kundrát68985442020-05-07 02:15:34 +0200509 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100510 DatastoreAccess::Tree expected{
Jan Kundrát68985442020-05-07 02:15:34 +0200511 {"/example-schema:blob", binary_{"cHduegByIQ=="s}},
512 };
513 REQUIRE(datastore.getItems("/example-schema:blob") == expected);
514 }
515
Jan Kundrát379bb572020-05-07 03:23:13 +0200516 SECTION("empty")
517 {
518 datastore.setLeaf("/example-schema:dummy", empty_{});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200519 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:dummy", std::nullopt, ""s, std::nullopt));
Jan Kundrát379bb572020-05-07 03:23:13 +0200520 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100521 DatastoreAccess::Tree expected{
Jan Kundrát379bb572020-05-07 03:23:13 +0200522 {"/example-schema:dummy", empty_{}},
523 };
524 REQUIRE(datastore.getItems("/example-schema:dummy") == expected);
525 }
526
Václav Kubernát19097f32020-10-05 10:08:29 +0200527 SECTION("bits")
528 {
529 datastore.setLeaf("/example-schema:flags", bits_{{"sign", "carry"}});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200530 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 +0200531 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100532 DatastoreAccess::Tree expected{
Václav Kubernát19097f32020-10-05 10:08:29 +0200533 {"/example-schema:flags", bits_{{"carry", "sign"}}},
534 };
535 REQUIRE(datastore.getItems("/example-schema:flags") == expected);
536 }
537
Jan Kundrátbb7aa852023-08-30 11:51:43 +0200538 SECTION("instance-identifier")
539 {
540 SECTION("simple")
541 {
542 datastore.setLeaf("/example-schema:lol/up", true);
543 datastore.setLeaf("/example-schema:iid-valid", instanceIdentifier_{"/example-schema:lol/up"});
544 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:lol/up", std::nullopt, "true"s, std::nullopt));
545 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:iid-valid", std::nullopt, "/example-schema:lol/up"s, std::nullopt));
546 datastore.commitChanges();
547 DatastoreAccess::Tree expected{
548 {"/example-schema:iid-valid", instanceIdentifier_{"/example-schema:lol/up"}},
549 };
550 REQUIRE(datastore.getItems("/example-schema:iid-valid") == expected);
551 }
552
553 SECTION("to a non-existing list without require-instance")
554 {
555 datastore.setLeaf("/example-schema:iid-relaxed", instanceIdentifier_{"/example-schema:ports[name='A']/shutdown"});
556 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:iid-relaxed", std::nullopt, "/example-schema:ports[name='A']/shutdown"s, std::nullopt));
557 datastore.commitChanges();
558 DatastoreAccess::Tree expected{
559 {"/example-schema:iid-relaxed", instanceIdentifier_{"/example-schema:ports[name='A']/shutdown"}},
560 };
561 REQUIRE(datastore.getItems("/example-schema:iid-relaxed") == expected);
562 }
563
564 SECTION("to a non-existing list with require-instance")
565 {
566 catching<OnInvalidRequireInstance>([&] {
567 datastore.setLeaf("/example-schema:iid-valid", instanceIdentifier_{"/example-schema:ports[name='A']/shutdown"});
568 datastore.commitChanges();
569 });
570 datastore.discardChanges();
571 }
572 }
573
574
Václav Kubernát74487df2020-06-04 01:29:28 +0200575#if not defined(yang_BACKEND)
Jan Kundrátbb525b42020-02-04 11:56:59 +0100576 SECTION("operational data")
577 {
578 MockDataSupplier mockOpsData;
Václav Kubernát654303f2020-07-31 13:16:54 +0200579 OperationalDataSubscription opsDataSub("example-schema", "/example-schema:temperature", mockOpsData);
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100580 OperationalDataSubscription opsDataSub2("example-schema", "/example-schema:users", mockOpsData);
Jan Kundrátbb525b42020-02-04 11:56:59 +0100581 DatastoreAccess::Tree expected;
582 std::string xpath;
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100583
Jan Kundrátbb525b42020-02-04 11:56:59 +0100584 SECTION("temperature")
585 {
586 expected = {{"/example-schema:temperature", int32_t{22}}};
587 xpath = "/example-schema:temperature";
588 }
589
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100590 SECTION("key-less lists")
591 {
592 expected = {
593 {"/example-schema:users/userList[1]", special_{SpecialValue::List}},
594 {"/example-schema:users/userList[1]/name", std::string{"John"}},
595 {"/example-schema:users/userList[1]/otherfield", std::string{"LOL"}},
596 {"/example-schema:users/userList[2]", special_{SpecialValue::List}},
597 {"/example-schema:users/userList[2]/name", std::string{"Foo"}},
598 {"/example-schema:users/userList[2]/otherfield", std::string{"Bar"}},
599 };
600 xpath = "/example-schema:users";
601 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100602 REQUIRE_CALL(mockOpsData, get_data(xpath)).RETURN(expected);
603 REQUIRE(datastore.getItems(xpath) == expected);
604 }
Václav Kubernát74487df2020-06-04 01:29:28 +0200605#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +0100606
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200607 SECTION("leaf list")
608 {
609 DatastoreAccess::Tree expected;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200610 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:addresses[.='0.0.0.0']", std::nullopt, "0.0.0.0"s, std::nullopt));
611 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 +0200612 datastore.createItem("/example-schema:addresses[.='0.0.0.0']");
613 datastore.createItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200614 datastore.commitChanges();
615 expected = {
616 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
617 {"/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s},
618 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
619 };
620 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
621
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200622 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 +0200623 datastore.deleteItem("/example-schema:addresses[.='0.0.0.0']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200624 datastore.commitChanges();
625 expected = {
626 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
627 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
628 };
629 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
630
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200631 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 +0200632 datastore.deleteItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200633 datastore.commitChanges();
634 expected = {};
635 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
636 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100637
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200638 SECTION("deleting a non-existing leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200639 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100640 catching<OnKeyNotFound>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200641 datastore.deleteItem("/example-schema:addresses[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200642 datastore.commitChanges();
643 });
644 }
645
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200646 SECTION("accessing a non-existing schema node as a leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200647 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100648 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200649 datastore.createItem("/example-schema:non-existing[.='non-existing']");
650 datastore.commitChanges();
651 });
652
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100653 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200654 datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200655 datastore.commitChanges();
656 });
657 }
658
Václav Kubernát7160a132020-04-03 02:11:01 +0200659 SECTION("copying data from startup refreshes the data")
660 {
661 {
662 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200663 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 +0200664 datastore.setLeaf("/example-schema:leafInt16", int16_t{123});
665 datastore.commitChanges();
666 }
667 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{{"/example-schema:leafInt16", int16_t{123}}});
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200668 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 +0200669 datastore.copyConfig(Datastore::Startup, Datastore::Running);
670 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
671 }
672
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200673 SECTION("moving leaflist instances")
674 {
675 DatastoreAccess::Tree expected;
676 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200677 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:protocols[.='http']", "", "http"s, std::nullopt));
678 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:protocols[.='ftp']", "http"s, "ftp"s, std::nullopt));
679 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 +0200680 datastore.createItem("/example-schema:protocols[.='http']");
681 datastore.createItem("/example-schema:protocols[.='ftp']");
682 datastore.createItem("/example-schema:protocols[.='pop3']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200683 datastore.commitChanges();
684 expected = {
685 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
686 {"/example-schema:protocols[.='http']", "http"s},
687 {"/example-schema:protocols[.='ftp']", "ftp"s},
688 {"/example-schema:protocols[.='pop3']", "pop3"s},
689 };
690 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
691 }
692
693 std::string sourcePath;
694 SECTION("begin")
695 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200696 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 +0200697 sourcePath = "/example-schema:protocols[.='pop3']";
698 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
699 datastore.commitChanges();
700 expected = {
701 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
702 {"/example-schema:protocols[.='pop3']", "pop3"s},
703 {"/example-schema:protocols[.='http']", "http"s},
704 {"/example-schema:protocols[.='ftp']", "ftp"s},
705 };
706 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
707 }
708
709 SECTION("end")
710 {
711 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200712
713#if defined(yang_BACKEND) || defined(netconf_BACKEND)
714 // Due to the libyang diff algorithm being imperfect, the move operations differ between backends.
715 // The same applies for the stuff below.
716 // https://github.com/sysrepo/sysrepo/issues/2732
717 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='ftp']", ""s, "ftp"s, std::nullopt));
718 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='pop3']", "ftp"s, "pop3"s, std::nullopt));
719#else
720 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='http']", "pop3"s, "http"s, std::nullopt));
721#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200722 datastore.moveItem(sourcePath, yang::move::Absolute::End);
723 datastore.commitChanges();
724 expected = {
725 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
726 {"/example-schema:protocols[.='ftp']", "ftp"s},
727 {"/example-schema:protocols[.='pop3']", "pop3"s},
728 {"/example-schema:protocols[.='http']", "http"s},
729 };
730 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
731 }
732
733 SECTION("after")
734 {
735 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200736#if defined(yang_BACKEND) || defined(netconf_BACKEND)
737 // see the test for "end" for explanation if this #ifdef
738 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='ftp']", ""s, "ftp"s, std::nullopt));
739#else
740 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='http']", "ftp"s, "http"s, std::nullopt));
741#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200742 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{".", "ftp"s}}});
743 datastore.commitChanges();
744 expected = {
745 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
746 {"/example-schema:protocols[.='ftp']", "ftp"s},
747 {"/example-schema:protocols[.='http']", "http"s},
748 {"/example-schema:protocols[.='pop3']", "pop3"s},
749 };
750 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
751 }
752
753 SECTION("before")
754 {
755 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200756#if defined(yang_BACKEND) || defined(netconf_BACKEND)
757 // see the test for "end" for explanation if this #ifdef
758 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='ftp']", ""s, "ftp"s, std::nullopt));
759#else
760 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:protocols[.='http']", "ftp"s, "http"s, std::nullopt));
761#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200762 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{".", "pop3"s}}});
763 datastore.commitChanges();
764 expected = {
765 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
766 {"/example-schema:protocols[.='ftp']", "ftp"s},
767 {"/example-schema:protocols[.='http']", "http"s},
768 {"/example-schema:protocols[.='pop3']", "pop3"s},
769 };
770 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
771 }
772 }
773
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200774 SECTION("moving non-existing schema nodes")
775 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100776 catching<OnInvalidSchemaPathMove>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200777 datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
778 datastore.commitChanges();
779 });
780 }
781
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200782 SECTION("moving list instances")
783 {
784 DatastoreAccess::Tree expected;
785 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200786 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='John']", std::nullopt, std::nullopt, ""s));
787 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='John']/name", std::nullopt, "John"s, std::nullopt));
788 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, "[name='John']"));
789 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Eve']/name", std::nullopt, "Eve"s, std::nullopt));
790 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:players[name='Adam']", std::nullopt, std::nullopt, "[name='Eve']"));
791 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 +0200792 datastore.createItem("/example-schema:players[name='John']");
793 datastore.createItem("/example-schema:players[name='Eve']");
794 datastore.createItem("/example-schema:players[name='Adam']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200795 datastore.commitChanges();
796 expected = {
797 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
798 {"/example-schema:players[name='John']/name", "John"s},
799 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
800 {"/example-schema:players[name='Eve']/name", "Eve"s},
801 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
802 {"/example-schema:players[name='Adam']/name", "Adam"s},
803 };
804 REQUIRE(datastore.getItems("/example-schema:players") == expected);
805 }
806
807 std::string sourcePath;
808 SECTION("begin")
809 {
810 sourcePath = "/example-schema:players[name='Adam']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200811 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 +0200812 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
813 datastore.commitChanges();
814 expected = {
815 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
816 {"/example-schema:players[name='Adam']/name", "Adam"s},
817 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
818 {"/example-schema:players[name='John']/name", "John"s},
819 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
820 {"/example-schema:players[name='Eve']/name", "Eve"s},
821 };
822 REQUIRE(datastore.getItems("/example-schema:players") == expected);
823 }
824
825 SECTION("end")
826 {
827 sourcePath = "/example-schema:players[name='John']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200828#if defined(yang_BACKEND) || defined(netconf_BACKEND)
829 // TODO: see TODO comment in leaflist/end
830 // Although these make much less sense
831 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, ""));
832 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Adam']", std::nullopt, std::nullopt, "[name='Eve']"));
833#else
834 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='John']", std::nullopt, std::nullopt, "[name='Adam']"));
835#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200836 datastore.moveItem(sourcePath, yang::move::Absolute::End);
837 datastore.commitChanges();
838 expected = {
839 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
840 {"/example-schema:players[name='Eve']/name", "Eve"s},
841 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
842 {"/example-schema:players[name='Adam']/name", "Adam"s},
843 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
844 {"/example-schema:players[name='John']/name", "John"s},
845 };
846 REQUIRE(datastore.getItems("/example-schema:players") == expected);
847 }
848
849 SECTION("after")
850 {
851 sourcePath = "/example-schema:players[name='John']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200852#if defined(yang_BACKEND) || defined(netconf_BACKEND)
853 // TODO: see TODO comment in leaflist/end
854 // Although these make much less sense
855 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, ""));
856#else
857 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='John']", std::nullopt, std::nullopt, "[name='Eve']"));
858#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200859 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{"name", "Eve"s}}});
860 datastore.commitChanges();
861 expected = {
862 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
863 {"/example-schema:players[name='Eve']/name", "Eve"s},
864 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
865 {"/example-schema:players[name='John']/name", "John"s},
866 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
867 {"/example-schema:players[name='Adam']/name", "Adam"s},
868 };
869 REQUIRE(datastore.getItems("/example-schema:players") == expected);
870 }
871
872 SECTION("before")
873 {
874 sourcePath = "/example-schema:players[name='John']";
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200875#if defined(yang_BACKEND) || defined(netconf_BACKEND)
876 // TODO: see TODO comment in leaflist/end
877 // Although these make much less sense
878 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='Eve']", std::nullopt, std::nullopt, ""));
879#else
880 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Moved, "/example-schema:players[name='John']", std::nullopt, std::nullopt, "[name='Eve']"));
881#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200882 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{"name", "Adam"s}}});
883 datastore.commitChanges();
884 expected = {
885 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
886 {"/example-schema:players[name='Eve']/name", "Eve"s},
887 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
888 {"/example-schema:players[name='John']/name", "John"s},
889 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
890 {"/example-schema:players[name='Adam']/name", "Adam"s},
891 };
892 REQUIRE(datastore.getItems("/example-schema:players") == expected);
893 }
894 }
895
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200896 SECTION("getting /")
897 {
898 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200899 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 +0200900 datastore.setLeaf("/example-schema:leafInt32", 64);
901 datastore.commitChanges();
902 }
903
904 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200905 {"/example-schema:leafInt32", 64}
906 };
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200907 // This tests if we at least get the data WE added.
Václav Kubernát654303f2020-07-31 13:16:54 +0200908 REQUIRE(std::all_of(expected.begin(), expected.end(), [items = datastore.getItems("/")] (const auto& item) {
909 return std::find(items.begin(), items.end(), item) != items.end();
910 }));
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200911 }
912
Václav Kubernát36986c52020-06-25 10:30:05 +0200913 SECTION("setting and removing without commit")
914 {
915 datastore.setLeaf("/example-schema:leafInt32", 64);
916 datastore.deleteItem("/example-schema:leafInt32");
917 }
918
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200919 SECTION("two key lists")
920 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200921 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:point[x='12'][y='10']", std::nullopt, std::nullopt, std::nullopt));
922 REQUIRE_CALL(mockRunning, write(sysrepo::ChangeOperation::Created, "/example-schema:point[x='12'][y='10']/x", std::nullopt, "12"s, std::nullopt));
923 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 +0200924 datastore.createItem("/example-schema:point[x='12'][y='10']");
925 datastore.commitChanges();
926 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:point") != std::string::npos);
927 }
928
Václav Kubernát73109382018-09-14 19:52:03 +0200929 waitForCompletionAndBitMore(seq1);
930}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100931
Václav Kubernát654303f2020-07-31 13:16:54 +0200932struct ActionCb {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200933 sysrepo::ErrorCode operator()(
934 [[maybe_unused]] sysrepo::Session session,
935 [[maybe_unused]] uint32_t subscriptionId,
936 std::string_view xpath,
937 [[maybe_unused]] const libyang::DataNode input,
938 [[maybe_unused]] sysrepo::Event event,
939 [[maybe_unused]] uint32_t requestId,
940 libyang::DataNode output)
Václav Kubernáta8789602020-07-20 15:18:19 +0200941 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200942 if (session.getContext().findPath(xpath.data()).path() == "/example-schema:ports/shutdown") {
943 // `xpath` holds the subscription xpath which won't have list keys. We need the path with list keys and
944 // we'll find that in the input.
945 auto inputPath = input.findXPath("/example-schema:ports/shutdown").front().path();
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100946 output.newPath(joinPaths(inputPath, "success"), "true", libyang::CreationOptions::Output);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200947 return sysrepo::ErrorCode::Ok;
Václav Kubernáta8789602020-07-20 15:18:19 +0200948 }
949 throw std::runtime_error("unrecognized RPC");
950 }
Václav Kubernát654303f2020-07-31 13:16:54 +0200951};
Václav Kubernáta8789602020-07-20 15:18:19 +0200952
Václav Kubernát654303f2020-07-31 13:16:54 +0200953struct RpcCb {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200954 sysrepo::ErrorCode operator()(
955 [[maybe_unused]] sysrepo::Session session,
956 [[maybe_unused]] uint32_t subscriptionId,
957 std::string_view xpath,
958 const libyang::DataNode input,
959 [[maybe_unused]] sysrepo::Event event,
960 [[maybe_unused]] uint32_t requestId,
961 libyang::DataNode output)
Jan Kundrát6ee84792020-01-24 01:43:36 +0100962 {
963 const auto nukes = "/example-schema:launch-nukes"s;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200964 if (xpath == "/example-schema:noop"s || xpath == "/example-schema:fire"s) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200965 return sysrepo::ErrorCode::Ok;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200966 }
967
968 if (xpath == nukes) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100969 uint64_t kilotons = 0;
970 bool hasCities = false;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200971 for (const auto& inputNode : input.childrenDfs()) {
972 if (inputNode.path() == nukes) {
973 continue; // ignore, top-level RPC
974 }
975 if (inputNode.path() == nukes + "/payload") {
Václav Kubernátf4b6a932020-07-09 10:34:19 +0200976 continue; // ignore, container
977 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200978 if (inputNode.path() == nukes + "/description") {
Václav Kubernátf4b6a932020-07-09 10:34:19 +0200979 continue; // unused
980 }
981
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200982 if (inputNode.path() == nukes + "/payload/kilotons") {
983 kilotons = std::get<uint64_t>(inputNode.asTerm().value());
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100984 } else if (inputNode.path().find(nukes + "/cities") == 0) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100985 hasCities = true;
986 } else {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100987 throw std::runtime_error("RPC launch-nukes: unexpected input "s + inputNode.path());
Jan Kundrát6ee84792020-01-24 01:43:36 +0100988 }
989 }
990 if (kilotons == 333'666) {
991 // 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 +0200992 return sysrepo::ErrorCode::Ok;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100993 }
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100994 output.newPath(nukes + "/blast-radius", "33666", libyang::CreationOptions::Output);
995 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 +0100996 if (hasCities) {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100997 output.newPath(nukes + "/damaged-places/targets[city='London']/city", "London", libyang::CreationOptions::Output);
998 output.newPath(nukes + "/damaged-places/targets[city='Berlin']/city", "Berlin", libyang::CreationOptions::Output);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100999 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +02001000 return sysrepo::ErrorCode::Ok;
Jan Kundrát6ee84792020-01-24 01:43:36 +01001001 }
1002 throw std::runtime_error("unrecognized RPC");
1003 }
1004};
1005
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001006TEST_CASE("rpc/action")
1007{
Jan Kundrát6ee84792020-01-24 01:43:36 +01001008 trompeloeil::sequence seq1;
Jan Kundrát6ee84792020-01-24 01:43:36 +01001009
1010#ifdef sysrepo_BACKEND
Václav Kubernátf5d75152020-12-03 03:52:34 +01001011 auto datastore = std::make_shared<SysrepoAccess>();
Jan Kundrát6ee84792020-01-24 01:43:36 +01001012#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +02001013 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
1014 auto datastore = std::make_shared<NetconfAccess>(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +02001015#elif defined(yang_BACKEND)
Václav Kubernáte7248b22020-06-26 15:38:59 +02001016 auto datastore = std::make_shared<YangAccess>();
1017 datastore->addSchemaDir(schemaDir);
1018 datastore->addSchemaFile(exampleSchemaFile);
Jan Kundrát6ee84792020-01-24 01:43:36 +01001019#else
1020#error "Unknown backend"
1021#endif
1022
Václav Kubernátcfdb9222021-07-07 22:36:24 +02001023 sysrepo::setLogLevelStderr(sysrepo::LogLevel::Information);
1024
1025 auto srSubscription = sysrepo::Connection{}.sessionStart().onRPCAction("/example-schema:noop", RpcCb{});
1026 srSubscription.onRPCAction("/example-schema:launch-nukes", RpcCb{});
1027 srSubscription.onRPCAction("/example-schema:fire", RpcCb{});
1028 srSubscription.onRPCAction("/example-schema:ports/shutdown", ActionCb{});
1029
Václav Kubernát654303f2020-07-31 13:16:54 +02001030 SysrepoSubscription subscription("example-schema", nullptr);
Václav Kubernáte7248b22020-06-26 15:38:59 +02001031
Václav Kubernáta8789602020-07-20 15:18:19 +02001032 SECTION("rpc")
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001033 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001034 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
1035 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
1036 };
1037 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Jan Kundrát6ee84792020-01-24 01:43:36 +01001038
Václav Kubernáta8789602020-07-20 15:18:19 +02001039 // ProxyDatastore cannot easily read DatastoreAccess::Tree, so we need to set the input via create/setLeaf/etc.
1040 SECTION("valid")
1041 {
1042 std::string rpc;
1043 DatastoreAccess::Tree input, output;
1044
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001045 SECTION("noop")
1046 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001047 rpc = "/example-schema:noop";
Václav Kubernátb3960f82020-12-01 03:21:48 +01001048 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001049 }
1050
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001051 SECTION("small nuke")
1052 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001053 rpc = "/example-schema:launch-nukes";
1054 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001055 {joinPaths(rpc, "description"), "dummy"s},
1056 {joinPaths(rpc, "payload/kilotons"), uint64_t{333'666}},
Václav Kubernáta8789602020-07-20 15:18:19 +02001057 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001058 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001059 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{333'666});
1060 // no data are returned
1061 }
1062
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001063 SECTION("small nuke")
1064 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001065 rpc = "/example-schema:launch-nukes";
1066 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001067 {joinPaths(rpc, "description"), "dummy"s},
1068 {joinPaths(rpc, "payload/kilotons"), uint64_t{4}},
Václav Kubernáta8789602020-07-20 15:18:19 +02001069 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001070 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001071 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{4});
1072
1073 output = {
1074 {"blast-radius", uint32_t{33'666}},
1075 {"actual-yield", uint64_t{5}},
1076 };
1077 }
1078
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001079 SECTION("with lists")
1080 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001081 rpc = "/example-schema:launch-nukes";
1082 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001083 {joinPaths(rpc, "payload/kilotons"), uint64_t{6}},
1084 {joinPaths(rpc, "cities/targets[city='Prague']/city"), "Prague"s},
Václav Kubernáta8789602020-07-20 15:18:19 +02001085 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001086 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001087 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{6});
1088 proxyDatastore.createItem("/example-schema:launch-nukes/example-schema:cities/example-schema:targets[city='Prague']");
1089 output = {
1090 {"blast-radius", uint32_t{33'666}},
1091 {"actual-yield", uint64_t{7}},
1092 {"damaged-places", special_{SpecialValue::PresenceContainer}},
1093 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
1094 {"damaged-places/targets[city='London']/city", "London"s},
1095 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
1096 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
1097 };
1098 }
1099
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001100 SECTION("with leafref")
1101 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001102 datastore->createItem("/example-schema:person[name='Colton']");
1103 datastore->commitChanges();
1104
1105 rpc = "/example-schema:fire";
1106 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001107 {joinPaths(rpc, "whom"), "Colton"s}
Václav Kubernáta8789602020-07-20 15:18:19 +02001108 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001109 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001110 proxyDatastore.setLeaf("/example-schema:fire/example-schema:whom", "Colton"s);
1111 }
1112
Václav Kubernátb3960f82020-12-01 03:21:48 +01001113 catching<OnExec>([&] { REQUIRE(datastore->execute(rpc, input) == output); });
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001114 catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001115 }
1116
Václav Kubernáta8789602020-07-20 15:18:19 +02001117 SECTION("non-existing RPC")
1118 {
Václav Kubernátb3960f82020-12-01 03:21:48 +01001119 catching<OnInvalidRpcPath>([&] { datastore->execute("/example-schema:non-existing", DatastoreAccess::Tree{}); });
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001120 }
Václav Kubernát2edfe542021-02-03 08:08:29 +01001121
1122 SECTION("invalid RPC exec resets temporary datastore")
1123 {
1124 proxyDatastore.initiate("/example-schema:setIp");
1125 catching<OnInvalidRpcInput>([&] { auto output = proxyDatastore.execute(); });
1126 REQUIRE(proxyDatastore.inputDatastorePath() == std::nullopt);
1127 }
Jan Kundrát6ee84792020-01-24 01:43:36 +01001128 }
1129
Václav Kubernáta8789602020-07-20 15:18:19 +02001130 SECTION("action")
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001131 {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001132 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
1133 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
1134 };
1135 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Václav Kubernáta8789602020-07-20 15:18:19 +02001136 std::string path;
1137 DatastoreAccess::Tree input, output;
1138
1139 output = {
Václav Kubernáta8789602020-07-20 15:18:19 +02001140 {"success", true}
1141 };
1142 datastore->createItem("/example-schema:ports[name='A']");
1143 datastore->commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001144 SECTION("shutdown")
1145 {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001146 path = "/example-schema:ports[name='A']/example-schema:shutdown";
1147 input = {
1148 {"/example-schema:ports[name='A']/shutdown/force", true}
1149 };
1150 proxyDatastore.initiate(path);
1151 proxyDatastore.setLeaf("/example-schema:ports[name='A']/example-schema:shutdown/example-schema:force", true);
1152
Václav Kubernáta8789602020-07-20 15:18:19 +02001153 }
1154
Václav Kubernátb3960f82020-12-01 03:21:48 +01001155 catching<OnExec>([&] { REQUIRE(datastore->execute(path, input) == output); });
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001156 catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
Jan Kundrát6ee84792020-01-24 01:43:36 +01001157 }
1158
Václav Kubernáted4e3782022-03-02 23:57:33 +01001159 SECTION("parsed info in yang context")
1160 {
1161 auto schema = datastore->schema();
1162 auto leafTypeName = schema->leafTypeName("/example-schema:typedefedLeaf");
1163
1164#if defined(sysrepo_BACKEND)
1165 // Sysrepo is not available yet, with libyang parsed info context
1166 REQUIRE(leafTypeName == std::nullopt);
1167#else
1168 REQUIRE(leafTypeName == "myType");
1169#endif
1170 }
1171
Jan Kundrát6ee84792020-01-24 01:43:36 +01001172 waitForCompletionAndBitMore(seq1);
1173}
Václav Kubernátf5d75152020-12-03 03:52:34 +01001174
1175#if not defined(yang_BACKEND)
1176TEST_CASE("datastore targets")
1177{
1178 const auto testNode = "/example-schema:leafInt32";
1179 {
Václav Kubernátcfdb9222021-07-07 22:36:24 +02001180 auto sess = sysrepo::Connection{}.sessionStart();
1181 sess.deleteItem(testNode);
1182 sess.applyChanges(std::chrono::milliseconds{1000});
1183 sess.switchDatastore(sysrepo::Datastore::Startup);
1184 sess.deleteItem(testNode);
1185 sess.applyChanges(std::chrono::milliseconds{1000});
Václav Kubernátf5d75152020-12-03 03:52:34 +01001186 }
1187 MockRecorder mockRunning;
1188 MockRecorder mockStartup;
1189
1190#ifdef sysrepo_BACKEND
1191 SysrepoAccess datastore;
1192#elif defined(netconf_BACKEND)
1193 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
1194 NetconfAccess datastore(NETOPEER_SOCKET);
1195#else
1196#error "Unknown backend"
1197#endif
1198
1199 auto testGetItems = [&datastore] (const auto& path, const DatastoreAccess::Tree& expected) {
1200 REQUIRE(datastore.getItems(path) == expected);
1201 };
1202
1203 SECTION("subscriptions change operational target")
1204 {
1205 // Default target is operational, so setting a value and reading it should return no values as there are no
1206 // subscriptions yet.
1207 datastore.setLeaf(testNode, 10);
1208 datastore.commitChanges();
1209 testGetItems(testNode, {});
1210
1211 // Now we create a subscription and try again.
1212 SysrepoSubscription subRunning("example-schema", &mockRunning);
1213 testGetItems(testNode, {{testNode, 10}});
1214 }
1215
1216 SECTION("running shows stuff even without subscriptions")
1217 {
1218 datastore.setTarget(DatastoreTarget::Running);
1219 datastore.setLeaf(testNode, 10);
1220 datastore.commitChanges();
1221 testGetItems(testNode, {{testNode, 10}});
1222
1223 // Actually creating a subscription shouldn't make a difference.
1224 SysrepoSubscription subRunning("example-schema", &mockRunning);
1225 testGetItems(testNode, {{testNode, 10}});
1226 }
1227
1228 SECTION("startup changes only affect startup")
1229 {
1230 datastore.setTarget(DatastoreTarget::Startup);
1231 datastore.setLeaf(testNode, 10);
1232 datastore.commitChanges();
1233 testGetItems(testNode, {{testNode, 10}});
1234 datastore.setTarget(DatastoreTarget::Running);
1235 testGetItems(testNode, {});
1236 datastore.setTarget(DatastoreTarget::Operational);
1237 testGetItems(testNode, {});
1238 }
1239
1240}
1241#endif