blob: ba52b9cc4a6c233663feabbd7c12c80ad6aa8d8a [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 +020030namespace std {
Václav Kubernát69aabe92020-01-24 16:53:12 +010031std::ostream& operator<<(std::ostream& s, const std::optional<std::string>& opt)
32{
33 s << (opt ? *opt : "std::nullopt");
34 return s;
35}
36
Jan Kundrátb331b552020-01-23 15:25:29 +010037std::ostream& operator<<(std::ostream& s, const DatastoreAccess::Tree& map)
Václav Kubernát8e121ff2019-10-15 15:47:45 +020038{
39 s << std::endl
40 << "{";
41 for (const auto& it : map) {
42 s << "{\"" << it.first << "\", " << leafDataToString(it.second) << "}" << std::endl;
43 }
44 s << "}" << std::endl;
45 return s;
46}
47}
48
49TEST_CASE("setting/getting values")
Václav Kubernát73109382018-09-14 19:52:03 +020050{
Václav Kubernát73109382018-09-14 19:52:03 +020051 trompeloeil::sequence seq1;
52 MockRecorder mock;
Václav Kubernátab612e92019-11-26 19:51:31 +010053 SysrepoSubscription subscription("example-schema", &mock);
Václav Kubernátc31bd602019-03-07 11:44:48 +010054
55#ifdef sysrepo_BACKEND
Václav Kubernát73109382018-09-14 19:52:03 +020056 SysrepoAccess datastore("netconf-cli-test");
Václav Kubernátc31bd602019-03-07 11:44:48 +010057#elif defined(netconf_BACKEND)
58 NetconfAccess datastore(NETOPEER_SOCKET_PATH);
59#else
60#error "Unknown backend"
61#endif
Václav Kubernát73109382018-09-14 19:52:03 +020062
Václav Kubernát69aabe92020-01-24 16:53:12 +010063
Václav Kubernát134d78f2019-09-03 16:42:29 +020064 SECTION("set leafInt8 to -128")
Václav Kubernát73109382018-09-14 19:52:03 +020065 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010066 REQUIRE_CALL(mock, write("/example-schema:leafInt8", std::nullopt, "-128"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020067 datastore.setLeaf("/example-schema:leafInt8", int8_t{-128});
68 datastore.commitChanges();
69 }
70
71 SECTION("set leafInt16 to -32768")
72 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010073 REQUIRE_CALL(mock, write("/example-schema:leafInt16", std::nullopt, "-32768"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020074 datastore.setLeaf("/example-schema:leafInt16", int16_t{-32768});
75 datastore.commitChanges();
76 }
77
78 SECTION("set leafInt32 to -2147483648")
79 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010080 REQUIRE_CALL(mock, write("/example-schema:leafInt32", std::nullopt, "-2147483648"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020081 datastore.setLeaf("/example-schema:leafInt32", int32_t{-2147483648});
82 datastore.commitChanges();
83 }
84
85 SECTION("set leafInt64 to -50000000000")
86 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010087 REQUIRE_CALL(mock, write("/example-schema:leafInt64", std::nullopt, "-50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020088 datastore.setLeaf("/example-schema:leafInt64", int64_t{-50000000000});
89 datastore.commitChanges();
90 }
91
92 SECTION("set leafUInt8 to 255")
93 {
Václav Kubernát69aabe92020-01-24 16:53:12 +010094 REQUIRE_CALL(mock, write("/example-schema:leafUInt8", std::nullopt, "255"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +020095 datastore.setLeaf("/example-schema:leafUInt8", uint8_t{255});
96 datastore.commitChanges();
97 }
98
99 SECTION("set leafUInt16 to 65535")
100 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100101 REQUIRE_CALL(mock, write("/example-schema:leafUInt16", std::nullopt, "65535"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200102 datastore.setLeaf("/example-schema:leafUInt16", uint16_t{65535});
103 datastore.commitChanges();
104 }
105
106 SECTION("set leafUInt32 to 4294967295")
107 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100108 REQUIRE_CALL(mock, write("/example-schema:leafUInt32", std::nullopt, "4294967295"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200109 datastore.setLeaf("/example-schema:leafUInt32", uint32_t{4294967295});
110 datastore.commitChanges();
111 }
112
113 SECTION("set leafUInt64 to 50000000000")
114 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100115 REQUIRE_CALL(mock, write("/example-schema:leafUInt64", std::nullopt, "50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200116 datastore.setLeaf("/example-schema:leafUInt64", uint64_t{50000000000});
Václav Kubernát73109382018-09-14 19:52:03 +0200117 datastore.commitChanges();
118 }
119
120 SECTION("set leafEnum to coze")
121 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100122 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "coze"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200123 datastore.setLeaf("/example-schema:leafEnum", enum_{"coze"});
124 datastore.commitChanges();
125 }
126
127 SECTION("set leafDecimal to 123.544")
128 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100129 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.544"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200130 datastore.setLeaf("/example-schema:leafDecimal", 123.544);
131 datastore.commitChanges();
132 }
133
134 SECTION("create presence container")
135 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100136 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
Václav Kubernát73109382018-09-14 19:52:03 +0200137 datastore.createPresenceContainer("/example-schema:pContainer");
138 datastore.commitChanges();
139 }
140
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100141 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200142 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100143 {
144 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", std::nullopt, ""s));
145 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", std::nullopt, "Nguyen"s));
146 datastore.createListInstance("/example-schema:person[name='Nguyen']");
147 datastore.commitChanges();
148 }
149 {
150 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']", ""s, std::nullopt));
151 REQUIRE_CALL(mock, write("/example-schema:person[name='Nguyen']/name", "Nguyen"s, std::nullopt));
152 datastore.deleteListInstance("/example-schema:person[name='Nguyen']");
153 datastore.commitChanges();
154 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200155 }
156
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200157 SECTION("leafref pointing to a key of a list")
158 {
159 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100160 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']", std::nullopt, ""s));
161 REQUIRE_CALL(mock, write("/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s));
162 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']", std::nullopt, ""s));
163 REQUIRE_CALL(mock, write("/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s));
164 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']", std::nullopt, ""s));
165 REQUIRE_CALL(mock, write("/example-schema:person[name='Kolafa']/name", std::nullopt, "Kolafa"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200166 datastore.createListInstance("/example-schema:person[name='Dan']");
167 datastore.createListInstance("/example-schema:person[name='Elfi']");
168 datastore.createListInstance("/example-schema:person[name='Kolafa']");
169 datastore.commitChanges();
170 }
171
172 // The commitChanges method has to be called in each of the
173 // SECTIONs, because the REQUIRE_CALL only works inside the given
174 // SECTION.
175 SECTION("Dan")
176 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100177 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Dan"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200178 datastore.setLeaf("/example-schema:bossPerson", std::string{"Dan"});
179 datastore.commitChanges();
180 }
181
182 SECTION("Elfi")
183 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100184 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Elfi"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200185 datastore.setLeaf("/example-schema:bossPerson", std::string{"Elfi"});
186 datastore.commitChanges();
187 }
188
189 SECTION("Kolafa")
190 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100191 REQUIRE_CALL(mock, write("/example-schema:bossPerson", std::nullopt, "Kolafa"s));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200192 datastore.setLeaf("/example-schema:bossPerson", std::string{"Kolafa"});
193 datastore.commitChanges();
194 }
195 }
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200196 SECTION("bool values get correctly represented as bools")
197 {
198 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100199 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "true"s));
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200200 datastore.setLeaf("/example-schema:down", bool{true});
201 datastore.commitChanges();
202 }
203
Jan Kundrátb331b552020-01-23 15:25:29 +0100204 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200205 REQUIRE(datastore.getItems("/example-schema:down") == expected);
206 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200207
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200208 SECTION("getting items from the whole module")
209 {
210 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100211 REQUIRE_CALL(mock, write("/example-schema:up", std::nullopt, "true"s));
212 REQUIRE_CALL(mock, write("/example-schema:down", std::nullopt, "false"s));
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200213 datastore.setLeaf("/example-schema:up", bool{true});
214 datastore.setLeaf("/example-schema:down", bool{false});
215 datastore.commitChanges();
216 }
217
Jan Kundrátb331b552020-01-23 15:25:29 +0100218 DatastoreAccess::Tree expected{{"/example-schema:down", bool{false}},
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200219 // Sysrepo always returns containers when getting values, but
220 // libnetconf does not. This is fine by the YANG standard:
221 // https://tools.ietf.org/html/rfc7950#section-7.5.7 Furthermore,
222 // NetconfAccess implementation actually only iterates over leafs,
223 // so even if libnetconf did include containers, they wouldn't get
224 // shown here anyway. With sysrepo2, this won't be necessary,
225 // because it'll use the same data structure as libnetconf, so the
226 // results will be consistent.
227#ifdef sysrepo_BACKEND
Václav Kubernát144729d2020-01-08 15:20:35 +0100228 {"/example-schema:lol", special_{SpecialValue::Container}},
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100229 {"/example-schema:inventory", special_{SpecialValue::Container}},
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200230#endif
231 {"/example-schema:up", bool{true}}};
232 REQUIRE(datastore.getItems("/example-schema:*") == expected);
233 }
234
Václav Kubernát152ce222019-12-19 12:23:32 +0100235 SECTION("getItems returns correct datatypes")
236 {
237 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100238 REQUIRE_CALL(mock, write("/example-schema:leafEnum", std::nullopt, "lol"s));
Václav Kubernát152ce222019-12-19 12:23:32 +0100239 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
240 datastore.commitChanges();
241 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100242 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100243
244 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
245 }
246
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100247 SECTION("getItems on a list")
248 {
249 {
Václav Kubernát69aabe92020-01-24 16:53:12 +0100250 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']", std::nullopt, ""s));
251 REQUIRE_CALL(mock, write("/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s));
252 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']", std::nullopt, ""s));
253 REQUIRE_CALL(mock, write("/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s));
254 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']", std::nullopt, ""s));
255 REQUIRE_CALL(mock, write("/example-schema:person[name='Petr']/name", std::nullopt, "Petr"s));
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100256 datastore.createListInstance("/example-schema:person[name='Jan']");
257 datastore.createListInstance("/example-schema:person[name='Michal']");
258 datastore.createListInstance("/example-schema:person[name='Petr']");
259 datastore.commitChanges();
260 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100261 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100262 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100263 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100264 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100265 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100266 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100267 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
268 };
269
270 REQUIRE(datastore.getItems("/example-schema:person") == expected);
271 }
272
Václav Kubernát69aabe92020-01-24 16:53:12 +0100273 SECTION("presence containers")
274 {
275 DatastoreAccess::Tree expected;
276 // Make sure it's not there before we create it
277 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
278
279 {
280 REQUIRE_CALL(mock, write("/example-schema:pContainer", std::nullopt, ""s));
281 datastore.createPresenceContainer("/example-schema:pContainer");
282 datastore.commitChanges();
283 }
284 expected = {
285 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
286 };
287 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
288
289 // Make sure it's not there after we delete it
290 {
291 REQUIRE_CALL(mock, write("/example-schema:pContainer", ""s, std::nullopt));
292 datastore.deletePresenceContainer("/example-schema:pContainer");
293 datastore.commitChanges();
294 }
295 expected = {};
296 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100297 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100298
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100299 SECTION("nested presence container")
300 {
301 DatastoreAccess::Tree expected;
302 // Make sure it's not there before we create it
303 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
304 {
305 REQUIRE_CALL(mock, write("/example-schema:inventory", std::nullopt, ""s));
306 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", std::nullopt, ""s));
307 datastore.createPresenceContainer("/example-schema:inventory/stuff");
308 datastore.commitChanges();
309 }
310 expected = {
311 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
312 };
313 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
314 {
315 REQUIRE_CALL(mock, write("/example-schema:inventory", ""s, std::nullopt));
316 REQUIRE_CALL(mock, write("/example-schema:inventory/stuff", ""s, std::nullopt));
317 datastore.deletePresenceContainer("/example-schema:inventory/stuff");
318 datastore.commitChanges();
319 }
320 expected = {};
321 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100322 }
323
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100324 SECTION("floats")
325 {
326 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
327 REQUIRE_CALL(mock, write("/example-schema:leafDecimal", std::nullopt, "123.4"s));
328 datastore.commitChanges();
329 DatastoreAccess::Tree expected {
330 {"/example-schema:leafDecimal", 123.4},
331 };
332 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
333 }
334
Václav Kubernát73109382018-09-14 19:52:03 +0200335 waitForCompletionAndBitMore(seq1);
336}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100337
338class RpcCb: public sysrepo::Callback {
339 int rpc(const char *xpath, const ::sysrepo::S_Vals input, ::sysrepo::S_Vals_Holder output, void *) override
340 {
341 const auto nukes = "/example-schema:launch-nukes"s;
342 if (xpath == "/example-schema:noop"s) {
343 return SR_ERR_OK;
344 } else if (xpath == nukes) {
345 uint64_t kilotons = 0;
346 bool hasCities = false;
347 for (size_t i = 0; i < input->val_cnt(); ++i) {
348 const auto& val = input->val(i);
349 if (val->xpath() == nukes + "/payload/kilotons") {
350 kilotons = val->data()->get_uint64();
351 } else if (val->xpath() == nukes + "/payload") {
352 // ignore, container
353 } else if (val->xpath() == nukes + "/description") {
354 // unused
355 } else if (std::string_view{val->xpath()}.find(nukes + "/cities") == 0) {
356 hasCities = true;
357 } else {
358 throw std::runtime_error("RPC launch-nukes: unexpected input "s + val->xpath());
359 }
360 }
361 if (kilotons == 333'666) {
362 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
363 return SR_ERR_OK;
364 }
365 auto buf = output->allocate(2);
366 size_t i = 0;
367 buf->val(i++)->set((nukes + "/blast-radius").c_str(), uint32_t{33'666});
368 buf->val(i++)->set((nukes + "/actual-yield").c_str(), static_cast<uint64_t>(1.33 * kilotons));
369 if (hasCities) {
370 buf = output->reallocate(output->val_cnt() + 2);
371 buf->val(i++)->set((nukes + "/damaged-places/targets[city='London']/city").c_str(), "London");
372 buf->val(i++)->set((nukes + "/damaged-places/targets[city='Berlin']/city").c_str(), "Berlin");
373 }
374 return SR_ERR_OK;
375 }
376 throw std::runtime_error("unrecognized RPC");
377 }
378};
379
380TEST_CASE("rpc") {
381 trompeloeil::sequence seq1;
382 auto srConn = std::make_shared<sysrepo::Connection>("netconf-cli-test-rpc");
383 auto srSession = std::make_shared<sysrepo::Session>(srConn);
384 auto srSubscription = std::make_shared<sysrepo::Subscribe>(srSession);
385 auto cb = std::make_shared<RpcCb>();
386 sysrepo::Logs{}.set_stderr(SR_LL_INF);
387 srSubscription->rpc_subscribe("/example-schema:noop", cb, nullptr, SR_SUBSCR_CTX_REUSE);
388 srSubscription->rpc_subscribe("/example-schema:launch-nukes", cb, nullptr, SR_SUBSCR_CTX_REUSE);
389
390#ifdef sysrepo_BACKEND
391 SysrepoAccess datastore("netconf-cli-test");
392#elif defined(netconf_BACKEND)
393 NetconfAccess datastore(NETOPEER_SOCKET_PATH);
394#else
395#error "Unknown backend"
396#endif
397
398 std::string rpc;
399 DatastoreAccess::Tree input, output;
400
401 SECTION("noop") {
402 rpc = "/example-schema:noop";
403 }
404
405 SECTION("small nuke") {
406 rpc = "/example-schema:launch-nukes";
407 input = {
408 {"description", "dummy"s},
409 {"payload/kilotons", uint64_t{333'666}},
410 };
411 // no data are returned
412 }
413
414 SECTION("small nuke") {
415 rpc = "/example-schema:launch-nukes";
416 input = {
417 {"description", "dummy"s},
418 {"payload/kilotons", uint64_t{4}},
419 };
420 output = {
421 {"blast-radius", uint32_t{33'666}},
422 {"actual-yield", uint64_t{5}},
423 };
424 }
425
426 SECTION("with lists") {
427 rpc = "/example-schema:launch-nukes";
428 input = {
429 {"payload/kilotons", uint64_t{6}},
430 {"cities/targets[city='Prague']/city", "Prague"s},
431 };
432 output = {
433 {"blast-radius", uint32_t{33'666}},
434 {"actual-yield", uint64_t{7}},
435 {"damaged-places", special_{SpecialValue::PresenceContainer}},
436 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
437 {"damaged-places/targets[city='London']/city", "London"s},
438 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
439 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
440 };
441 }
442
443 REQUIRE(datastore.executeRpc(rpc, input) == output);
444
445 waitForCompletionAndBitMore(seq1);
446}