Report a dedicated exception on unique failure
[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(UniqueValidationFailedException.class,
82             () -> writeMap(inMemoryDataTree, true)),
83             "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
84             "}] violates unique constraint on [l2, l1] of ",
85             "(foo?revision=2016-05-17)my-leaf-1",
86             "(foo?revision=2016-05-17)my-leaf-2]");
87
88         writeMap(inMemoryDataTree, false);
89         verifyExceptionMessage(assertThrows(UniqueConstraintException.class,
90             () -> writeMapEntry(inMemoryDataTree, "4", "l1", "l2", "l30")),
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
97     @Test
98     public void mapEntryTest() throws ReactorException, DataValidationFailedException {
99         final InMemoryDataTree inMemoryDataTree = initDataTree(TEST_MODEL, true);
100         writeAndRemoveMapEntries(inMemoryDataTree, true);
101         writeAndRemoveMapEntries(inMemoryDataTree, false);
102     }
103
104     private static void writeAndRemoveMapEntries(final InMemoryDataTree inMemoryDataTree, final boolean clear)
105             throws DataValidationFailedException {
106         writeMapEntry(inMemoryDataTree, "1", "l1", "l2", "l3");
107         writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l4");
108         writeMapEntry(inMemoryDataTree, "3", "l3", "l4", "l5");
109         writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l6");
110         writeMapEntry(inMemoryDataTree, "10", "l2", "l10", "l4");
111         verifyExceptionMessage(assertThrows(UniqueConstraintException.class,
112             () -> writeMapEntry(inMemoryDataTree, "4", "l1", "l5", "l3")),
113             "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
114                     "}] violates unique constraint on [l1, l3] of ",
115                     "(foo?revision=2016-05-17)my-container, my-leaf-3",
116                     "(foo?revision=2016-05-17)my-leaf-1");
117         writeMapEntry(inMemoryDataTree, "4", "l4", "l5", "l6");
118         verifyExceptionMessage(assertThrows(UniqueConstraintException.class,
119             () -> writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7")),
120             "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
121             "}] violates unique constraint on [l4, l3] of ",
122             "(foo?revision=2016-05-17)my-leaf-1",
123             "(foo?revision=2016-05-17)my-leaf-2");
124         removeMapEntry(inMemoryDataTree, taskEntryKey("3"));
125         writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
126         writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
127         verifyExceptionMessage(assertThrows(UniqueConstraintException.class,
128             () -> writeMapEntry(inMemoryDataTree, "6", "l3", "l4", "l11")),
129             "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
130             "}] violates unique constraint on [l4, l3] of ",
131             "(foo?revision=2016-05-17)my-leaf-1",
132             "(foo?revision=2016-05-17)my-leaf-2");
133
134         if (clear) {
135             removeMapEntry(inMemoryDataTree, taskEntryKey("1"));
136             removeMapEntry(inMemoryDataTree, taskEntryKey("2"));
137             removeMapEntry(inMemoryDataTree, taskEntryKey("4"));
138             removeMapEntry(inMemoryDataTree, taskEntryKey("5"));
139             removeMapEntry(inMemoryDataTree, taskEntryKey("10"));
140         }
141     }
142
143     private static void verifyExceptionMessage(final Exception ex, final String expectedStart,
144             final String... expectedLeaves) {
145         verifyExceptionMessage(expectedStart,  ex.getMessage(), expectedLeaves);
146     }
147
148     private static void verifyExceptionMessage(final String expectedStart, final String message,
149             final String... leafs) {
150         assertThat(message, startsWith(expectedStart));
151         for (final String leaf : leafs) {
152             assertThat(message, containsString(leaf));
153         }
154     }
155
156     private static void writeMap(final InMemoryDataTree inMemoryDataTree, final boolean withUniqueViolation)
157             throws DataValidationFailedException {
158         final MapNode taskNode = Builders
159                 .mapBuilder()
160                 .withNodeIdentifier(new NodeIdentifier(TASK))
161                 .withChild(createMapEntry("1", "l1", "l2", "l3"))
162                 .withChild(createMapEntry("2", "l2", "l3", "l4"))
163                 .withChild(
164                         withUniqueViolation ? createMapEntry("3", "l1", "l2", "l10") : createMapEntry("3", "l3", "l4",
165                                 "l5")).build();
166
167         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
168         modificationTree.write(YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK), taskNode);
169         modificationTree.ready();
170         inMemoryDataTree.validate(modificationTree);
171         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
172         inMemoryDataTree.commit(prepare);
173     }
174
175     private static void writeMapEntry(final InMemoryDataTree inMemoryDataTree, final Object taskIdValue,
176             final Object myLeaf1Value, final Object myLeaf2Value, final Object myLeaf3Value)
177             throws DataValidationFailedException {
178         final MapEntryNode taskEntryNode = Builders
179                 .mapEntryBuilder()
180                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskIdValue))
181                 .withChild(ImmutableNodes.leafNode(TASK_ID, taskIdValue))
182                 .withChild(ImmutableNodes.leafNode(MY_LEAF_1, myLeaf1Value))
183                 .withChild(ImmutableNodes.leafNode(MY_LEAF_2, myLeaf2Value))
184                 .withChild(
185                         Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(MY_CONTAINER))
186                                 .withChild(ImmutableNodes.leafNode(MY_LEAF_3, myLeaf3Value)).build()).build();
187
188         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
189         modificationTree.write(
190                 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
191                         .node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskIdValue)),
192                 taskEntryNode);
193         modificationTree.ready();
194         inMemoryDataTree.validate(modificationTree);
195         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
196         inMemoryDataTree.commit(prepare);
197     }
198
199     private static void removeMapEntry(final InMemoryDataTree inMemoryDataTree,
200             final NodeIdentifierWithPredicates mapEntryKey) throws DataValidationFailedException {
201         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
202         modificationTree.delete(YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK).node(mapEntryKey));
203         modificationTree.ready();
204         inMemoryDataTree.validate(modificationTree);
205         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
206         inMemoryDataTree.commit(prepare);
207     }
208
209     private static MapEntryNode createMapEntry(final Object taskIdValue, final Object myLeaf1Value,
210             final Object myLeaf2Value, final Object myLeaf3Value) throws DataValidationFailedException {
211         return Builders
212                 .mapEntryBuilder()
213                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskIdValue))
214                 .withChild(ImmutableNodes.leafNode(TASK_ID, taskIdValue))
215                 .withChild(ImmutableNodes.leafNode(MY_LEAF_1, myLeaf1Value))
216                 .withChild(ImmutableNodes.leafNode(MY_LEAF_2, myLeaf2Value))
217                 .withChild(
218                         Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(MY_CONTAINER))
219                                 .withChild(ImmutableNodes.leafNode(MY_LEAF_3, myLeaf3Value)).build()).build();
220     }
221
222     private static NodeIdentifierWithPredicates taskEntryKey(final String taskId) {
223         return NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskId);
224     }
225
226     @Test
227     public void disabledUniqueIndexTest() throws ReactorException, DataValidationFailedException {
228         final InMemoryDataTree inMemoryDataTree = initDataTree(TEST_MODEL, false);
229
230         writeMapEntry(inMemoryDataTree, "1", "l1", "l2", "l3");
231         writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l4");
232         writeMapEntry(inMemoryDataTree, "3", "l3", "l4", "l5");
233         writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l6");
234         writeMapEntry(inMemoryDataTree, "10", "l2", "l10", "l4");
235         writeMapEntry(inMemoryDataTree, "4", "l1", "l5", "l3");
236         writeMapEntry(inMemoryDataTree, "4", "l4", "l5", "l6");
237         writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
238         removeMapEntry(inMemoryDataTree, taskEntryKey("3"));
239         writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
240         writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
241         writeMapEntry(inMemoryDataTree, "6", "l3", "l4", "l7");
242     }
243
244     private static InMemoryDataTree initDataTree(final EffectiveModelContext schemaContext, final boolean uniqueIndex)
245             throws DataValidationFailedException {
246         final InMemoryDataTree inMemoryDataTree = (InMemoryDataTree) new InMemoryDataTreeFactory().create(
247             new DataTreeConfiguration.Builder(TreeType.CONFIGURATION).setUniqueIndexes(uniqueIndex).build());
248         inMemoryDataTree.setEffectiveModelContext(schemaContext);
249
250         final MapNode taskNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(TASK)).build();
251         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
252         modificationTree.write(YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK), taskNode);
253         modificationTree.ready();
254
255         inMemoryDataTree.validate(modificationTree);
256         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
257         inMemoryDataTree.commit(prepare);
258         return inMemoryDataTree;
259     }
260
261     private static InMemoryDataTree emptyDataTree(final EffectiveModelContext schemaContext, final boolean uniqueIndex)
262             throws DataValidationFailedException {
263         final InMemoryDataTree inMemoryDataTree = (InMemoryDataTree) new InMemoryDataTreeFactory().create(
264             new DataTreeConfiguration.Builder(TreeType.CONFIGURATION).setUniqueIndexes(uniqueIndex).build());
265         inMemoryDataTree.setEffectiveModelContext(schemaContext);
266
267         return inMemoryDataTree;
268     }
269 }