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