Deprecate simple DataTreeFactory.create()
[yangtools.git] / data / yang-data-tree-ri / src / test / java / org / opendaylight / yangtools / yang / data / tree / impl / UniqueConstraintTest.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.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
12 import static org.junit.jupiter.api.Assertions.assertThrows;
13 import static org.junit.jupiter.api.Assertions.assertTrue;
14
15 import org.junit.jupiter.api.BeforeAll;
16 import org.junit.jupiter.api.Test;
17 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
18 import org.opendaylight.yangtools.yang.common.ErrorTag;
19 import org.opendaylight.yangtools.yang.common.ErrorType;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
24 import org.opendaylight.yangtools.yang.data.api.YangNetconfErrorAware;
25 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
26 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
27 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
28 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
29 import org.opendaylight.yangtools.yang.data.tree.api.TreeType;
30 import org.opendaylight.yangtools.yang.data.tree.api.UniqueConstraintException;
31 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
32 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
33
34 class UniqueConstraintTest {
35     private static final String NS = "foo";
36     private static final String REV = "2016-05-17";
37     private static final QName TASK_CONTAINER = QName.create(NS, REV, "task-container");
38     private static final QName TASK = QName.create(NS, REV, "task");
39     private static final QName TASK_ID = QName.create(NS, REV, "task-id");
40     private static final QName MY_LEAF_1 = QName.create(NS, REV, "my-leaf-1");
41     private static final QName MY_LEAF_2 = QName.create(NS, REV, "my-leaf-2");
42     private static final QName MY_LEAF_3 = QName.create(NS, REV, "my-leaf-3");
43     private static final QName MY_CONTAINER = QName.create(NS, REV, "my-container");
44
45     private static EffectiveModelContext TEST_MODEL;
46
47     @BeforeAll
48     static void beforeClass() {
49         TEST_MODEL = TestModel.createTestContext("/yt570.yang");
50     }
51
52     @Test
53     void switchEntriesTest() throws DataValidationFailedException {
54         final var inMemoryDataTree = initDataTree(TEST_MODEL, true);
55         writeMapEntry(inMemoryDataTree, "1", "l1", "l2", "l3");
56         writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l4");
57
58         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
59
60         final var mapEntry1 = createMapEntry("1", "l2", "l3", "l4");
61         final var mapEntry2 = createMapEntry("2", "l1", "l2", "l3");
62
63         //switch values of map entries
64         modificationTree.write(
65                 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
66                         .node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "1")), mapEntry1);
67         modificationTree.write(
68                 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
69                         .node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "2")), mapEntry2);
70
71         modificationTree.ready();
72         inMemoryDataTree.validate(modificationTree);
73         final var prepare = inMemoryDataTree.prepare(modificationTree);
74         inMemoryDataTree.commit(prepare);
75     }
76
77     @Test
78     void mapTest() throws DataValidationFailedException {
79         final var inMemoryDataTree = emptyDataTree(TEST_MODEL, true);
80
81
82         verifyException(assertThrows(UniqueValidationFailedException.class,
83             () -> writeMap(inMemoryDataTree, true)),
84             "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
85             "}] violates unique constraint on [l1, l2] of ",
86             "(foo?revision=2016-05-17)my-leaf-1",
87             "(foo?revision=2016-05-17)my-leaf-2]");
88
89         writeMap(inMemoryDataTree, false);
90         verifyException(assertThrows(UniqueConstraintException.class,
91             () -> writeMapEntry(inMemoryDataTree, "4", "l1", "l2", "l30")),
92             "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
93             "}] violates unique constraint on [l1, l2] of ",
94             "(foo?revision=2016-05-17)my-leaf-1",
95             "(foo?revision=2016-05-17)my-leaf-2");
96     }
97
98     @Test
99     void mapEntryTest() throws DataValidationFailedException {
100         final var inMemoryDataTree = initDataTree(TEST_MODEL, true);
101         writeAndRemoveMapEntries(inMemoryDataTree, true);
102         writeAndRemoveMapEntries(inMemoryDataTree, false);
103     }
104
105     private static void writeAndRemoveMapEntries(final InMemoryDataTree inMemoryDataTree, final boolean clear)
106             throws DataValidationFailedException {
107         writeMapEntry(inMemoryDataTree, "1", "l1", "l2", "l3");
108         writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l4");
109         writeMapEntry(inMemoryDataTree, "3", "l3", "l4", "l5");
110         writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l6");
111         writeMapEntry(inMemoryDataTree, "10", "l2", "l10", "l4");
112         verifyException(assertThrows(UniqueConstraintException.class,
113             () -> writeMapEntry(inMemoryDataTree, "4", "l1", "l5", "l3")),
114             "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
115                     "}] violates unique constraint on [l1, l3] of ",
116                     "(foo?revision=2016-05-17)my-container, my-leaf-3",
117                     "(foo?revision=2016-05-17)my-leaf-1");
118         writeMapEntry(inMemoryDataTree, "4", "l4", "l5", "l6");
119         verifyException(assertThrows(UniqueConstraintException.class,
120             () -> writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7")),
121             "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
122             "}] violates unique constraint on [l3, l4] of ",
123             "(foo?revision=2016-05-17)my-leaf-1",
124             "(foo?revision=2016-05-17)my-leaf-2");
125         removeMapEntry(inMemoryDataTree, taskEntryKey("3"));
126         writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
127         writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
128         verifyException(assertThrows(UniqueConstraintException.class,
129             () -> writeMapEntry(inMemoryDataTree, "6", "l3", "l4", "l11")),
130             "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
131             "}] violates unique constraint on [l3, l4] of ",
132             "(foo?revision=2016-05-17)my-leaf-1",
133             "(foo?revision=2016-05-17)my-leaf-2");
134
135         if (clear) {
136             removeMapEntry(inMemoryDataTree, taskEntryKey("1"));
137             removeMapEntry(inMemoryDataTree, taskEntryKey("2"));
138             removeMapEntry(inMemoryDataTree, taskEntryKey("4"));
139             removeMapEntry(inMemoryDataTree, taskEntryKey("5"));
140             removeMapEntry(inMemoryDataTree, taskEntryKey("10"));
141         }
142     }
143
144     private static void verifyException(final Exception ex, final String expectedStart,
145             final String... expectedLeaves) {
146         verifyExceptionMessage(expectedStart, ex.getMessage(), expectedLeaves);
147         assertInstanceOf(YangNetconfErrorAware.class, ex);
148         final var errors = ((YangNetconfErrorAware) ex).getNetconfErrors();
149         assertEquals(1, errors.size());
150         final var error = errors.get(0);
151         assertEquals(ErrorSeverity.ERROR, error.severity());
152         assertEquals(ErrorType.APPLICATION, error.type());
153         assertEquals(ErrorTag.OPERATION_FAILED, error.tag());
154         assertEquals("data-not-unique", error.appTag());
155     }
156
157     private static void verifyExceptionMessage(final String expectedStart, final String message,
158             final String... leafs) {
159         assertTrue(message.startsWith(expectedStart));
160         for (final var leaf : leafs) {
161             assertTrue(message.contains(leaf));
162         }
163     }
164
165     private static void writeMap(final InMemoryDataTree inMemoryDataTree, final boolean withUniqueViolation)
166             throws DataValidationFailedException {
167         final var taskNode = ImmutableNodes.newSystemMapBuilder()
168             .withNodeIdentifier(new NodeIdentifier(TASK))
169             .withChild(createMapEntry("1", "l1", "l2", "l3"))
170             .withChild(createMapEntry("2", "l2", "l3", "l4"))
171             .withChild(
172                 withUniqueViolation ? createMapEntry("3", "l1", "l2", "l10") : createMapEntry("3", "l3", "l4", "l5"))
173             .build();
174
175         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
176         modificationTree.write(YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK), taskNode);
177         modificationTree.ready();
178         inMemoryDataTree.validate(modificationTree);
179         final var prepare = inMemoryDataTree.prepare(modificationTree);
180         inMemoryDataTree.commit(prepare);
181     }
182
183     private static void writeMapEntry(final InMemoryDataTree inMemoryDataTree, final Object taskIdValue,
184             final Object myLeaf1Value, final Object myLeaf2Value, final Object myLeaf3Value)
185             throws DataValidationFailedException {
186         final var taskEntryNode = ImmutableNodes.newMapEntryBuilder()
187             .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskIdValue))
188             .withChild(ImmutableNodes.leafNode(TASK_ID, taskIdValue))
189             .withChild(ImmutableNodes.leafNode(MY_LEAF_1, myLeaf1Value))
190             .withChild(ImmutableNodes.leafNode(MY_LEAF_2, myLeaf2Value))
191             .withChild(ImmutableNodes.newContainerBuilder()
192                 .withNodeIdentifier(new NodeIdentifier(MY_CONTAINER))
193                 .withChild(ImmutableNodes.leafNode(MY_LEAF_3, myLeaf3Value))
194                 .build())
195             .build();
196
197         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
198         modificationTree.write(
199                 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
200                         .node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskIdValue)),
201                 taskEntryNode);
202         modificationTree.ready();
203         inMemoryDataTree.validate(modificationTree);
204         final var prepare = inMemoryDataTree.prepare(modificationTree);
205         inMemoryDataTree.commit(prepare);
206     }
207
208     private static void removeMapEntry(final InMemoryDataTree inMemoryDataTree,
209             final NodeIdentifierWithPredicates mapEntryKey) throws DataValidationFailedException {
210         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
211         modificationTree.delete(YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK).node(mapEntryKey));
212         modificationTree.ready();
213         inMemoryDataTree.validate(modificationTree);
214         final var prepare = inMemoryDataTree.prepare(modificationTree);
215         inMemoryDataTree.commit(prepare);
216     }
217
218     private static MapEntryNode createMapEntry(final Object taskIdValue, final Object myLeaf1Value,
219             final Object myLeaf2Value, final Object myLeaf3Value) {
220         return ImmutableNodes.newMapEntryBuilder()
221             .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskIdValue))
222             .withChild(ImmutableNodes.leafNode(TASK_ID, taskIdValue))
223             .withChild(ImmutableNodes.leafNode(MY_LEAF_1, myLeaf1Value))
224             .withChild(ImmutableNodes.leafNode(MY_LEAF_2, myLeaf2Value))
225             .withChild(ImmutableNodes.newContainerBuilder()
226                 .withNodeIdentifier(new NodeIdentifier(MY_CONTAINER))
227                 .withChild(ImmutableNodes.leafNode(MY_LEAF_3, myLeaf3Value))
228                 .build())
229             .build();
230     }
231
232     private static NodeIdentifierWithPredicates taskEntryKey(final String taskId) {
233         return NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskId);
234     }
235
236     @Test
237     void disabledUniqueIndexTest() throws DataValidationFailedException {
238         final var inMemoryDataTree = initDataTree(TEST_MODEL, false);
239
240         writeMapEntry(inMemoryDataTree, "1", "l1", "l2", "l3");
241         writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l4");
242         writeMapEntry(inMemoryDataTree, "3", "l3", "l4", "l5");
243         writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l6");
244         writeMapEntry(inMemoryDataTree, "10", "l2", "l10", "l4");
245         writeMapEntry(inMemoryDataTree, "4", "l1", "l5", "l3");
246         writeMapEntry(inMemoryDataTree, "4", "l4", "l5", "l6");
247         writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
248         removeMapEntry(inMemoryDataTree, taskEntryKey("3"));
249         writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
250         writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
251         writeMapEntry(inMemoryDataTree, "6", "l3", "l4", "l7");
252     }
253
254     private static InMemoryDataTree initDataTree(final EffectiveModelContext schemaContext, final boolean uniqueIndex)
255             throws DataValidationFailedException {
256         final var inMemoryDataTree = (InMemoryDataTree) new InMemoryDataTreeFactory().create(
257             new DataTreeConfiguration.Builder(TreeType.CONFIGURATION).setUniqueIndexes(uniqueIndex).build());
258         inMemoryDataTree.setEffectiveModelContext(schemaContext);
259
260         final var taskNode = ImmutableNodes.newSystemMapBuilder().withNodeIdentifier(new NodeIdentifier(TASK)).build();
261         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
262         modificationTree.write(YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK), taskNode);
263         modificationTree.ready();
264
265         inMemoryDataTree.validate(modificationTree);
266         final var prepare = inMemoryDataTree.prepare(modificationTree);
267         inMemoryDataTree.commit(prepare);
268         return inMemoryDataTree;
269     }
270
271     private static InMemoryDataTree emptyDataTree(final EffectiveModelContext schemaContext,
272             final boolean uniqueIndex) {
273         final var inMemoryDataTree = (InMemoryDataTree) new InMemoryDataTreeFactory().create(
274             new DataTreeConfiguration.Builder(TreeType.CONFIGURATION).setUniqueIndexes(uniqueIndex).build());
275         inMemoryDataTree.setEffectiveModelContext(schemaContext);
276
277         return inMemoryDataTree;
278     }
279 }