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