Migrate yang-data-tree-ri to JUnit5
[yangtools.git] / data / yang-data-tree-ri / src / test / java / org / opendaylight / yangtools / yang / data / tree / impl / Bug4454Test.java
1 /*
2  * Copyright (c) 2015 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.data.tree.impl;
9
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;
15
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;
42
43 class Bug4454Test {
44
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",
47                     "master-container");
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");
55
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();
66
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"))
70         .build();
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"))
74         .build();
75     private final MapEntryNode fooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
76             "foo");
77     private final MapEntryNode barEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
78             "bar");
79     private final MapEntryNode bazEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
80             "baz");
81     private final SystemMapNode mapNodeBazFuzWithNodes = ImmutableNodes.mapNodeBuilder()
82             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
83             .withChild(bazEntryNode).withChild(bazEntryNodeWithValue).withChild(fooEntryNode)
84             .build();
85     private final SystemMapNode mapNodeFooWithNodes = ImmutableNodes.mapNodeBuilder()
86             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
87             .withChild(fooEntryNode).withChild(fooEntryNodeWithValue).withChild(barEntryNode).withChild(bazEntryNode)
88             .build();
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();
95
96     private static EffectiveModelContext schemaContext;
97
98     private DataTree inMemoryDataTree;
99
100     @BeforeAll
101     static void beforeClass() {
102         schemaContext = YangParserTestUtils.parseYang("""
103             module Bug4454Test {
104               yang-version 1;
105               namespace "urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model";
106               prefix "list-constraints-validation";
107
108               revision "2015-02-02" {
109                 description "Initial revision.";
110               }
111
112               container master-container {
113                 list min-max-list {
114                   min-elements 1;
115                   max-elements 3;
116                   key "min-max-key-leaf";
117                   leaf min-max-key-leaf {
118                     type string;
119                   }
120                   leaf min-max-value-leaf {
121                     type string;
122                   }
123                 }
124
125                 list min-max-list-no-minmax {
126                   key "min-max-key-leaf";
127                   leaf min-max-key-leaf {
128                     type string;
129                   }
130                 }
131
132                 leaf-list min-max-leaf-list {
133                   min-elements 0;
134                   max-elements 10;
135                   type string;
136                 }
137               }
138
139               container presence {
140                 presence "anchor point";
141
142                 list min-max-list {
143                   min-elements 2;
144                   max-elements 3;
145
146                   key "min-max-key-leaf";
147
148                   leaf min-max-key-leaf {
149                     type string;
150                   }
151                 }
152               }
153             }""");
154     }
155
156     @AfterAll
157     static void afterClass() {
158         schemaContext = null;
159     }
160
161     @BeforeEach
162     void prepare() throws DataValidationFailedException {
163         inMemoryDataTree =  new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
164             schemaContext);
165         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
166         final var modificationTree = initialDataTreeSnapshot.newModification();
167
168         modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.containerNode(MASTER_CONTAINER_QNAME));
169         modificationTree.ready();
170         inMemoryDataTree.commit(inMemoryDataTree.prepare(modificationTree));
171     }
172
173     @Test
174     void minMaxListDeleteWriteTest() throws DataValidationFailedException {
175         final var modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
176
177         final var key = new HashMap<QName, Object>();
178         key.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
179
180         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , key);
181
182         final var minMaxLeafFoo = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
183                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2).build();
184
185         key.clear();
186         key.put(MIN_MAX_KEY_LEAF_QNAME, "NON-EXISTING-LEAF");
187
188         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, key);
189
190         final var minMaxLeafNel = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
191                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2).build();
192
193         final var keyTemp = new HashMap<QName, Object>();
194         keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "baz");
195
196         var mapEntryPathTest = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , keyTemp);
197
198         final var pathToBaz = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
199                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTest).node(MIN_MAX_VALUE_LEAF_QNAME).build();
200
201         keyTemp.clear();
202         keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "bar");
203
204         mapEntryPathTest = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , keyTemp);
205
206         final var pathToBar = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
207                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTest).node(MIN_MAX_VALUE_LEAF_QNAME).build();
208
209         keyTemp.clear();
210         keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
211
212         final var mapEntryPathTestKey = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
213             keyTemp);
214
215         final var pathToKeyFoo = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
216                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTestKey).node(MIN_MAX_KEY_LEAF_QNAME).build();
217
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");
222
223         assertFalse(inMemoryDataTree.toString().contains("list"));
224
225         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
226         var minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
227         assertFalse(minMaxListRead.isPresent());
228
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);
240
241         modificationTree1.ready();
242         inMemoryDataTree.validate(modificationTree1);
243         final var prepare = inMemoryDataTree.prepare(modificationTree1);
244         inMemoryDataTree.commit(prepare);
245
246         final var test = inMemoryDataTree.takeSnapshot();
247         testLoop(test, "bar", "test");
248
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);
254
255         tempMod.ready();
256         inMemoryDataTree.validate(tempMod);
257         final var prepare1 = inMemoryDataTree.prepare(tempMod);
258         inMemoryDataTree.commit(prepare1);
259
260         final var test1 = inMemoryDataTree.takeSnapshot();
261         testLoop(test1, "bar", "test1");
262
263         final var tempMod1 = test1.newModification();
264         tempMod1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
265
266         tempMod1.ready();
267         inMemoryDataTree.validate(tempMod1);
268         final var prepare2 = inMemoryDataTree.prepare(tempMod1);
269         inMemoryDataTree.commit(prepare2);
270
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());
275
276         final var tempMod2 = test2.newModification();
277         tempMod2.write(MIN_MAX_LIST_PATH, mapNodeBaz);
278         tempMod2.write(pathToBaz, newNode2);
279
280         tempMod2.ready();
281         inMemoryDataTree.validate(tempMod2);
282         final var prepare3 = inMemoryDataTree.prepare(tempMod2);
283         inMemoryDataTree.commit(prepare3);
284
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"));
290
291         final var tempMod3 = test3.newModification();
292         tempMod3.merge(MIN_MAX_LIST_PATH, mapNodeBar);
293         tempMod3.merge(pathToBar, newNode1);
294
295         tempMod3.ready();
296         inMemoryDataTree.validate(tempMod3);
297         final var prepare4 = inMemoryDataTree.prepare(tempMod3);
298         inMemoryDataTree.commit(prepare4);
299
300         final var test4 = inMemoryDataTree.takeSnapshot();
301         testLoop(test4, "test1", "test2");
302     }
303
304     @Test
305     void minMaxLeafListPass() throws DataValidationFailedException {
306         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
307
308         final var barPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "bar");
309         final var gooPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "goo");
310
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();
317
318         final var fooLeafSetNode = Builders.leafSetBuilder()
319                 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
320                 .withChildValue("foo")
321                 .build();
322
323         modificationTree.write(MIN_MAX_LEAF_LIST_PATH, fooLeafSetNode);
324         modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
325         modificationTree.ready();
326
327         inMemoryDataTree.validate(modificationTree);
328         final var prepare1 = inMemoryDataTree.prepare(modificationTree);
329         inMemoryDataTree.commit(prepare1);
330
331         final var test1 = inMemoryDataTree.takeSnapshot();
332
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);
336         tempMod1.ready();
337
338         inMemoryDataTree.validate(tempMod1);
339         final var prepare2 = inMemoryDataTree.prepare(tempMod1);
340         inMemoryDataTree.commit(prepare2);
341
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());
349     }
350
351     @Test
352     void minMaxListDeleteTest() throws DataValidationFailedException {
353         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
354
355
356         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
357             MIN_MAX_KEY_LEAF_QNAME, "foo");
358
359         final var minMaxLeafFoo = MASTER_CONTAINER_PATH
360                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
361
362         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
363
364         final var minMaxLeafBar = MASTER_CONTAINER_PATH
365                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
366
367         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz");
368
369         final var minMaxLeafBaz = MASTER_CONTAINER_PATH
370                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
371
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);
378
379         modificationTree.ready();
380
381         inMemoryDataTree.validate(modificationTree);
382         final var prepare = inMemoryDataTree.prepare(modificationTree);
383         inMemoryDataTree.commit(prepare);
384
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());
389     }
390
391     @Test
392     void minMaxListDeleteExceptionTest() throws DataValidationFailedException {
393         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
394
395         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
396             MIN_MAX_KEY_LEAF_QNAME, "foo");
397
398         final var minMaxLeafFoo = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
399
400         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
401
402         final var minMaxLeafBar = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
403
404         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz");
405
406         final var minMaxLeafBaz = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
407
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);
415
416         try {
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) {
421             assertEquals("""
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());
425         }
426     }
427
428     @Test
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();
435
436         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
437
438         final var key = new HashMap<QName, Object>();
439         key.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
440
441         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, key);
442
443         final var minMaxLeafFoo = MASTER_CONTAINER_PATH
444                 .node(MIN_MAX_LIST_QNAME_NO_MINMAX).node(mapEntryPath2);
445
446         key.clear();
447         key.put(MIN_MAX_KEY_LEAF_QNAME, "non-existing-leaf");
448
449         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, key);
450
451         final var minMaxLeafNel = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
452                 .node(MIN_MAX_LIST_QNAME_NO_MINMAX).node(mapEntryPath2).build();
453
454         modificationTree.write(MIN_MAX_LIST_NO_MINMAX_PATH, mapNode1);
455         modificationTree.delete(minMaxLeafFoo);
456         modificationTree.delete(minMaxLeafNel);
457
458         modificationTree.ready();
459
460         inMemoryDataTree.validate(modificationTree);
461         final var prepare = inMemoryDataTree.prepare(modificationTree);
462         inMemoryDataTree.commit(prepare);
463
464         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
465         final var minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_NO_MINMAX_PATH);
466
467         // Empty list should have disappeared
468         assertFalse(minMaxListRead.isPresent());
469     }
470
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());
475
476         for (var collectionChild : (Collection<?>) minMaxListRead.orElseThrow().body()) {
477             if (collectionChild.toString().contains(first)) {
478                 assertTrue(collectionChild.toString().contains(first));
479             } else {
480                 assertTrue(collectionChild.toString().contains(second));
481             }
482         }
483     }
484 }