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.impl.schema.Builders;
27 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
28 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
29 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
30 import org.opendaylight.yangtools.yang.data.tree.api.TreeType;
31 import org.opendaylight.yangtools.yang.data.tree.api.UniqueConstraintException;
32 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
33 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
35 class UniqueConstraintTest {
36 private static final String NS = "foo";
37 private static final String REV = "2016-05-17";
38 private static final QName TASK_CONTAINER = QName.create(NS, REV, "task-container");
39 private static final QName TASK = QName.create(NS, REV, "task");
40 private static final QName TASK_ID = QName.create(NS, REV, "task-id");
41 private static final QName MY_LEAF_1 = QName.create(NS, REV, "my-leaf-1");
42 private static final QName MY_LEAF_2 = QName.create(NS, REV, "my-leaf-2");
43 private static final QName MY_LEAF_3 = QName.create(NS, REV, "my-leaf-3");
44 private static final QName MY_CONTAINER = QName.create(NS, REV, "my-container");
46 private static EffectiveModelContext TEST_MODEL;
49 static void beforeClass() {
50 TEST_MODEL = TestModel.createTestContext("/yt570.yang");
54 void switchEntriesTest() throws DataValidationFailedException {
55 final var inMemoryDataTree = initDataTree(TEST_MODEL, true);
56 writeMapEntry(inMemoryDataTree, "1", "l1", "l2", "l3");
57 writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l4");
59 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
61 final var mapEntry1 = createMapEntry("1", "l2", "l3", "l4");
62 final var mapEntry2 = createMapEntry("2", "l1", "l2", "l3");
64 //switch values of map entries
65 modificationTree.write(
66 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
67 .node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "1")), mapEntry1);
68 modificationTree.write(
69 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
70 .node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "2")), mapEntry2);
72 modificationTree.ready();
73 inMemoryDataTree.validate(modificationTree);
74 final var prepare = inMemoryDataTree.prepare(modificationTree);
75 inMemoryDataTree.commit(prepare);
79 void mapTest() throws DataValidationFailedException {
80 final var inMemoryDataTree = emptyDataTree(TEST_MODEL, true);
83 verifyException(assertThrows(UniqueValidationFailedException.class,
84 () -> writeMap(inMemoryDataTree, true)),
85 "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
86 "}] violates unique constraint on [l1, l2] of ",
87 "(foo?revision=2016-05-17)my-leaf-1",
88 "(foo?revision=2016-05-17)my-leaf-2]");
90 writeMap(inMemoryDataTree, false);
91 verifyException(assertThrows(UniqueConstraintException.class,
92 () -> writeMapEntry(inMemoryDataTree, "4", "l1", "l2", "l30")),
93 "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
94 "}] violates unique constraint on [l1, l2] of ",
95 "(foo?revision=2016-05-17)my-leaf-1",
96 "(foo?revision=2016-05-17)my-leaf-2");
100 void mapEntryTest() throws DataValidationFailedException {
101 final var inMemoryDataTree = initDataTree(TEST_MODEL, true);
102 writeAndRemoveMapEntries(inMemoryDataTree, true);
103 writeAndRemoveMapEntries(inMemoryDataTree, false);
106 private static void writeAndRemoveMapEntries(final InMemoryDataTree inMemoryDataTree, final boolean clear)
107 throws DataValidationFailedException {
108 writeMapEntry(inMemoryDataTree, "1", "l1", "l2", "l3");
109 writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l4");
110 writeMapEntry(inMemoryDataTree, "3", "l3", "l4", "l5");
111 writeMapEntry(inMemoryDataTree, "2", "l2", "l3", "l6");
112 writeMapEntry(inMemoryDataTree, "10", "l2", "l10", "l4");
113 verifyException(assertThrows(UniqueConstraintException.class,
114 () -> writeMapEntry(inMemoryDataTree, "4", "l1", "l5", "l3")),
115 "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
116 "}] violates unique constraint on [l1, l3] of ",
117 "(foo?revision=2016-05-17)my-container, my-leaf-3",
118 "(foo?revision=2016-05-17)my-leaf-1");
119 writeMapEntry(inMemoryDataTree, "4", "l4", "l5", "l6");
120 verifyException(assertThrows(UniqueConstraintException.class,
121 () -> writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7")),
122 "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
123 "}] violates unique constraint on [l3, l4] of ",
124 "(foo?revision=2016-05-17)my-leaf-1",
125 "(foo?revision=2016-05-17)my-leaf-2");
126 removeMapEntry(inMemoryDataTree, taskEntryKey("3"));
127 writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
128 writeMapEntry(inMemoryDataTree, "5", "l3", "l4", "l7");
129 verifyException(assertThrows(UniqueConstraintException.class,
130 () -> writeMapEntry(inMemoryDataTree, "6", "l3", "l4", "l11")),
131 "(foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=",
132 "}] violates unique constraint on [l3, l4] of ",
133 "(foo?revision=2016-05-17)my-leaf-1",
134 "(foo?revision=2016-05-17)my-leaf-2");
137 removeMapEntry(inMemoryDataTree, taskEntryKey("1"));
138 removeMapEntry(inMemoryDataTree, taskEntryKey("2"));
139 removeMapEntry(inMemoryDataTree, taskEntryKey("4"));
140 removeMapEntry(inMemoryDataTree, taskEntryKey("5"));
141 removeMapEntry(inMemoryDataTree, taskEntryKey("10"));
145 private static void verifyException(final Exception ex, final String expectedStart,
146 final String... expectedLeaves) {
147 verifyExceptionMessage(expectedStart, ex.getMessage(), expectedLeaves);
148 assertInstanceOf(YangNetconfErrorAware.class, ex);
149 final var errors = ((YangNetconfErrorAware) ex).getNetconfErrors();
150 assertEquals(1, errors.size());
151 final var error = errors.get(0);
152 assertEquals(ErrorSeverity.ERROR, error.severity());
153 assertEquals(ErrorType.APPLICATION, error.type());
154 assertEquals(ErrorTag.OPERATION_FAILED, error.tag());
155 assertEquals("data-not-unique", error.appTag());
158 private static void verifyExceptionMessage(final String expectedStart, final String message,
159 final String... leafs) {
160 assertTrue(message.startsWith(expectedStart));
161 for (final var leaf : leafs) {
162 assertTrue(message.contains(leaf));
166 private static void writeMap(final InMemoryDataTree inMemoryDataTree, final boolean withUniqueViolation)
167 throws DataValidationFailedException {
168 final var taskNode = Builders
170 .withNodeIdentifier(new NodeIdentifier(TASK))
171 .withChild(createMapEntry("1", "l1", "l2", "l3"))
172 .withChild(createMapEntry("2", "l2", "l3", "l4"))
174 withUniqueViolation ? createMapEntry("3", "l1", "l2", "l10") : createMapEntry("3", "l3", "l4",
177 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
178 modificationTree.write(YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK), taskNode);
179 modificationTree.ready();
180 inMemoryDataTree.validate(modificationTree);
181 final var prepare = inMemoryDataTree.prepare(modificationTree);
182 inMemoryDataTree.commit(prepare);
185 private static void writeMapEntry(final InMemoryDataTree inMemoryDataTree, final Object taskIdValue,
186 final Object myLeaf1Value, final Object myLeaf2Value, final Object myLeaf3Value)
187 throws DataValidationFailedException {
188 final var taskEntryNode = Builders
190 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskIdValue))
191 .withChild(ImmutableNodes.leafNode(TASK_ID, taskIdValue))
192 .withChild(ImmutableNodes.leafNode(MY_LEAF_1, myLeaf1Value))
193 .withChild(ImmutableNodes.leafNode(MY_LEAF_2, myLeaf2Value))
195 Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(MY_CONTAINER))
196 .withChild(ImmutableNodes.leafNode(MY_LEAF_3, myLeaf3Value)).build()).build();
198 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
199 modificationTree.write(
200 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
201 .node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskIdValue)),
203 modificationTree.ready();
204 inMemoryDataTree.validate(modificationTree);
205 final var prepare = inMemoryDataTree.prepare(modificationTree);
206 inMemoryDataTree.commit(prepare);
209 private static void removeMapEntry(final InMemoryDataTree inMemoryDataTree,
210 final NodeIdentifierWithPredicates mapEntryKey) throws DataValidationFailedException {
211 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
212 modificationTree.delete(YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK).node(mapEntryKey));
213 modificationTree.ready();
214 inMemoryDataTree.validate(modificationTree);
215 final var prepare = inMemoryDataTree.prepare(modificationTree);
216 inMemoryDataTree.commit(prepare);
219 private static MapEntryNode createMapEntry(final Object taskIdValue, final Object myLeaf1Value,
220 final Object myLeaf2Value, final Object myLeaf3Value) {
223 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, taskIdValue))
224 .withChild(ImmutableNodes.leafNode(TASK_ID, taskIdValue))
225 .withChild(ImmutableNodes.leafNode(MY_LEAF_1, myLeaf1Value))
226 .withChild(ImmutableNodes.leafNode(MY_LEAF_2, myLeaf2Value))
228 Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(MY_CONTAINER))
229 .withChild(ImmutableNodes.leafNode(MY_LEAF_3, myLeaf3Value)).build()).build();
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 = Builders.mapBuilder().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;