2 * Copyright (c) 2015 Pantheon Technologies s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.data.tree.impl;
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertFalse;
12 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
13 import static org.junit.jupiter.api.Assertions.assertTrue;
14 import static org.junit.jupiter.api.Assertions.fail;
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.Optional;
19 import org.junit.jupiter.api.AfterAll;
20 import org.junit.jupiter.api.BeforeAll;
21 import org.junit.jupiter.api.BeforeEach;
22 import org.junit.jupiter.api.Test;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
28 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
30 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
32 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
33 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
34 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
35 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
36 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
37 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeSnapshot;
38 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
39 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
40 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
41 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
45 private static final QName MASTER_CONTAINER_QNAME = QName
46 .create("urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model", "2015-02-02",
48 private static final QName MIN_MAX_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-list");
49 private static final QName MIN_MAX_LEAF_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-leaf-list");
50 private static final QName MIN_MAX_LIST_QNAME_NO_MINMAX = QName
51 .create(MASTER_CONTAINER_QNAME, "min-max-list-no-minmax");
52 private static final QName MIN_MAX_KEY_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-key-leaf");
53 private static final QName MIN_MAX_VALUE_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-value-leaf");
54 private static final QName PRESENCE_QNAME = QName.create(MASTER_CONTAINER_QNAME, "presence");
56 private static final YangInstanceIdentifier MASTER_CONTAINER_PATH =
57 YangInstanceIdentifier.of(MASTER_CONTAINER_QNAME);
58 private static final YangInstanceIdentifier MIN_MAX_LIST_PATH =
59 YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LIST_QNAME).build();
60 private static final YangInstanceIdentifier PRESENCE_PATH = YangInstanceIdentifier.of(PRESENCE_QNAME);
61 private static final YangInstanceIdentifier PRESENCE_MIN_MAX_LIST_PATH = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME);
62 private static final YangInstanceIdentifier MIN_MAX_LIST_NO_MINMAX_PATH =
63 YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LIST_QNAME_NO_MINMAX).build();
64 private static final YangInstanceIdentifier MIN_MAX_LEAF_LIST_PATH =
65 YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LEAF_LIST_QNAME).build();
67 private final MapEntryNode fooEntryNodeWithValue = Builders.mapEntryBuilder()
68 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "foo"))
69 .withChild(ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "footest"))
71 private final MapEntryNode bazEntryNodeWithValue = Builders.mapEntryBuilder()
72 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz"))
73 .withChild(ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "baztest"))
75 private final MapEntryNode fooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
77 private final MapEntryNode barEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
79 private final MapEntryNode bazEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
81 private final SystemMapNode mapNodeBazFuzWithNodes = ImmutableNodes.mapNodeBuilder()
82 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
83 .withChild(bazEntryNode).withChild(bazEntryNodeWithValue).withChild(fooEntryNode)
85 private final SystemMapNode mapNodeFooWithNodes = ImmutableNodes.mapNodeBuilder()
86 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
87 .withChild(fooEntryNode).withChild(fooEntryNodeWithValue).withChild(barEntryNode).withChild(bazEntryNode)
89 private final SystemMapNode mapNodeBar = ImmutableNodes.mapNodeBuilder()
90 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
91 .withChild(barEntryNode).build();
92 private final SystemMapNode mapNodeBaz = ImmutableNodes.mapNodeBuilder()
93 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
94 .withChild(bazEntryNode).build();
96 private static EffectiveModelContext schemaContext;
98 private DataTree inMemoryDataTree;
101 static void beforeClass() {
102 schemaContext = YangParserTestUtils.parseYang("""
105 namespace "urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model";
106 prefix "list-constraints-validation";
108 revision "2015-02-02" {
109 description "Initial revision.";
112 container master-container {
116 key "min-max-key-leaf";
117 leaf min-max-key-leaf {
120 leaf min-max-value-leaf {
125 list min-max-list-no-minmax {
126 key "min-max-key-leaf";
127 leaf min-max-key-leaf {
132 leaf-list min-max-leaf-list {
140 presence "anchor point";
146 key "min-max-key-leaf";
148 leaf min-max-key-leaf {
157 static void afterClass() {
158 schemaContext = null;
162 void prepare() throws DataValidationFailedException {
163 inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
165 final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
166 final var modificationTree = initialDataTreeSnapshot.newModification();
168 modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.containerNode(MASTER_CONTAINER_QNAME));
169 modificationTree.ready();
170 inMemoryDataTree.commit(inMemoryDataTree.prepare(modificationTree));
174 void minMaxListDeleteWriteTest() throws DataValidationFailedException {
175 final var modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
177 final var key = new HashMap<QName, Object>();
178 key.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
180 var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , key);
182 final var minMaxLeafFoo = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
183 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2).build();
186 key.put(MIN_MAX_KEY_LEAF_QNAME, "NON-EXISTING-LEAF");
188 mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, key);
190 final var minMaxLeafNel = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
191 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2).build();
193 final var keyTemp = new HashMap<QName, Object>();
194 keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "baz");
196 var mapEntryPathTest = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , keyTemp);
198 final var pathToBaz = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
199 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTest).node(MIN_MAX_VALUE_LEAF_QNAME).build();
202 keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "bar");
204 mapEntryPathTest = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , keyTemp);
206 final var pathToBar = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
207 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTest).node(MIN_MAX_VALUE_LEAF_QNAME).build();
210 keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
212 final var mapEntryPathTestKey = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
215 final var pathToKeyFoo = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
216 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTestKey).node(MIN_MAX_KEY_LEAF_QNAME).build();
218 final var newNode = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test");
219 final var newNode1 = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test1");
220 final var newNode2 = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test2");
221 final var newNodekey = ImmutableNodes.leafNode(MIN_MAX_KEY_LEAF_QNAME, "foo");
223 assertFalse(inMemoryDataTree.toString().contains("list"));
225 final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
226 var minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
227 assertFalse(minMaxListRead.isPresent());
229 modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
230 modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
231 modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
232 modificationTree1.merge(MIN_MAX_LIST_PATH, mapNodeBar);
233 modificationTree1.merge(MIN_MAX_LIST_PATH, mapNodeBaz);
234 modificationTree1.write(pathToKeyFoo, newNodekey);
235 modificationTree1.write(pathToBaz, newNode2);
236 modificationTree1.write(pathToBaz, newNode1);
237 modificationTree1.write(pathToBaz, newNode);
238 modificationTree1.delete(minMaxLeafFoo);
239 modificationTree1.delete(minMaxLeafNel);
241 modificationTree1.ready();
242 inMemoryDataTree.validate(modificationTree1);
243 final var prepare = inMemoryDataTree.prepare(modificationTree1);
244 inMemoryDataTree.commit(prepare);
246 final var test = inMemoryDataTree.takeSnapshot();
247 testLoop(test, "bar", "test");
249 final var tempMod = test.newModification();
250 tempMod.write(pathToBaz, newNode2);
251 tempMod.write(pathToBaz, newNode1);
252 tempMod.merge(pathToBaz, newNode2);
253 tempMod.write(pathToBaz, newNode1);
256 inMemoryDataTree.validate(tempMod);
257 final var prepare1 = inMemoryDataTree.prepare(tempMod);
258 inMemoryDataTree.commit(prepare1);
260 final var test1 = inMemoryDataTree.takeSnapshot();
261 testLoop(test1, "bar", "test1");
263 final var tempMod1 = test1.newModification();
264 tempMod1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
267 inMemoryDataTree.validate(tempMod1);
268 final var prepare2 = inMemoryDataTree.prepare(tempMod1);
269 inMemoryDataTree.commit(prepare2);
271 final var test2 = inMemoryDataTree.takeSnapshot();
272 minMaxListRead = test2.readNode(MIN_MAX_LIST_PATH);
273 assertTrue(minMaxListRead.isPresent());
274 assertEquals(3, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
276 final var tempMod2 = test2.newModification();
277 tempMod2.write(MIN_MAX_LIST_PATH, mapNodeBaz);
278 tempMod2.write(pathToBaz, newNode2);
281 inMemoryDataTree.validate(tempMod2);
282 final var prepare3 = inMemoryDataTree.prepare(tempMod2);
283 inMemoryDataTree.commit(prepare3);
285 final var test3 = inMemoryDataTree.takeSnapshot();
286 minMaxListRead = test3.readNode(MIN_MAX_LIST_PATH);
287 assertTrue(minMaxListRead.isPresent());
288 assertEquals(1, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
289 assertTrue(minMaxListRead.orElseThrow().body().toString().contains("test2"));
291 final var tempMod3 = test3.newModification();
292 tempMod3.merge(MIN_MAX_LIST_PATH, mapNodeBar);
293 tempMod3.merge(pathToBar, newNode1);
296 inMemoryDataTree.validate(tempMod3);
297 final var prepare4 = inMemoryDataTree.prepare(tempMod3);
298 inMemoryDataTree.commit(prepare4);
300 final var test4 = inMemoryDataTree.takeSnapshot();
301 testLoop(test4, "test1", "test2");
305 void minMaxLeafListPass() throws DataValidationFailedException {
306 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
308 final var barPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "bar");
309 final var gooPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "goo");
311 final var barLeafSetEntry = Builders.leafSetEntryBuilder()
312 .withNodeIdentifier(barPath)
313 .withValue("bar").build();
314 final var gooLeafSetEntry = Builders.leafSetEntryBuilder()
315 .withNodeIdentifier(gooPath)
316 .withValue("goo").build();
318 final var fooLeafSetNode = Builders.leafSetBuilder()
319 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
320 .withChildValue("foo")
323 modificationTree.write(MIN_MAX_LEAF_LIST_PATH, fooLeafSetNode);
324 modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
325 modificationTree.ready();
327 inMemoryDataTree.validate(modificationTree);
328 final var prepare1 = inMemoryDataTree.prepare(modificationTree);
329 inMemoryDataTree.commit(prepare1);
331 final var test1 = inMemoryDataTree.takeSnapshot();
333 final var tempMod1 = test1.newModification();
334 tempMod1.write(MIN_MAX_LEAF_LIST_PATH.node(gooPath), gooLeafSetEntry);
335 tempMod1.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
338 inMemoryDataTree.validate(tempMod1);
339 final var prepare2 = inMemoryDataTree.prepare(tempMod1);
340 inMemoryDataTree.commit(prepare2);
342 final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
343 final var masterContainer = snapshotAfterCommit.readNode(MASTER_CONTAINER_PATH);
344 assertTrue(masterContainer.isPresent());
345 final var leafList = ((DistinctNodeContainer) masterContainer.orElseThrow())
346 .findChildByArg(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME));
347 assertTrue(leafList.isPresent());
348 assertEquals(3, ((Optional<NormalizedNodeContainer<?>>) leafList).orElseThrow().size());
352 void minMaxListDeleteTest() throws DataValidationFailedException {
353 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
356 var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
357 MIN_MAX_KEY_LEAF_QNAME, "foo");
359 final var minMaxLeafFoo = MASTER_CONTAINER_PATH
360 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
362 mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
364 final var minMaxLeafBar = MASTER_CONTAINER_PATH
365 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
367 mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz");
369 final var minMaxLeafBaz = MASTER_CONTAINER_PATH
370 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
372 modificationTree.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
373 modificationTree.merge(MIN_MAX_LIST_PATH, mapNodeBar);
374 modificationTree.merge(MIN_MAX_LIST_PATH, mapNodeBaz);
375 modificationTree.delete(minMaxLeafFoo);
376 modificationTree.delete(minMaxLeafBar);
377 modificationTree.delete(minMaxLeafBaz);
379 modificationTree.ready();
381 inMemoryDataTree.validate(modificationTree);
382 final var prepare = inMemoryDataTree.prepare(modificationTree);
383 inMemoryDataTree.commit(prepare);
385 // Empty list should have disappeared, along with the container, as we are not enforcing root
386 final var data = inMemoryDataTree.takeSnapshot().readNode(YangInstanceIdentifier.of()).orElseThrow();
387 assertInstanceOf(ContainerNode.class, data);
388 assertEquals(0, ((ContainerNode) data).size());
392 void minMaxListDeleteExceptionTest() throws DataValidationFailedException {
393 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
395 var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
396 MIN_MAX_KEY_LEAF_QNAME, "foo");
398 final var minMaxLeafFoo = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
400 mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
402 final var minMaxLeafBar = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
404 mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz");
406 final var minMaxLeafBaz = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
408 modificationTree.write(PRESENCE_PATH, ImmutableNodes.containerNode(PRESENCE_QNAME));
409 modificationTree.write(PRESENCE_MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
410 modificationTree.merge(PRESENCE_MIN_MAX_LIST_PATH, mapNodeBar);
411 modificationTree.merge(PRESENCE_MIN_MAX_LIST_PATH, mapNodeBaz);
412 modificationTree.delete(minMaxLeafFoo);
413 modificationTree.delete(minMaxLeafBar);
414 modificationTree.delete(minMaxLeafBaz);
417 // Unlike minMaxListDeleteTest(), presence container enforces the list to be present
418 modificationTree.ready();
419 fail("Should have failed with IAE");
420 } catch (IllegalArgumentException e) {
422 Node (urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model?revision=2015-02-02)\
423 presence is missing mandatory descendant /(urn:opendaylight:params:xml:ns:yang:list-constraints-\
424 validation-test-model?revision=2015-02-02)min-max-list""", e.getMessage());
429 void minMaxListNoMinMaxDeleteTest() throws DataValidationFailedException {
430 final var fooEntryNoMinMaxNode =
431 ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME_NO_MINMAX, MIN_MAX_KEY_LEAF_QNAME, "foo");
432 final var mapNode1 = ImmutableNodes.mapNodeBuilder()
433 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME_NO_MINMAX))
434 .withChild(fooEntryNoMinMaxNode).build();
436 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
438 final var key = new HashMap<QName, Object>();
439 key.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
441 var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, key);
443 final var minMaxLeafFoo = MASTER_CONTAINER_PATH
444 .node(MIN_MAX_LIST_QNAME_NO_MINMAX).node(mapEntryPath2);
447 key.put(MIN_MAX_KEY_LEAF_QNAME, "non-existing-leaf");
449 mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, key);
451 final var minMaxLeafNel = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
452 .node(MIN_MAX_LIST_QNAME_NO_MINMAX).node(mapEntryPath2).build();
454 modificationTree.write(MIN_MAX_LIST_NO_MINMAX_PATH, mapNode1);
455 modificationTree.delete(minMaxLeafFoo);
456 modificationTree.delete(minMaxLeafNel);
458 modificationTree.ready();
460 inMemoryDataTree.validate(modificationTree);
461 final var prepare = inMemoryDataTree.prepare(modificationTree);
462 inMemoryDataTree.commit(prepare);
464 final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
465 final var minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_NO_MINMAX_PATH);
467 // Empty list should have disappeared
468 assertFalse(minMaxListRead.isPresent());
471 private static void testLoop(final DataTreeSnapshot snapshot, final String first, final String second) {
472 final var minMaxListRead = snapshot.readNode(MIN_MAX_LIST_PATH);
473 assertTrue(minMaxListRead.isPresent());
474 assertEquals(2, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
476 for (var collectionChild : (Collection<?>) minMaxListRead.orElseThrow().body()) {
477 if (collectionChild.toString().contains(first)) {
478 assertTrue(collectionChild.toString().contains(first));
480 assertTrue(collectionChild.toString().contains(second));