28
28
#define REQUIRE_NOTIFICATION (SUBSCRIPTION, NOTIFICATION ) \
29
29
TROMPELOEIL_REQUIRE_CALL (rec, recordNotification(NOTIFICATION)).IN_SEQUENCE(seq);
30
30
31
+ #define REQUIRE_NAMED_NOTIFICATION (SUBSCRIPTION, NOTIFICATION ) \
32
+ expectations.emplace_back(TROMPELOEIL_NAMED_REQUIRE_CALL(rec, recordNotification(NOTIFICATION)).IN_SEQUENCE(seq));
33
+
31
34
#define READ_NOTIFICATION (SUBSCRIPTION ) \
32
35
REQUIRE (pipeStatus((SUBSCRIPTION).fd()) == PipeStatus::DataReady); \
33
36
(SUBSCRIPTION).processEvent(cbNotif);
@@ -276,6 +279,89 @@ TEST_CASE("Dynamic subscriptions")
276
279
const auto excMessage = " Couldn't terminate yang-push subscription with id " + std::to_string (sub->subscriptionId ()) + " : SR_ERR_NOT_FOUND" ;
277
280
REQUIRE_THROWS_WITH_AS (sub->terminate (), excMessage.c_str (), sysrepo::ErrorWithCode);
278
281
}
282
+
283
+ DOCTEST_SUBCASE (" Filtering" )
284
+ {
285
+ std::optional<sysrepo::DynamicSubscription> sub;
286
+ std::vector<std::unique_ptr<trompeloeil::expectation>> expectations;
287
+
288
+ DOCTEST_SUBCASE (" xpath filter" )
289
+ {
290
+ sub = sess.subscribeNotifications (" /test_module:ping" );
291
+
292
+ REQUIRE_NAMED_NOTIFICATION (sub, notifications[0 ]);
293
+ }
294
+
295
+ DOCTEST_SUBCASE (" subtree filter" )
296
+ {
297
+ libyang::CreatedNodes createdNodes;
298
+
299
+ DOCTEST_SUBCASE (" filter a node" )
300
+ {
301
+ DOCTEST_SUBCASE (" XML" )
302
+ {
303
+ createdNodes = sess.getContext ().newPath2 (
304
+ " /ietf-subscribed-notifications:establish-subscription/stream-subtree-filter" ,
305
+ libyang::XML{" <ping xmlns='urn:ietf:params:xml:ns:yang:test_module' />" });
306
+ }
307
+
308
+ DOCTEST_SUBCASE (" JSON" )
309
+ {
310
+ createdNodes = sess.getContext ().newPath2 (
311
+ " /ietf-subscribed-notifications:establish-subscription/stream-subtree-filter" ,
312
+ libyang::JSON{R"( {"test_module:ping": {}})" });
313
+ }
314
+
315
+ REQUIRE_NAMED_NOTIFICATION (sub, notifications[0 ]);
316
+ }
317
+
318
+ DOCTEST_SUBCASE (" filter more top level nodes" )
319
+ {
320
+ DOCTEST_SUBCASE (" XML" )
321
+ {
322
+ createdNodes = sess.getContext ().newPath2 (
323
+ " /ietf-subscribed-notifications:establish-subscription/stream-subtree-filter" ,
324
+ libyang::XML{" <ping xmlns='urn:ietf:params:xml:ns:yang:test_module' />"
325
+ " <silent-ping xmlns='urn:ietf:params:xml:ns:yang:test_module' />" });
326
+ }
327
+
328
+ DOCTEST_SUBCASE (" JSON" )
329
+ {
330
+ createdNodes = sess.getContext ().newPath2 (
331
+ " /ietf-subscribed-notifications:establish-subscription/stream-subtree-filter" ,
332
+ libyang::JSON{R"( {
333
+ "test_module:ping": {},
334
+ "test_module:silent-ping": {}
335
+ })" });
336
+ }
337
+
338
+ REQUIRE_NAMED_NOTIFICATION (sub, notifications[0 ]);
339
+ REQUIRE_NAMED_NOTIFICATION (sub, notifications[1 ]);
340
+ }
341
+
342
+ DOCTEST_SUBCASE (" empty filter selects nothing" )
343
+ {
344
+ createdNodes = sess.getContext ().newPath2 (
345
+ " /ietf-subscribed-notifications:establish-subscription/stream-subtree-filter" ,
346
+ std::nullopt);
347
+ }
348
+
349
+ sub = sess.subscribeNotifications (createdNodes.createdNode ->asAny ());
350
+ }
351
+
352
+ CLIENT_SEND_NOTIFICATION (notifications[0 ]);
353
+ CLIENT_SEND_NOTIFICATION (notifications[1 ]);
354
+
355
+ // read as many notifications as we expect
356
+ for (size_t i = 0 ; i < expectations.size (); ++i) {
357
+ READ_NOTIFICATION_BLOCKING (*sub);
358
+ }
359
+
360
+ sub->terminate ();
361
+
362
+ // ensure no more notifications were sent
363
+ REQUIRE_PIPE_HANGUP (*sub);
364
+ }
279
365
}
280
366
281
367
DOCTEST_SUBCASE (" YANG Push on change" )
@@ -285,9 +371,73 @@ TEST_CASE("Dynamic subscriptions")
285
371
* between writing to sysrepo and reading the notifications.
286
372
*/
287
373
288
- auto sub = sess.yangPushOnChange (std::nullopt, std::nullopt, sysrepo::SyncOnStart::Yes);
374
+ DOCTEST_SUBCASE (" Filters" )
375
+ {
376
+ std::optional<sysrepo::DynamicSubscription> sub;
377
+
378
+ DOCTEST_SUBCASE (" XPath filter" )
379
+ {
380
+ sub = sess.yangPushOnChange (" /test_module:leafInt32 | /test_module:popelnice/content/trash[name='asd']" );
381
+ }
382
+
383
+ DOCTEST_SUBCASE (" Subtree filter" )
384
+ {
385
+ auto createdNodes = sess.getContext ().newPath2 (
386
+ " /ietf-subscribed-notifications:establish-subscription/ietf-yang-push:datastore-subtree-filter" ,
387
+ libyang::XML{" <leafInt32 xmlns='http://example.com/' />"
388
+ " <popelnice xmlns='http://example.com/'><content><trash><name>asd</name></trash></content></popelnice>" });
389
+ sub = sess.yangPushOnChange (createdNodes.createdNode ->asAny ());
390
+ }
391
+
392
+ client.setItem (" /test_module:leafInt32" , " 42" );
393
+ client.setItem (" /test_module:popelnice/s" , " asd" );
394
+ client.setItem (" /test_module:popelnice/content/trash[name='asd']" , std::nullopt);
395
+ client.applyChanges ();
289
396
290
- REQUIRE_YANG_PUSH_UPDATE (sub, R"( {
397
+ client.deleteItem (" /test_module:popelnice/s" );
398
+ client.applyChanges ();
399
+
400
+ REQUIRE_YANG_PUSH_UPDATE (*sub, R"( {
401
+ "ietf-yang-push:push-change-update": {
402
+ "datastore-changes": {
403
+ "yang-patch": {
404
+ "patch-id": "patch-1",
405
+ "edit": [
406
+ {
407
+ "edit-id": "edit-1",
408
+ "operation": "create",
409
+ "target": "/test_module:leafInt32",
410
+ "value": {
411
+ "test_module:leafInt32": 42
412
+ }
413
+ },
414
+ {
415
+ "edit-id": "edit-2",
416
+ "operation": "create",
417
+ "target": "/test_module:popelnice/content/trash[name='asd']",
418
+ "value": {
419
+ "test_module:trash": {
420
+ "name": "asd"
421
+ }
422
+ }
423
+ }
424
+ ]
425
+ }
426
+ }
427
+ }
428
+ }
429
+ )" );
430
+ READ_YANG_PUSH_UPDATE (*sub);
431
+
432
+ sub->terminate ();
433
+ REQUIRE_PIPE_HANGUP (*sub);
434
+ }
435
+
436
+ DOCTEST_SUBCASE (" Sync on start" )
437
+ {
438
+ auto sub = sess.yangPushOnChange (std::nullopt, std::nullopt, sysrepo::SyncOnStart::Yes);
439
+
440
+ REQUIRE_YANG_PUSH_UPDATE (sub, R"( {
291
441
"ietf-yang-push:push-update": {
292
442
"datastore-contents": {
293
443
"test_module:values": [
@@ -298,14 +448,14 @@ TEST_CASE("Dynamic subscriptions")
298
448
}
299
449
}
300
450
)" );
301
- READ_YANG_PUSH_UPDATE (sub);
451
+ READ_YANG_PUSH_UPDATE (sub);
302
452
303
- client.setItem (" /test_module:leafInt32" , " 123" );
304
- client.setItem (" /test_module:values[.='5']" , std::nullopt);
305
- client.deleteItem (" /test_module:values[.='3']" );
306
- client.applyChanges ();
453
+ client.setItem (" /test_module:leafInt32" , " 123" );
454
+ client.setItem (" /test_module:values[.='5']" , std::nullopt);
455
+ client.deleteItem (" /test_module:values[.='3']" );
456
+ client.applyChanges ();
307
457
308
- REQUIRE_YANG_PUSH_UPDATE (sub, R"( {
458
+ REQUIRE_YANG_PUSH_UPDATE (sub, R"( {
309
459
"ietf-yang-push:push-change-update": {
310
460
"datastore-changes": {
311
461
"yang-patch": {
@@ -342,10 +492,11 @@ TEST_CASE("Dynamic subscriptions")
342
492
}
343
493
}
344
494
)" );
345
- READ_YANG_PUSH_UPDATE (sub);
495
+ READ_YANG_PUSH_UPDATE (sub);
346
496
347
- sub.terminate ();
348
- REQUIRE_PIPE_HANGUP (sub);
497
+ sub.terminate ();
498
+ REQUIRE_PIPE_HANGUP (sub);
499
+ }
349
500
}
350
501
351
502
DOCTEST_SUBCASE (" YANG Push periodic" )
0 commit comments