Rename opendaylight.mdsal.binding.runtime.spi
[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.spi.node.ImmutableNodes;
34 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
35 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
36 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeSnapshot;
37 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
38 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
39 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
40 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
41
42 class Bug4454Test {
43
44     private static final QName MASTER_CONTAINER_QNAME = QName
45             .create("urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model", "2015-02-02",
46                     "master-container");
47     private static final QName MIN_MAX_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-list");
48     private static final QName MIN_MAX_LEAF_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-leaf-list");
49     private static final QName MIN_MAX_LIST_QNAME_NO_MINMAX = QName
50             .create(MASTER_CONTAINER_QNAME, "min-max-list-no-minmax");
51     private static final QName MIN_MAX_KEY_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-key-leaf");
52     private static final QName MIN_MAX_VALUE_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-value-leaf");
53     private static final QName PRESENCE_QNAME = QName.create(MASTER_CONTAINER_QNAME, "presence");
54
55     private static final YangInstanceIdentifier MASTER_CONTAINER_PATH =
56         YangInstanceIdentifier.of(MASTER_CONTAINER_QNAME);
57     private static final YangInstanceIdentifier MIN_MAX_LIST_PATH =
58         YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LIST_QNAME).build();
59     private static final YangInstanceIdentifier PRESENCE_PATH = YangInstanceIdentifier.of(PRESENCE_QNAME);
60     private static final YangInstanceIdentifier PRESENCE_MIN_MAX_LIST_PATH = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME);
61     private static final YangInstanceIdentifier MIN_MAX_LIST_NO_MINMAX_PATH =
62         YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LIST_QNAME_NO_MINMAX).build();
63     private static final YangInstanceIdentifier MIN_MAX_LEAF_LIST_PATH =
64         YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LEAF_LIST_QNAME).build();
65
66     private final MapEntryNode fooEntryNodeWithValue = ImmutableNodes.newMapEntryBuilder()
67         .withNodeIdentifier(NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "foo"))
68         .withChild(ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "footest"))
69         .build();
70     private final MapEntryNode bazEntryNodeWithValue = ImmutableNodes.newMapEntryBuilder()
71         .withNodeIdentifier(NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz"))
72         .withChild(ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "baztest"))
73         .build();
74     private final MapEntryNode fooEntryNode = ImmutableNodes.newMapEntryBuilder()
75         .withNodeIdentifier(NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "foo"))
76         .withChild(ImmutableNodes.leafNode(MIN_MAX_KEY_LEAF_QNAME, "foo"))
77         .build();
78     private final MapEntryNode barEntryNode = ImmutableNodes.newMapEntryBuilder()
79         .withNodeIdentifier(NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar"))
80         .withChild(ImmutableNodes.leafNode(MIN_MAX_KEY_LEAF_QNAME, "bar"))
81         .build();
82     private final MapEntryNode bazEntryNode = ImmutableNodes.newMapEntryBuilder()
83         .withNodeIdentifier(NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz"))
84         .withChild(ImmutableNodes.leafNode(MIN_MAX_KEY_LEAF_QNAME, "baz"))
85         .build();
86     private final SystemMapNode mapNodeBazFuzWithNodes = ImmutableNodes.newSystemMapBuilder()
87             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
88             .withChild(bazEntryNode).withChild(bazEntryNodeWithValue).withChild(fooEntryNode)
89             .build();
90     private final SystemMapNode mapNodeFooWithNodes = ImmutableNodes.newSystemMapBuilder()
91             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
92             .withChild(fooEntryNode).withChild(fooEntryNodeWithValue).withChild(barEntryNode).withChild(bazEntryNode)
93             .build();
94     private final SystemMapNode mapNodeBar = ImmutableNodes.newSystemMapBuilder()
95             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
96             .withChild(barEntryNode).build();
97     private final SystemMapNode mapNodeBaz = ImmutableNodes.newSystemMapBuilder()
98             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
99             .withChild(bazEntryNode).build();
100
101     private static EffectiveModelContext schemaContext;
102
103     private DataTree inMemoryDataTree;
104
105     @BeforeAll
106     static void beforeClass() {
107         schemaContext = YangParserTestUtils.parseYang("""
108             module Bug4454Test {
109               yang-version 1;
110               namespace "urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model";
111               prefix "list-constraints-validation";
112
113               revision "2015-02-02" {
114                 description "Initial revision.";
115               }
116
117               container master-container {
118                 list min-max-list {
119                   min-elements 1;
120                   max-elements 3;
121                   key "min-max-key-leaf";
122                   leaf min-max-key-leaf {
123                     type string;
124                   }
125                   leaf min-max-value-leaf {
126                     type string;
127                   }
128                 }
129
130                 list min-max-list-no-minmax {
131                   key "min-max-key-leaf";
132                   leaf min-max-key-leaf {
133                     type string;
134                   }
135                 }
136
137                 leaf-list min-max-leaf-list {
138                   min-elements 0;
139                   max-elements 10;
140                   type string;
141                 }
142               }
143
144               container presence {
145                 presence "anchor point";
146
147                 list min-max-list {
148                   min-elements 2;
149                   max-elements 3;
150
151                   key "min-max-key-leaf";
152
153                   leaf min-max-key-leaf {
154                     type string;
155                   }
156                 }
157               }
158             }""");
159     }
160
161     @AfterAll
162     static void afterClass() {
163         schemaContext = null;
164     }
165
166     @BeforeEach
167     void prepare() throws DataValidationFailedException {
168         inMemoryDataTree =  new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
169             schemaContext);
170         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
171         final var modificationTree = initialDataTreeSnapshot.newModification();
172
173         modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.newContainerBuilder()
174             .withNodeIdentifier(new NodeIdentifier(MASTER_CONTAINER_QNAME))
175             .build());
176         modificationTree.ready();
177         inMemoryDataTree.commit(inMemoryDataTree.prepare(modificationTree));
178     }
179
180     @Test
181     void minMaxListDeleteWriteTest() throws DataValidationFailedException {
182         final var modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
183
184         final var key = new HashMap<QName, Object>();
185         key.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
186
187         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , key);
188
189         final var minMaxLeafFoo = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
190                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2).build();
191
192         key.clear();
193         key.put(MIN_MAX_KEY_LEAF_QNAME, "NON-EXISTING-LEAF");
194
195         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, key);
196
197         final var minMaxLeafNel = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
198                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2).build();
199
200         final var keyTemp = new HashMap<QName, Object>();
201         keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "baz");
202
203         var mapEntryPathTest = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , keyTemp);
204
205         final var pathToBaz = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
206                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTest).node(MIN_MAX_VALUE_LEAF_QNAME).build();
207
208         keyTemp.clear();
209         keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "bar");
210
211         mapEntryPathTest = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , keyTemp);
212
213         final var pathToBar = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
214                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTest).node(MIN_MAX_VALUE_LEAF_QNAME).build();
215
216         keyTemp.clear();
217         keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
218
219         final var mapEntryPathTestKey = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
220             keyTemp);
221
222         final var pathToKeyFoo = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
223                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTestKey).node(MIN_MAX_KEY_LEAF_QNAME).build();
224
225         final var newNode = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test");
226         final var newNode1 = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test1");
227         final var newNode2 = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test2");
228         final var newNodekey = ImmutableNodes.leafNode(MIN_MAX_KEY_LEAF_QNAME, "foo");
229
230         assertFalse(inMemoryDataTree.toString().contains("list"));
231
232         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
233         var minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
234         assertFalse(minMaxListRead.isPresent());
235
236         modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
237         modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
238         modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
239         modificationTree1.merge(MIN_MAX_LIST_PATH, mapNodeBar);
240         modificationTree1.merge(MIN_MAX_LIST_PATH, mapNodeBaz);
241         modificationTree1.write(pathToKeyFoo, newNodekey);
242         modificationTree1.write(pathToBaz, newNode2);
243         modificationTree1.write(pathToBaz, newNode1);
244         modificationTree1.write(pathToBaz, newNode);
245         modificationTree1.delete(minMaxLeafFoo);
246         modificationTree1.delete(minMaxLeafNel);
247
248         modificationTree1.ready();
249         inMemoryDataTree.validate(modificationTree1);
250         final var prepare = inMemoryDataTree.prepare(modificationTree1);
251         inMemoryDataTree.commit(prepare);
252
253         final var test = inMemoryDataTree.takeSnapshot();
254         testLoop(test, "bar", "test");
255
256         final var tempMod = test.newModification();
257         tempMod.write(pathToBaz, newNode2);
258         tempMod.write(pathToBaz, newNode1);
259         tempMod.merge(pathToBaz, newNode2);
260         tempMod.write(pathToBaz, newNode1);
261
262         tempMod.ready();
263         inMemoryDataTree.validate(tempMod);
264         final var prepare1 = inMemoryDataTree.prepare(tempMod);
265         inMemoryDataTree.commit(prepare1);
266
267         final var test1 = inMemoryDataTree.takeSnapshot();
268         testLoop(test1, "bar", "test1");
269
270         final var tempMod1 = test1.newModification();
271         tempMod1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
272
273         tempMod1.ready();
274         inMemoryDataTree.validate(tempMod1);
275         final var prepare2 = inMemoryDataTree.prepare(tempMod1);
276         inMemoryDataTree.commit(prepare2);
277
278         final var test2 = inMemoryDataTree.takeSnapshot();
279         minMaxListRead = test2.readNode(MIN_MAX_LIST_PATH);
280         assertTrue(minMaxListRead.isPresent());
281         assertEquals(3, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
282
283         final var tempMod2 = test2.newModification();
284         tempMod2.write(MIN_MAX_LIST_PATH, mapNodeBaz);
285         tempMod2.write(pathToBaz, newNode2);
286
287         tempMod2.ready();
288         inMemoryDataTree.validate(tempMod2);
289         final var prepare3 = inMemoryDataTree.prepare(tempMod2);
290         inMemoryDataTree.commit(prepare3);
291
292         final var test3 = inMemoryDataTree.takeSnapshot();
293         minMaxListRead = test3.readNode(MIN_MAX_LIST_PATH);
294         assertTrue(minMaxListRead.isPresent());
295         assertEquals(1, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
296         assertTrue(minMaxListRead.orElseThrow().body().toString().contains("test2"));
297
298         final var tempMod3 = test3.newModification();
299         tempMod3.merge(MIN_MAX_LIST_PATH, mapNodeBar);
300         tempMod3.merge(pathToBar, newNode1);
301
302         tempMod3.ready();
303         inMemoryDataTree.validate(tempMod3);
304         final var prepare4 = inMemoryDataTree.prepare(tempMod3);
305         inMemoryDataTree.commit(prepare4);
306
307         final var test4 = inMemoryDataTree.takeSnapshot();
308         testLoop(test4, "test1", "test2");
309     }
310
311     @Test
312     void minMaxLeafListPass() throws DataValidationFailedException {
313         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
314
315         final var barPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "bar");
316         final var gooPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "goo");
317
318         final var barLeafSetEntry = ImmutableNodes.leafSetEntry(barPath);
319         final var gooLeafSetEntry = ImmutableNodes.leafSetEntry(gooPath);
320
321         final var fooLeafSetNode = ImmutableNodes.newSystemLeafSetBuilder()
322                 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
323                 .withChildValue("foo")
324                 .build();
325
326         modificationTree.write(MIN_MAX_LEAF_LIST_PATH, fooLeafSetNode);
327         modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
328         modificationTree.ready();
329
330         inMemoryDataTree.validate(modificationTree);
331         final var prepare1 = inMemoryDataTree.prepare(modificationTree);
332         inMemoryDataTree.commit(prepare1);
333
334         final var test1 = inMemoryDataTree.takeSnapshot();
335
336         final var tempMod1 = test1.newModification();
337         tempMod1.write(MIN_MAX_LEAF_LIST_PATH.node(gooPath), gooLeafSetEntry);
338         tempMod1.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
339         tempMod1.ready();
340
341         inMemoryDataTree.validate(tempMod1);
342         final var prepare2 = inMemoryDataTree.prepare(tempMod1);
343         inMemoryDataTree.commit(prepare2);
344
345         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
346         final var masterContainer = snapshotAfterCommit.readNode(MASTER_CONTAINER_PATH);
347         assertTrue(masterContainer.isPresent());
348         final var leafList = ((DistinctNodeContainer) masterContainer.orElseThrow())
349                 .findChildByArg(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME));
350         assertTrue(leafList.isPresent());
351         assertEquals(3, ((Optional<NormalizedNodeContainer<?>>) leafList).orElseThrow().size());
352     }
353
354     @Test
355     void minMaxListDeleteTest() throws DataValidationFailedException {
356         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
357
358
359         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
360             MIN_MAX_KEY_LEAF_QNAME, "foo");
361
362         final var minMaxLeafFoo = MASTER_CONTAINER_PATH
363                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
364
365         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
366
367         final var minMaxLeafBar = MASTER_CONTAINER_PATH
368                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
369
370         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz");
371
372         final var minMaxLeafBaz = MASTER_CONTAINER_PATH
373                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
374
375         modificationTree.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
376         modificationTree.merge(MIN_MAX_LIST_PATH, mapNodeBar);
377         modificationTree.merge(MIN_MAX_LIST_PATH, mapNodeBaz);
378         modificationTree.delete(minMaxLeafFoo);
379         modificationTree.delete(minMaxLeafBar);
380         modificationTree.delete(minMaxLeafBaz);
381
382         modificationTree.ready();
383
384         inMemoryDataTree.validate(modificationTree);
385         final var prepare = inMemoryDataTree.prepare(modificationTree);
386         inMemoryDataTree.commit(prepare);
387
388         // Empty list should have disappeared, along with the container, as we are not enforcing root
389         final var data = inMemoryDataTree.takeSnapshot().readNode(YangInstanceIdentifier.of()).orElseThrow();
390         assertInstanceOf(ContainerNode.class, data);
391         assertEquals(0, ((ContainerNode) data).size());
392     }
393
394     @Test
395     void minMaxListDeleteExceptionTest() throws DataValidationFailedException {
396         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
397
398         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
399             MIN_MAX_KEY_LEAF_QNAME, "foo");
400
401         final var minMaxLeafFoo = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
402
403         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
404
405         final var minMaxLeafBar = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
406
407         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz");
408
409         final var minMaxLeafBaz = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
410
411         modificationTree.write(PRESENCE_PATH,
412             ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(PRESENCE_QNAME)).build());
413         modificationTree.write(PRESENCE_MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
414         modificationTree.merge(PRESENCE_MIN_MAX_LIST_PATH, mapNodeBar);
415         modificationTree.merge(PRESENCE_MIN_MAX_LIST_PATH, mapNodeBaz);
416         modificationTree.delete(minMaxLeafFoo);
417         modificationTree.delete(minMaxLeafBar);
418         modificationTree.delete(minMaxLeafBaz);
419
420         try {
421             // Unlike minMaxListDeleteTest(), presence container enforces the list to be present
422             modificationTree.ready();
423             fail("Should have failed with IAE");
424         } catch (IllegalArgumentException e) {
425             assertEquals("""
426                 Node (urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model?revision=2015-02-02)\
427                 presence is missing mandatory descendant /(urn:opendaylight:params:xml:ns:yang:list-constraints-\
428                 validation-test-model?revision=2015-02-02)min-max-list""", e.getMessage());
429         }
430     }
431
432     @Test
433     void minMaxListNoMinMaxDeleteTest() throws DataValidationFailedException {
434         final var fooEntryNoMinMaxNode = ImmutableNodes.newMapEntryBuilder()
435             .withNodeIdentifier(
436                 NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, MIN_MAX_KEY_LEAF_QNAME, "foo"))
437             .withChild(ImmutableNodes.leafNode(MIN_MAX_KEY_LEAF_QNAME, "foo"))
438             .build();
439         final var mapNode1 = ImmutableNodes.newSystemMapBuilder()
440                 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME_NO_MINMAX))
441                 .withChild(fooEntryNoMinMaxNode).build();
442
443         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
444
445         final var key = new HashMap<QName, Object>();
446         key.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
447
448         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, key);
449
450         final var minMaxLeafFoo = MASTER_CONTAINER_PATH
451                 .node(MIN_MAX_LIST_QNAME_NO_MINMAX).node(mapEntryPath2);
452
453         key.clear();
454         key.put(MIN_MAX_KEY_LEAF_QNAME, "non-existing-leaf");
455
456         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, key);
457
458         final var minMaxLeafNel = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
459                 .node(MIN_MAX_LIST_QNAME_NO_MINMAX).node(mapEntryPath2).build();
460
461         modificationTree.write(MIN_MAX_LIST_NO_MINMAX_PATH, mapNode1);
462         modificationTree.delete(minMaxLeafFoo);
463         modificationTree.delete(minMaxLeafNel);
464
465         modificationTree.ready();
466
467         inMemoryDataTree.validate(modificationTree);
468         final var prepare = inMemoryDataTree.prepare(modificationTree);
469         inMemoryDataTree.commit(prepare);
470
471         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
472         final var minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_NO_MINMAX_PATH);
473
474         // Empty list should have disappeared
475         assertFalse(minMaxListRead.isPresent());
476     }
477
478     private static void testLoop(final DataTreeSnapshot snapshot, final String first, final String second) {
479         final var minMaxListRead = snapshot.readNode(MIN_MAX_LIST_PATH);
480         assertTrue(minMaxListRead.isPresent());
481         assertEquals(2, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
482
483         for (var collectionChild : (Collection<?>) minMaxListRead.orElseThrow().body()) {
484             if (collectionChild.toString().contains(first)) {
485                 assertTrue(collectionChild.toString().contains(first));
486             } else {
487                 assertTrue(collectionChild.toString().contains(second));
488             }
489         }
490     }
491 }