2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.tree.impl;
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;
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;
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");
45 private static EffectiveModelContext TEST_MODEL;
48 static void beforeClass() {
49 TEST_MODEL = TestModel.createTestContext("/yt570.yang");
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");
58 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
60 final var mapEntry1 = createMapEntry("1", "l2", "l3", "l4");
61 final var mapEntry2 = createMapEntry("2", "l1", "l2", "l3");
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);
71 modificationTree.ready();
72 inMemoryDataTree.validate(modificationTree);
73 final var prepare = inMemoryDataTree.prepare(modificationTree);
74 inMemoryDataTree.commit(prepare);
78 void mapTest() throws DataValidationFailedException {
79 final var inMemoryDataTree = emptyDataTree(TEST_MODEL, true);
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]");
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");
99 void mapEntryTest() throws DataValidationFailedException {
100 final var inMemoryDataTree = initDataTree(TEST_MODEL, true);
101 writeAndRemoveMapEntries(inMemoryDataTree, true);
102 writeAndRemoveMapEntries(inMemoryDataTree, false);
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");
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"));
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());
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));
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"))
172 withUniqueViolation ? createMapEntry("3", "l1", "l2", "l10") : createMapEntry("3", "l3", "l4", "l5"))
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);
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))
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)),
202 modificationTree.ready();
203 inMemoryDataTree.validate(modificationTree);
204 final var prepare = inMemoryDataTree.prepare(modificationTree);
205 inMemoryDataTree.commit(prepare);
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);
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))
232 private static NodeIdentifierWithPredicates taskEntryKey(final String taskId) {
233 return NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskId);
237 void disabledUniqueIndexTest() throws DataValidationFailedException {
238 final var inMemoryDataTree = initDataTree(TEST_MODEL, false);
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");
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);
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();
265 inMemoryDataTree.validate(modificationTree);
266 final var prepare = inMemoryDataTree.prepare(modificationTree);
267 inMemoryDataTree.commit(prepare);
268 return inMemoryDataTree;
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);
277 return inMemoryDataTree;