blob: 311b395c20786ada2f1185dbbf2b3a993cdd1420 [file] [log] [blame]
Václav Kubernát73109382018-09-14 19:52:03 +02001/*
2 * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
3 * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
4 *
5 * Written by Václav Kubernát <kubervac@fit.cvut.cz>
6 *
7*/
8
Václav Kubernát26b56082020-02-03 18:28:56 +01009#include "trompeloeil_doctest.hpp"
Jan Kundrát6ee84792020-01-24 01:43:36 +010010#include <sysrepo-cpp/Session.hpp>
Václav Kubernát73109382018-09-14 19:52:03 +020011
Václav Kubernátc31bd602019-03-07 11:44:48 +010012#ifdef sysrepo_BACKEND
Václav Kubernát73109382018-09-14 19:52:03 +020013#include "sysrepo_access.hpp"
Václav Kubernátc31bd602019-03-07 11:44:48 +010014#elif defined(netconf_BACKEND)
15#include "netconf_access.hpp"
16#include "netopeer_vars.hpp"
17#else
18#error "Unknown backend"
19#endif
Václav Kubernát73109382018-09-14 19:52:03 +020020#include "sysrepo_subscription.hpp"
Václav Kubernát8e121ff2019-10-15 15:47:45 +020021#include "utils.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020022
Jan Kundrát6ee84792020-01-24 01:43:36 +010023using namespace std::literals::string_literals;
24
Václav Kubernát69aabe92020-01-24 16:53:12 +010025class MockRecorder : public trompeloeil::mock_interface<Recorder> {
Václav Kubernát73109382018-09-14 19:52:03 +020026public:
Václav Kubernát69aabe92020-01-24 16:53:12 +010027 IMPLEMENT_MOCK3(write);
Václav Kubernát73109382018-09-14 19:52:03 +020028};
29
Václav Kubernát8e121ff2019-10-15 15:47:45 +020030TEST_CASE("setting/getting values")
Václav Kubernát73109382018-09-14 19:52:03 +020031{
Václav Kubernát73109382018-09-14 19:52:03 +020032 trompeloeil::sequence seq1;
33 MockRecorder mock;
Václav Kubernátab612e92019-11-26 19:51:31 +010034 SysrepoSubscription subscription("example-schema", &mock);
Václav Kubernátc31bd602019-03-07 11:44:48 +010035
36#ifdef sysrepo_BACKEND
Václav Kubernát73109382018-09-14 19:52:03 +020037 SysrepoAccess datastore("netconf-cli-test");
Václav Kubernátc31bd602019-03-07 11:44:48 +010038#elif defined(netconf_BACKEND)
39 NetconfAccess datastore(NETOPEER_SOCKET_PATH);
40#else
41#error "Unknown backend"
42#endif
Václav Kubernát73109382018-09-14 19:52:03 +020043
Václav Kubernát69aabe92020-01-24 16:53:12 +010044
Václav Kubernát134d78f2019-09-03 16:42:29 +020045 SECTION("set leafInt8 to -128")
Václav Kubernát73109382018-09-14 19:52:03 +020046 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010047 REQUIRE_CALL(mock, write("/example-schema:leafInt8", std::nullopt, "-128"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020048 datastore.setLeaf("/example-schema:leafInt8", int8_t{-128});
49 datastore.commitChanges();
50 }
51
52 SECTION("set leafInt16 to -32768")
53 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010054 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "-32768"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020055 datastore.setLeaf("/example-schema:leafInt16", int16_t{-32768});
56 datastore.commitChanges();
57 }
58
59 SECTION("set leafInt32 to -2147483648")
60 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010061 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "-2147483648"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020062 datastore.setLeaf("/example-schema:leafInt32", int32_t{-2147483648});
63 datastore.commitChanges();
64 }
65
66 SECTION("set leafInt64 to -50000000000")
67 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010068 REQUIRE_CALL(mock, write("/example-schema:leafInt64", std::nullopt, "-50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020069 datastore.setLeaf("/example-schema:leafInt64", int64_t{-50000000000});
70 datastore.commitChanges();
71 }
72
73 SECTION("set leafUInt8 to 255")
74 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010075 REQUIRE_CALL(mock, write("/example-schema:leafUInt8", std::nullopt, "255"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020076 datastore.setLeaf("/example-schema:leafUInt8", uint8_t{255});
77 datastore.commitChanges();
78 }
79
80 SECTION("set leafUInt16 to 65535")
81 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010082 REQUIRE_CALL(mock, write("/example-schema:leafUInt16", std::nullopt, "65535"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020083 datastore.setLeaf("/example-schema:leafUInt16", uint16_t{65535});
84 datastore.commitChanges();
85 }
86
87 SECTION("set leafUInt32 to 4294967295")
88 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010089 REQUIRE_CALL(mock, write("/example-schema:leafUInt32", std::nullopt, "4294967295"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020090 datastore.setLeaf("/example-schema:leafUInt32", uint32_t{4294967295});
91 datastore.commitChanges();
92 }
93
94 SECTION("set leafUInt64 to 50000000000")
95 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010096 REQUIRE_CALL(mock, write("/example-schema:leafUInt64", std::nullopt, "50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020097 datastore.setLeaf("/example-schema:leafUInt64", uint64_t{50000000000});
Václav Kubernát73109382018-09-14 19:52:03 +020098 datastore.commitChanges();
99 }
100
101 SECTION("set leafEnum to coze")
102 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100103 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "coze"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200104 datastore.setLeaf("/example-schema:leafEnum", enum_{"coze"});
105 datastore.commitChanges();
106 }
107
108 SECTION("set leafDecimal to 123.544")
109 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100110 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.544"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200111 datastore.setLeaf("/example-schema:leafDecimal", 123.544);
112 datastore.commitChanges();
113 }
114
115 SECTION("create presence container")
116 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100117 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Václav Kubernát73109382018-09-14 19:52:03 +0200118 datastore.createPresenceContainer("/example-schema:pContainer");
119 datastore.commitChanges();
120 }
121
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100122 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200123 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100124 {
125 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", std::nullopt, ""s));
126 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", std::nullopt, "Nguyen"s));
127 datastore.createListInstance("/example-schema:person[name='Nguyen']");
128 datastore.commitChanges();
129 }
130 {
131 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", ""s, std::nullopt));
132 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", "Nguyen"s, std::nullopt));
133 datastore.deleteListInstance("/example-schema:person[name='Nguyen']");
134 datastore.commitChanges();
135 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200136 }
137
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200138 SECTION("leafref pointing to a key of a list")
139 {
140 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100141 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']", std::nullopt, ""s));
142 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s));
143 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']", std::nullopt, ""s));
144 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s));
145 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']", std::nullopt, ""s));
146 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']/name", std::nullopt, "Kolafa"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200147 datastore.createListInstance("/example-schema:person[name='Dan']");
148 datastore.createListInstance("/example-schema:person[name='Elfi']");
149 datastore.createListInstance("/example-schema:person[name='Kolafa']");
150 datastore.commitChanges();
151 }
152
153 // The commitChanges method has to be called in each of the
154 // SECTIONs, because the REQUIRE_CALL only works inside the given
155 // SECTION.
156 SECTION("Dan")
157 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100158 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Dan"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200159 datastore.setLeaf("/example-schema:bossPerson", std::string{"Dan"});
160 datastore.commitChanges();
161 }
162
163 SECTION("Elfi")
164 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100165 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Elfi"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200166 datastore.setLeaf("/example-schema:bossPerson", std::string{"Elfi"});
167 datastore.commitChanges();
168 }
169
170 SECTION("Kolafa")
171 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100172 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Kolafa"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200173 datastore.setLeaf("/example-schema:bossPerson", std::string{"Kolafa"});
174 datastore.commitChanges();
175 }
176 }
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200177 SECTION("bool values get correctly represented as bools")
178 {
179 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100180 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "true"s));
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200181 datastore.setLeaf("/example-schema:down", bool{true});
182 datastore.commitChanges();
183 }
184
Jan Kundrátb331b552020-01-23 15:25:29 +0100185 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200186 REQUIRE(datastore.getItems("/example-schema:down") == expected);
187 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200188
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200189 SECTION("getting items from the whole module")
190 {
191 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100192 REQUIRE_CALL(mock, write("/example-schema:up", std::nullopt, "true"s));
193 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "false"s));
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200194 datastore.setLeaf("/example-schema:up", bool{true});
195 datastore.setLeaf("/example-schema:down", bool{false});
196 datastore.commitChanges();
197 }
198
Jan Kundrátb331b552020-01-23 15:25:29 +0100199 DatastoreAccess::Tree expected{{"/example-schema:down", bool{false}},
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200200 // Sysrepo always returns containers when getting values, but
201 // libnetconf does not. This is fine by the YANG standard:
202 // https://tools.ietf.org/html/rfc7950#section-7.5.7 Furthermore,
203 // NetconfAccess implementation actually only iterates over leafs,
204 // so even if libnetconf did include containers, they wouldn't get
205 // shown here anyway. With sysrepo2, this won't be necessary,
206 // because it'll use the same data structure as libnetconf, so the
207 // results will be consistent.
208#ifdef sysrepo_BACKEND
Václav Kubernát144729d2020-01-08 15:20:35 +0100209 {"/example-schema:lol", special_{SpecialValue::Container}},
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100210 {"/example-schema:inventory", special_{SpecialValue::Container}},
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200211#endif
212 {"/example-schema:up", bool{true}}};
213 REQUIRE(datastore.getItems("/example-schema:*") == expected);
214 }
215
Václav Kubernát152ce222019-12-19 12:23:32 +0100216 SECTION("getItems returns correct datatypes")
217 {
218 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100219 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "lol"s));
Václav Kubernát152ce222019-12-19 12:23:32 +0100220 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
221 datastore.commitChanges();
222 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100223 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100224
225 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
226 }
227
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100228 SECTION("getItems on a list")
229 {
230 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100231 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']", std::nullopt, ""s));
232 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s));
233 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']", std::nullopt, ""s));
234 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s));
235 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']", std::nullopt, ""s));
236 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']/name", std::nullopt, "Petr"s));
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100237 datastore.createListInstance("/example-schema:person[name='Jan']");
238 datastore.createListInstance("/example-schema:person[name='Michal']");
239 datastore.createListInstance("/example-schema:person[name='Petr']");
240 datastore.commitChanges();
241 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100242 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100243 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100244 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100245 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100246 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100247 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100248 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
249 };
250
251 REQUIRE(datastore.getItems("/example-schema:person") == expected);
252 }
253
Václav Kubernát69aabe92020-01-24 16:53:12 +0100254 SECTION("presence containers")
255 {
256 DatastoreAccess::Tree expected;
257 // Make sure it's not there before we create it
258 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
259
260 {
261 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
262 datastore.createPresenceContainer("/example-schema:pContainer");
263 datastore.commitChanges();
264 }
265 expected = {
266 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
267 };
268 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
269
270 // Make sure it's not there after we delete it
271 {
272 REQUIRE_CALL(mock, write("/example-schema:pContainer", ""s, std::nullopt));
273 datastore.deletePresenceContainer("/example-schema:pContainer");
274 datastore.commitChanges();
275 }
276 expected = {};
277 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100278 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100279
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100280 SECTION("nested presence container")
281 {
282 DatastoreAccess::Tree expected;
283 // Make sure it's not there before we create it
284 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
285 {
286 REQUIRE_CALL(mock, write("/example-schema:inventory", std::nullopt, ""s));
287 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", std::nullopt, ""s));
288 datastore.createPresenceContainer("/example-schema:inventory/stuff");
289 datastore.commitChanges();
290 }
291 expected = {
292 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
293 };
294 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
295 {
296 REQUIRE_CALL(mock, write("/example-schema:inventory", ""s, std::nullopt));
297 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", ""s, std::nullopt));
298 datastore.deletePresenceContainer("/example-schema:inventory/stuff");
299 datastore.commitChanges();
300 }
301 expected = {};
302 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100303 }
304
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100305 SECTION("floats")
306 {
307 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
308 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.4"s));
309 datastore.commitChanges();
310 DatastoreAccess::Tree expected {
311 {"/example-schema:leafDecimal", 123.4},
312 };
313 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
314 }
315
Václav Kubernát73109382018-09-14 19:52:03 +0200316 waitForCompletionAndBitMore(seq1);
317}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100318
319class RpcCb: public sysrepo::Callback {
320 int rpc(const char *xpath, const ::sysrepo::S_Vals input, ::sysrepo::S_Vals_Holder output, void *) override
321 {
322 const auto nukes = "/example-schema:launch-nukes"s;
323 if (xpath == "/example-schema:noop"s) {
324 return SR_ERR_OK;
325 } else if (xpath == nukes) {
326 uint64_t kilotons = 0;
327 bool hasCities = false;
328 for (size_t i = 0; i < input->val_cnt(); ++i) {
329 const auto& val = input->val(i);
330 if (val->xpath() == nukes + "/payload/kilotons") {
331 kilotons = val->data()->get_uint64();
332 } else if (val->xpath() == nukes + "/payload") {
333 // ignore, container
334 } else if (val->xpath() == nukes + "/description") {
335 // unused
336 } else if (std::string_view{val->xpath()}.find(nukes + "/cities") == 0) {
337 hasCities = true;
338 } else {
339 throw std::runtime_error("RPC launch-nukes: unexpected input "s + val->xpath());
340 }
341 }
342 if (kilotons == 333'666) {
343 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
344 return SR_ERR_OK;
345 }
346 auto buf = output->allocate(2);
347 size_t i = 0;
348 buf->val(i++)->set((nukes + "/blast-radius").c_str(), uint32_t{33'666});
349 buf->val(i++)->set((nukes + "/actual-yield").c_str(), static_cast<uint64_t>(1.33 * kilotons));
350 if (hasCities) {
351 buf = output->reallocate(output->val_cnt() + 2);
352 buf->val(i++)->set((nukes + "/damaged-places/targets[city='London']/city").c_str(), "London");
353 buf->val(i++)->set((nukes + "/damaged-places/targets[city='Berlin']/city").c_str(), "Berlin");
354 }
355 return SR_ERR_OK;
356 }
357 throw std::runtime_error("unrecognized RPC");
358 }
359};
360
361TEST_CASE("rpc") {
362 trompeloeil::sequence seq1;
363 auto srConn = std::make_shared<sysrepo::Connection>("netconf-cli-test-rpc");
364 auto srSession = std::make_shared<sysrepo::Session>(srConn);
365 auto srSubscription = std::make_shared<sysrepo::Subscribe>(srSession);
366 auto cb = std::make_shared<RpcCb>();
367 sysrepo::Logs{}.set_stderr(SR_LL_INF);
368 srSubscription->rpc_subscribe("/example-schema:noop", cb, nullptr, SR_SUBSCR_CTX_REUSE);
369 srSubscription->rpc_subscribe("/example-schema:launch-nukes", cb, nullptr, SR_SUBSCR_CTX_REUSE);
370
371#ifdef sysrepo_BACKEND
372 SysrepoAccess datastore("netconf-cli-test");
373#elif defined(netconf_BACKEND)
374 NetconfAccess datastore(NETOPEER_SOCKET_PATH);
375#else
376#error "Unknown backend"
377#endif
378
379 std::string rpc;
380 DatastoreAccess::Tree input, output;
381
382 SECTION("noop") {
383 rpc = "/example-schema:noop";
384 }
385
386 SECTION("small nuke") {
387 rpc = "/example-schema:launch-nukes";
388 input = {
389 {"description", "dummy"s},
390 {"payload/kilotons", uint64_t{333'666}},
391 };
392 // no data are returned
393 }
394
395 SECTION("small nuke") {
396 rpc = "/example-schema:launch-nukes";
397 input = {
398 {"description", "dummy"s},
399 {"payload/kilotons", uint64_t{4}},
400 };
401 output = {
402 {"blast-radius", uint32_t{33'666}},
403 {"actual-yield", uint64_t{5}},
404 };
405 }
406
407 SECTION("with lists") {
408 rpc = "/example-schema:launch-nukes";
409 input = {
410 {"payload/kilotons", uint64_t{6}},
411 {"cities/targets[city='Prague']/city", "Prague"s},
412 };
413 output = {
414 {"blast-radius", uint32_t{33'666}},
415 {"actual-yield", uint64_t{7}},
416 {"damaged-places", special_{SpecialValue::PresenceContainer}},
417 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
418 {"damaged-places/targets[city='London']/city", "London"s},
419 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
420 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
421 };
422 }
423
424 REQUIRE(datastore.executeRpc(rpc, input) == output);
425
426 waitForCompletionAndBitMore(seq1);
427}