Migrate yang-data-tree-ri to JUnit5
[yangtools.git] / data / yang-data-tree-ri / src / test / java / org / opendaylight / yangtools / yang / data / tree / impl / ListConstraintsValidation.java
1 /*
2  * Copyright (c) 2015 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.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
12 import static org.junit.jupiter.api.Assertions.assertNotNull;
13 import static org.junit.jupiter.api.Assertions.assertThrows;
14 import static org.junit.jupiter.api.Assertions.assertTrue;
15
16 import java.util.List;
17 import org.junit.jupiter.api.AfterAll;
18 import org.junit.jupiter.api.BeforeAll;
19 import org.junit.jupiter.api.BeforeEach;
20 import org.junit.jupiter.api.Test;
21 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
22 import org.opendaylight.yangtools.yang.common.ErrorTag;
23 import org.opendaylight.yangtools.yang.common.ErrorType;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
28 import org.opendaylight.yangtools.yang.data.api.YangNetconfErrorAware;
29 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
31 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
32 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
33 import org.opendaylight.yangtools.yang.data.impl.schema.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.DataValidationFailedException;
37 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
38 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
39 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
40
41 class ListConstraintsValidation {
42     private static final QName MASTER_CONTAINER_QNAME = QName.create(
43             "urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model", "2015-02-02",
44             "master-container");
45     private static final QName MIN_MAX_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-list");
46     private static final QName MIN_MAX_KEY_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-key-leaf");
47     private static final QName UNBOUNDED_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unbounded-list");
48     private static final QName UNBOUNDED_KEY_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unbounded-key-leaf");
49     private static final QName MIN_MAX_LEAF_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-leaf-list");
50     private static final QName UNBOUNDED_LEAF_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unbounded-leaf-list");
51     private static final QName UNKEYED_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unkeyed-list");
52     private static final QName UNKEYED_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unkeyed-leaf");
53
54     private static final YangInstanceIdentifier MASTER_CONTAINER_PATH = YangInstanceIdentifier
55             .of(MASTER_CONTAINER_QNAME);
56     private static final YangInstanceIdentifier MIN_MAX_LIST_PATH = YangInstanceIdentifier
57             .builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LIST_QNAME).build();
58     private static final YangInstanceIdentifier UNBOUNDED_LIST_PATH = YangInstanceIdentifier
59             .builder(MASTER_CONTAINER_PATH).node(UNBOUNDED_LIST_QNAME).build();
60     private static final YangInstanceIdentifier MIN_MAX_LEAF_LIST_PATH = YangInstanceIdentifier
61             .builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LEAF_LIST_QNAME).build();
62     private static final YangInstanceIdentifier UNBOUNDED_LEAF_LIST_PATH = YangInstanceIdentifier
63             .builder(MASTER_CONTAINER_PATH).node(UNBOUNDED_LEAF_LIST_QNAME).build();
64     private static final YangInstanceIdentifier UNKEYED_LIST_PATH = YangInstanceIdentifier
65             .builder(MASTER_CONTAINER_PATH).node(UNKEYED_LIST_QNAME).build();
66
67     private static EffectiveModelContext schemaContext;
68
69     private DataTree inMemoryDataTree;
70
71     @BeforeAll
72     static void beforeClass() {
73         schemaContext = YangParserTestUtils.parseYang("""
74             module list-constraints-validation-test-model  {
75               yang-version 1;
76               namespace "urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model";
77               prefix "list-constraints-validation";
78
79               revision "2015-02-02" {
80                 description "Initial revision.";
81               }
82
83               container master-container {
84                 list min-max-list {
85                   min-elements 2;
86                   max-elements 3;
87                   key "min-max-key-leaf";
88                   leaf min-max-key-leaf {
89                     type string;
90                   }
91                 }
92
93                 list unbounded-list {
94                   key "unbounded-key-leaf";
95                   leaf unbounded-key-leaf {
96                     type int8;
97                   }
98                 }
99
100                 leaf-list min-max-leaf-list {
101                   min-elements 1;
102                   max-elements 3;
103                   type string;
104                 }
105
106                 leaf-list unbounded-leaf-list {
107                   type string;
108                 }
109
110                 list unkeyed-list {
111                   max-elements 1;
112                   leaf unkeyed-leaf {
113                     type string;
114                   }
115                 }
116               }
117             }""");
118     }
119
120     @AfterAll
121     static void afterClass() {
122         schemaContext = null;
123     }
124
125     @BeforeEach
126     void prepare() throws DataValidationFailedException {
127         inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
128             schemaContext);
129         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
130         final var modificationTree = initialDataTreeSnapshot.newModification();
131
132         modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.containerNode(MASTER_CONTAINER_QNAME));
133         modificationTree.ready();
134         inMemoryDataTree.commit(inMemoryDataTree.prepare(modificationTree));
135     }
136
137     @Test
138     void minMaxListTestPass() throws DataValidationFailedException {
139
140         final var fooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "foo");
141         final var barEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
142         final var mapNode1 = ImmutableNodes.mapNodeBuilder()
143                 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
144                 .withChild(fooEntryNode).build();
145         final var mapNode2 = ImmutableNodes.mapNodeBuilder()
146                 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
147                 .withChild(barEntryNode).build();
148
149         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
150         modificationTree.write(MIN_MAX_LIST_PATH, mapNode1);
151         modificationTree.merge(MIN_MAX_LIST_PATH, mapNode2);
152         modificationTree.ready();
153
154         inMemoryDataTree.validate(modificationTree);
155         final var prepare = inMemoryDataTree.prepare(modificationTree);
156         inMemoryDataTree.commit(prepare);
157
158         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
159         final var minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
160         assertTrue(minMaxListRead.isPresent());
161         assertEquals(2, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
162     }
163
164     @Test
165     void minMaxListFail() throws DataValidationFailedException {
166         assertThrows(DataValidationFailedException.class, () -> {
167             var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
168
169             final var fooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
170                     "foo");
171             final var barEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
172                     "bar");
173             final var gooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
174                     "goo");
175             final var mapNode = ImmutableNodes.mapNodeBuilder()
176                 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
177                 .withChild(fooEntryNode).build();
178
179             final var fooPath = MIN_MAX_LIST_PATH.node(fooEntryNode.name());
180             final var barPath = MIN_MAX_LIST_PATH.node(barEntryNode.name());
181             final var gooPath = MIN_MAX_LIST_PATH.node(gooEntryNode.name());
182
183             modificationTree.write(MIN_MAX_LIST_PATH, mapNode);
184             modificationTree.merge(barPath, barEntryNode);
185             modificationTree.write(gooPath, gooEntryNode);
186             modificationTree.delete(gooPath);
187             modificationTree.ready();
188
189             inMemoryDataTree.validate(modificationTree);
190             var prepare1 = inMemoryDataTree.prepare(modificationTree);
191             inMemoryDataTree.commit(prepare1);
192
193             var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
194             var minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
195             assertTrue(minMaxListRead.isPresent());
196             assertEquals(2, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
197
198             modificationTree = inMemoryDataTree.takeSnapshot().newModification();
199             modificationTree.write(gooPath, gooEntryNode);
200             modificationTree.ready();
201
202             inMemoryDataTree.validate(modificationTree);
203             prepare1 = inMemoryDataTree.prepare(modificationTree);
204             inMemoryDataTree.commit(prepare1);
205
206             snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
207             minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
208             assertTrue(minMaxListRead.isPresent());
209             assertEquals(3, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
210
211             modificationTree = inMemoryDataTree.takeSnapshot().newModification();
212
213             modificationTree.delete(gooPath);
214             modificationTree.delete(fooPath);
215             modificationTree.ready();
216
217             inMemoryDataTree.validate(modificationTree);
218         });
219     }
220
221     @Test
222     void minMaxLeafListPass() throws DataValidationFailedException {
223         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
224
225         final var barPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "bar");
226         final var gooPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "goo");
227
228         modificationTree.write(MIN_MAX_LEAF_LIST_PATH, Builders.leafSetBuilder()
229             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
230             .withChildValue("foo")
231             .build());
232         modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), Builders.leafSetEntryBuilder()
233             .withNodeIdentifier(barPath)
234             .withValue("bar")
235             .build());
236         modificationTree.merge(MIN_MAX_LEAF_LIST_PATH.node(gooPath), Builders.leafSetEntryBuilder()
237             .withNodeIdentifier(gooPath)
238             .withValue("goo")
239             .build());
240         modificationTree.delete(MIN_MAX_LEAF_LIST_PATH.node(gooPath));
241         modificationTree.ready();
242
243         inMemoryDataTree.validate(modificationTree);
244         final var prepare1 = inMemoryDataTree.prepare(modificationTree);
245         inMemoryDataTree.commit(prepare1);
246
247         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
248         final var masterContainer = snapshotAfterCommit.readNode(MASTER_CONTAINER_PATH);
249         assertTrue(masterContainer.isPresent());
250         final var leafList =
251             (NormalizedNodeContainer<?>) ((DistinctNodeContainer) masterContainer.orElseThrow())
252                 .childByArg(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME));
253         assertNotNull(leafList);
254         assertEquals(2, leafList.size());
255     }
256
257     @Test
258     void minMaxLeafListFail() {
259         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
260
261         final var barPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "bar");
262         final var gooPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "goo");
263         final var fuuPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "fuu");
264
265         modificationTree.write(MIN_MAX_LEAF_LIST_PATH, Builders.leafSetBuilder()
266             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
267             .withChildValue("foo")
268             .build());
269         modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), Builders.leafSetEntryBuilder()
270             .withNodeIdentifier(barPath)
271             .withValue("bar")
272             .build());
273         modificationTree.merge(MIN_MAX_LEAF_LIST_PATH.node(gooPath), Builders.leafSetEntryBuilder()
274             .withNodeIdentifier(gooPath)
275             .withValue("goo")
276             .build());
277         modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(fuuPath), Builders.leafSetEntryBuilder()
278             .withNodeIdentifier(fuuPath)
279             .withValue("fuu")
280             .build());
281
282         final var ex = assertThrows(MinMaxElementsValidationFailedException.class,
283             () -> modificationTree.ready());
284         assertEquals("(urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model?"
285             + "revision=2015-02-02)min-max-leaf-list has too many elements (4), can have at most 3",
286             ex.getMessage());
287         assertTooManyElements(ex);
288     }
289
290     @Test
291     void unkeyedListTestPass() throws DataValidationFailedException {
292         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
293
294         final var unkeyedListNode = Builders.unkeyedListBuilder()
295             .withNodeIdentifier(new NodeIdentifier(UNKEYED_LIST_QNAME))
296             .withValue(List.of(Builders.unkeyedListEntryBuilder()
297                 .withNodeIdentifier(new NodeIdentifier(UNKEYED_LEAF_QNAME))
298                 .withChild(ImmutableNodes.leafNode(UNKEYED_LEAF_QNAME, "foo"))
299                 .build()))
300             .build();
301
302         modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.containerNode(MASTER_CONTAINER_QNAME));
303         modificationTree.merge(UNKEYED_LIST_PATH, unkeyedListNode);
304         modificationTree.ready();
305
306         inMemoryDataTree.validate(modificationTree);
307         final var prepare1 = inMemoryDataTree.prepare(modificationTree);
308         inMemoryDataTree.commit(prepare1);
309
310         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
311         final var unkeyedListRead = snapshotAfterCommit.readNode(UNKEYED_LIST_PATH);
312         assertTrue(unkeyedListRead.isPresent());
313         assertEquals(1, ((UnkeyedListNode) unkeyedListRead.orElseThrow()).size());
314     }
315
316     @Test
317     void unkeyedListTestFail() {
318         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
319
320         modificationTree.write(UNKEYED_LIST_PATH, Builders.unkeyedListBuilder()
321             .withNodeIdentifier(new NodeIdentifier(UNKEYED_LIST_QNAME))
322             .withValue(List.of(
323                 Builders.unkeyedListEntryBuilder()
324                     .withNodeIdentifier(new NodeIdentifier(UNKEYED_LEAF_QNAME))
325                     .withChild(ImmutableNodes.leafNode(UNKEYED_LEAF_QNAME, "foo"))
326                     .build(),
327                 Builders.unkeyedListEntryBuilder()
328                     .withNodeIdentifier(new NodeIdentifier(UNKEYED_LEAF_QNAME))
329                     .withChild(ImmutableNodes.leafNode(UNKEYED_LEAF_QNAME, "bar"))
330                     .build()))
331             .build());
332         final var ex = assertThrows(MinMaxElementsValidationFailedException.class,
333             () -> modificationTree.ready());
334         assertEquals("(urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model?"
335             + "revision=2015-02-02)unkeyed-list has too many elements (2), can have at most 1", ex.getMessage());
336         assertTooManyElements(ex);
337     }
338
339     static void assertTooFewElements(final Exception ex) {
340         assertOperationFailed(ex, "too-few-elements");
341     }
342
343     static void assertTooManyElements(final Exception ex) {
344         assertOperationFailed(ex, "too-many-elements");
345     }
346
347     private static void assertOperationFailed(final Exception ex, final String expectedAppTag) {
348         assertInstanceOf(YangNetconfErrorAware.class, ex);
349         final var errors = ((YangNetconfErrorAware) ex).getNetconfErrors();
350         assertEquals(1, errors.size());
351         final var error = errors.get(0);
352         assertEquals(ErrorSeverity.ERROR, error.severity());
353         assertEquals(ErrorType.APPLICATION, error.type());
354         assertEquals(ErrorTag.OPERATION_FAILED, error.tag());
355         assertEquals(expectedAppTag, error.appTag());
356     }
357 }