5ac2356117e575eda4f1dcad7a16000fb36231fa
[yangtools.git] / data / yang-data-tree-ri / src / test / java / org / opendaylight / yangtools / yang / data / tree / impl / Bug5830Test.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. 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.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertThrows;
13 import static org.junit.Assert.assertTrue;
14 import static org.junit.Assert.fail;
15
16 import org.junit.Test;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
21 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
23 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
26 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
27 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
28 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
29 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
30 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
31 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
32 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
33 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
34 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
35 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
36
37 public class Bug5830Test {
38     private static final String NS = "foo";
39     private static final String REV = "2016-05-17";
40     private static final QName TASK_CONTAINER = QName.create(NS, REV, "task-container");
41     private static final QName TASK = QName.create(NS, REV, "task");
42     private static final QName TASK_ID = QName.create(NS, REV, "task-id");
43     private static final QName TASK_DATA = QName.create(NS, REV, "task-data");
44     private static final QName OTHER_DATA = QName.create(NS, REV, "other-data");
45     private static final QName MANDATORY_DATA = QName.create(NS, REV, "mandatory-data");
46     private static final QName TASK_MANDATORY_LEAF = QName.create(NS, REV, "task-mandatory-leaf");
47     private static final QName NON_PRESENCE_CONTAINER = QName.create(NS, REV, "non-presence-container");
48     private static final QName NON_PRESENCE_CONTAINER_2 = QName.create(NS, REV, "non-presence-container-2");
49     private static final QName PRESENCE_CONTAINER_2 = QName.create(NS, REV, "presence-container-2");
50     private static final QName MANDATORY_LEAF_2 = QName.create(NS, REV, "mandatory-leaf-2");
51
52     private static DataTree initDataTree(final EffectiveModelContext schemaContext)
53             throws DataValidationFailedException {
54         DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(
55                 DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
56
57         final SystemMapNode taskNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(TASK)).build();
58         final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
59         modificationTree.write(YangInstanceIdentifier.of(TASK_CONTAINER, TASK), taskNode);
60         modificationTree.ready();
61
62         inMemoryDataTree.validate(modificationTree);
63         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
64         inMemoryDataTree.commit(prepare);
65         return inMemoryDataTree;
66     }
67
68     @Test
69     public void testMandatoryNodes() throws DataValidationFailedException {
70         testPresenceContainer();
71         testNonPresenceContainer();
72         testMultipleContainers();
73     }
74
75     private static void testPresenceContainer() throws DataValidationFailedException {
76         final EffectiveModelContext schemaContext = YangParserTestUtils.parseYang("""
77             module foo-presence {
78               yang-version 1;
79               namespace "foo";
80               prefix foo;
81
82               revision 2016-05-17 {
83                 description "test";
84               }
85
86               container task-container {
87                 list task {
88                   key "task-id";
89                   leaf task-id {
90                     type string;
91                   }
92                   leaf task-mandatory-leaf {
93                     type string;
94                     mandatory true;
95                   }
96
97                   container task-data {
98                     presence "Task data";
99                     leaf mandatory-data {
100                       type string;
101                       mandatory true;
102                     }
103                     leaf other-data {
104                       type string;
105                     }
106                   }
107                 }
108               }
109             }""");
110         assertNotNull("Schema context must not be null.", schemaContext);
111
112         testContainerIsNotPresent(schemaContext);
113         final var ex = assertThrows(IllegalArgumentException.class, () -> testContainerIsPresent(schemaContext));
114         assertEquals(
115             "Node (foo?revision=2016-05-17)task-data is missing mandatory descendant /(foo?revision=2016-05-17)"
116                 + "mandatory-data", ex.getMessage());
117         testMandatoryDataLeafIsPresent(schemaContext);
118     }
119
120     private static void testNonPresenceContainer() throws DataValidationFailedException {
121         final EffectiveModelContext schemaContext = YangParserTestUtils.parseYang("""
122             module foo-non-presence {
123               yang-version 1;
124               namespace "foo";
125               prefix foo;
126
127               revision 2016-05-17 {
128                 description "test";
129               }
130
131               container task-container {
132                 list task {
133                   key "task-id";
134
135                   leaf task-id {
136                     type string;
137                   }
138                   leaf task-mandatory-leaf {
139                     type string;
140                     mandatory true;
141                   }
142
143                   container task-data {
144                     leaf mandatory-data {
145                       type string;
146                       mandatory true;
147                     }
148                     leaf other-data {
149                       type string;
150                     }
151                   }
152                 }
153               }
154             }""");
155         assertNotNull("Schema context must not be null.", schemaContext);
156
157         try {
158             testContainerIsNotPresent(schemaContext);
159             fail("Should fail due to missing mandatory node.");
160         } catch (IllegalArgumentException e) {
161             assertEquals(
162                     "Node (foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=123}] is missing mandatory "
163                             + "descendant /(foo?revision=2016-05-17)task-data/mandatory-data", e.getMessage());
164         }
165
166         try {
167             testContainerIsPresent(schemaContext);
168             fail("Should fail due to missing mandatory node.");
169         } catch (IllegalArgumentException e) {
170             assertEquals(
171                     "Node (foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=123}] is missing mandatory "
172                             + "descendant /(foo?revision=2016-05-17)task-data/mandatory-data", e.getMessage());
173         }
174         testMandatoryDataLeafIsPresent(schemaContext);
175     }
176
177     private static void testMultipleContainers() throws DataValidationFailedException {
178         final EffectiveModelContext schemaContext = YangParserTestUtils.parseYang("""
179             module foo-multiple {
180               yang-version 1;
181               namespace "foo";
182               prefix foo;
183
184               revision 2016-05-17 {
185                 description "test";
186               }
187
188               container task-container {
189                 list task {
190                   key "task-id";
191
192                   leaf task-id {
193                     type string;
194                   }
195                   leaf task-mandatory-leaf {
196                     type string;
197                     mandatory true;
198                   }
199
200                   container task-data {
201                     presence "Task data";
202
203                     leaf mandatory-data {
204                       type string;
205                       mandatory true;
206                     }
207                     leaf other-data {
208                       type string;
209                     }
210
211                     container non-presence-container {
212
213                       container presence-container {
214                         presence "presence container";
215
216                         leaf mandatory-leaf {
217                           mandatory true;
218                           type string;
219                         }
220                       }
221
222                       container non-presence-container-2 {
223                         leaf mandatory-leaf-2 {
224                           mandatory true;
225                           type string;
226                         }
227                       }
228
229                       container presence-container-2 {
230                         presence "presence container";
231
232                         leaf mandatory-leaf-3 {
233                           mandatory true;
234                           type string;
235                         }
236                       }
237                     }
238                   }
239                 }
240               }
241             }""");
242         assertNotNull("Schema context must not be null.", schemaContext);
243
244         testContainerIsNotPresent(schemaContext);
245
246         try {
247             testContainerIsPresent(schemaContext);
248             fail("Should fail due to missing mandatory node under present presence container.");
249         } catch (IllegalArgumentException e) {
250             assertTrue(e.getMessage().startsWith(
251                     "Node (foo?revision=2016-05-17)task-data is missing mandatory descendant"));
252         }
253
254         try {
255             testMandatoryDataLeafIsPresent(schemaContext);
256             fail("Should fail due to missing mandatory node under present presence container.");
257         } catch (IllegalArgumentException e) {
258             assertEquals("""
259                 Node (foo?revision=2016-05-17)task-data is missing mandatory descendant \
260                 /(foo?revision=2016-05-17)non-presence-container/\
261                 non-presence-container-2/mandatory-leaf-2""", e.getMessage());
262         }
263
264         testMandatoryLeaf2IsPresent(schemaContext, false);
265
266         try {
267             testMandatoryLeaf2IsPresent(schemaContext, true);
268             fail("Should fail due to missing mandatory node under present presence container.");
269         } catch (IllegalArgumentException e) {
270             assertEquals(
271                     "Node (foo?revision=2016-05-17)presence-container-2 is missing mandatory "
272                             + "descendant /(foo?revision=2016-05-17)mandatory-leaf-3", e.getMessage());
273         }
274     }
275
276     private static void testContainerIsNotPresent(final EffectiveModelContext schemaContext)
277             throws DataValidationFailedException {
278         final DataTree inMemoryDataTree = initDataTree(schemaContext);
279         final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
280                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123"))
281                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
282                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
283                 .build();
284
285         final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
286         modificationTree.write(
287             YangInstanceIdentifier.of(TASK_CONTAINER, TASK).node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123")),
288             taskEntryNode);
289         modificationTree.ready();
290
291         inMemoryDataTree.validate(modificationTree);
292         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
293         inMemoryDataTree.commit(prepare);
294     }
295
296     private static void testContainerIsPresent(final EffectiveModelContext schemaContext)
297             throws DataValidationFailedException {
298         final DataTree inMemoryDataTree = initDataTree(schemaContext);
299
300         final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
301                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123"))
302                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
303                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
304                 .withChild(createTaskDataContainer(false)).build();
305
306         final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
307         modificationTree.write(
308             YangInstanceIdentifier.of(TASK_CONTAINER, TASK).node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123")),
309             taskEntryNode);
310         modificationTree.ready();
311
312         inMemoryDataTree.validate(modificationTree);
313         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
314         inMemoryDataTree.commit(prepare);
315     }
316
317     private static void testMandatoryDataLeafIsPresent(final EffectiveModelContext schemaContext)
318             throws DataValidationFailedException {
319         final DataTree inMemoryDataTree = initDataTree(schemaContext);
320
321         final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
322                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123"))
323                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
324                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
325                 .withChild(createTaskDataContainer(true)).build();
326
327         final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
328         modificationTree.write(
329             YangInstanceIdentifier.of(TASK_CONTAINER, TASK).node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123")),
330             taskEntryNode);
331         modificationTree.ready();
332
333         inMemoryDataTree.validate(modificationTree);
334         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
335         inMemoryDataTree.commit(prepare);
336     }
337
338     private static void testMandatoryLeaf2IsPresent(final EffectiveModelContext schemaContext,
339             final boolean withPresenceContianer) throws DataValidationFailedException {
340         final DataTree inMemoryDataTree = initDataTree(schemaContext);
341
342         final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
343                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123"))
344                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
345                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
346                 .withChild(createTaskDataMultipleContainer(withPresenceContianer))
347                 .build();
348
349         final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
350         modificationTree.write(
351             YangInstanceIdentifier.of(TASK_CONTAINER, TASK).node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123")),
352             taskEntryNode);
353         modificationTree.ready();
354
355         inMemoryDataTree.validate(modificationTree);
356         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
357         inMemoryDataTree.commit(prepare);
358     }
359
360     private static DataContainerChild createTaskDataContainer(final boolean withMandatoryNode) {
361         DataContainerNodeBuilder<NodeIdentifier, ContainerNode> taskDataBuilder = Builders.containerBuilder()
362                 .withNodeIdentifier(new NodeIdentifier(TASK_DATA))
363                 .withChild(ImmutableNodes.leafNode(OTHER_DATA, "foo"));
364         if (withMandatoryNode) {
365             taskDataBuilder.withChild(ImmutableNodes.leafNode(MANDATORY_DATA, "mandatory-data-value"));
366         }
367         return taskDataBuilder.build();
368     }
369
370     private static DataContainerChild createTaskDataMultipleContainer(final boolean withPresenceContianer) {
371         DataContainerNodeBuilder<NodeIdentifier, ContainerNode> nonPresenceContainerBuilder = Builders
372                 .containerBuilder()
373                 .withNodeIdentifier(new NodeIdentifier(NON_PRESENCE_CONTAINER))
374                 .withChild(
375                         Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(NON_PRESENCE_CONTAINER_2))
376                                 .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF_2, "mandatory leaf data 2")).build());
377
378         if (withPresenceContianer) {
379             nonPresenceContainerBuilder.withChild(Builders.containerBuilder()
380                     .withNodeIdentifier(new NodeIdentifier(PRESENCE_CONTAINER_2)).build());
381         }
382
383         DataContainerNodeBuilder<NodeIdentifier, ContainerNode> taskDataBuilder = Builders.containerBuilder()
384                 .withNodeIdentifier(new NodeIdentifier(TASK_DATA))
385                 .withChild(ImmutableNodes.leafNode(OTHER_DATA, "foo"));
386         taskDataBuilder.withChild(ImmutableNodes.leafNode(MANDATORY_DATA, "mandatory-data-value"));
387         taskDataBuilder.withChild(nonPresenceContainerBuilder.build());
388
389         return taskDataBuilder.build();
390     }
391 }