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
9 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertTrue;
14 import static org.mockito.Mockito.doReturn;
15 import static org.mockito.Mockito.mock;
16 import static org.mockito.MockitoAnnotations.initMocks;
17 import static org.opendaylight.restconf.common.patch.PatchEditOperation.CREATE;
18 import static org.opendaylight.restconf.common.patch.PatchEditOperation.DELETE;
19 import static org.opendaylight.restconf.common.patch.PatchEditOperation.MERGE;
20 import static org.opendaylight.restconf.common.patch.PatchEditOperation.REMOVE;
21 import static org.opendaylight.restconf.common.patch.PatchEditOperation.REPLACE;
23 import com.google.common.util.concurrent.Futures;
24 import java.lang.reflect.Field;
25 import java.util.ArrayList;
26 import java.util.List;
27 import org.junit.Before;
28 import org.junit.Test;
29 import org.mockito.Mock;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
33 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
34 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
35 import org.opendaylight.restconf.common.errors.RestconfError;
36 import org.opendaylight.restconf.common.patch.PatchContext;
37 import org.opendaylight.restconf.common.patch.PatchEntity;
38 import org.opendaylight.restconf.common.patch.PatchStatusContext;
39 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
40 import org.opendaylight.restconf.nb.rfc8040.RestConnectorProvider;
41 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
42 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
43 import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
44 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
45 import org.opendaylight.yangtools.yang.common.QName;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
47 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
51 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
52 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
53 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
55 public class PatchDataTransactionUtilTest {
57 private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
60 private DOMTransactionChain transactionChain;
63 private DOMDataReadWriteTransaction rwTransaction;
65 private SchemaContextRef refSchemaCtx;
66 private YangInstanceIdentifier instanceIdContainer;
67 private YangInstanceIdentifier instanceIdCreateAndDelete;
68 private YangInstanceIdentifier instanceIdMerge;
69 private ContainerNode buildBaseContainerForTests;
70 private YangInstanceIdentifier targetNodeForCreateAndDelete;
71 private YangInstanceIdentifier targetNodeMerge;
72 private MapNode buildArtistList;
74 // Fields used when delete operation fails to reset transaction chain
75 private static Field handler;
76 private static Field broker;
79 public void setUp() throws Exception {
82 PatchDataTransactionUtilTest.handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
83 PatchDataTransactionUtilTest.broker = RestConnectorProvider.class.getDeclaredField("dataBroker");
85 PatchDataTransactionUtilTest.handler.setAccessible(true);
86 PatchDataTransactionUtilTest.handler.set(RestConnectorProvider.class, mock(TransactionChainHandler.class));
88 PatchDataTransactionUtilTest.broker.setAccessible(true);
89 PatchDataTransactionUtilTest.broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
91 this.refSchemaCtx = new SchemaContextRef(
92 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles(PATH_FOR_NEW_SCHEMA_CONTEXT)));
93 final QName baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
94 final QName containerPlayerQName = QName.create(baseQName, "player");
95 final QName leafGapQName = QName.create(baseQName, "gap");
96 final QName containerLibraryQName = QName.create(baseQName, "library");
97 final QName listArtistQName = QName.create(baseQName, "artist");
98 final QName leafNameQName = QName.create(baseQName, "name");
99 final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey =
100 new YangInstanceIdentifier.NodeIdentifierWithPredicates(listArtistQName, leafNameQName, "name of artist");
102 /* instance identifier for accessing container node "player" */
103 this.instanceIdContainer = YangInstanceIdentifier.builder()
105 .node(containerPlayerQName)
108 /* instance identifier for accessing leaf node "gap" */
109 this.instanceIdCreateAndDelete = instanceIdContainer.node(leafGapQName);
111 /* values that are used for creating leaf for testPatchDataCreateAndDelete test */
112 final LeafNode<?> buildGapLeaf = Builders.leafBuilder()
113 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafGapQName))
117 final ContainerNode buildPlayerContainer = Builders.containerBuilder()
118 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQName))
119 .withChild(buildGapLeaf)
122 this.buildBaseContainerForTests = Builders.containerBuilder()
123 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
124 .withChild(buildPlayerContainer)
127 this.targetNodeForCreateAndDelete = YangInstanceIdentifier.builder(this.instanceIdCreateAndDelete)
128 .node(containerPlayerQName)
132 /* instance identifier for accessing leaf node "name" in list "artist" */
133 this.instanceIdMerge = YangInstanceIdentifier.builder()
135 .node(containerLibraryQName)
136 .node(listArtistQName)
137 .nodeWithKey(listArtistQName, QName.create(listArtistQName, "name"), "name of artist")
141 /* values that are used for creating leaf for testPatchDataReplaceMergeAndRemove test */
142 final LeafNode<Object> contentName = Builders.leafBuilder()
143 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
144 .withValue("name of artist")
147 final LeafNode<Object> contentDescription = Builders.leafBuilder()
148 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
149 .withValue("description of artist")
152 final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
153 .withNodeIdentifier(nodeWithKey)
154 .withChild(contentName)
155 .withChild(contentDescription)
158 this.buildArtistList = Builders.mapBuilder()
159 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listArtistQName))
160 .withChild(mapEntryNode)
163 this.targetNodeMerge = YangInstanceIdentifier.builder()
165 .node(containerLibraryQName)
166 .node(listArtistQName)
167 .nodeWithKey(listArtistQName, leafNameQName, "name of artist")
171 doReturn(this.rwTransaction).when(this.transactionChain).newReadWriteTransaction();
172 doReturn(Futures.immediateCheckedFuture(null)).when(this.rwTransaction).submit();
176 public void testPatchDataReplaceMergeAndRemove() {
177 doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
178 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeMerge);
180 final PatchEntity entityReplace =
181 new PatchEntity("edit1", REPLACE, this.targetNodeMerge, this.buildArtistList);
182 final PatchEntity entityMerge = new PatchEntity("edit2", MERGE, this.targetNodeMerge, this.buildArtistList);
183 final PatchEntity entityRemove = new PatchEntity("edit3", REMOVE, this.targetNodeMerge);
184 final List<PatchEntity> entities = new ArrayList<>();
186 entities.add(entityReplace);
187 entities.add(entityMerge);
188 entities.add(entityRemove);
190 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
191 new InstanceIdentifierContext<>(this.instanceIdMerge, null, null, this.refSchemaCtx.get());
192 final PatchContext patchContext = new PatchContext(iidContext, entities, "patchRMRm");
193 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
194 final PatchStatusContext patchStatusContext =
195 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
197 for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
198 assertTrue(entity.isOk());
200 assertTrue(patchStatusContext.isOk());
204 public void testPatchDataCreateAndDelete() throws Exception {
205 doReturn(Futures.immediateCheckedFuture(false))
206 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.instanceIdContainer);
207 doReturn(Futures.immediateCheckedFuture(true))
208 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
210 final PatchEntity entityCreate =
211 new PatchEntity("edit1", CREATE, this.instanceIdContainer, this.buildBaseContainerForTests);
212 final PatchEntity entityDelete =
213 new PatchEntity("edit2", DELETE, this.targetNodeForCreateAndDelete);
214 final List<PatchEntity> entities = new ArrayList<>();
216 entities.add(entityCreate);
217 entities.add(entityDelete);
219 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
220 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
221 final PatchContext patchContext = new PatchContext(iidContext, entities, "patchCD");
222 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
223 final PatchStatusContext patchStatusContext =
224 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
226 for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
227 assertTrue("Edit " + entity.getEditId() + " failed", entity.isOk());
229 assertTrue(patchStatusContext.isOk());
233 public void deleteNonexistentDataTest() {
234 doReturn(Futures.immediateCheckedFuture(false))
235 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
237 final PatchEntity entityDelete =
238 new PatchEntity("edit", DELETE, this.targetNodeForCreateAndDelete);
239 final List<PatchEntity> entities = new ArrayList<>();
241 entities.add(entityDelete);
243 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
244 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
245 final PatchContext patchContext = new PatchContext(iidContext, entities, "patchD");
246 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
247 final PatchStatusContext patchStatusContext =
248 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
250 assertFalse(patchStatusContext.isOk());
251 assertEquals(RestconfError.ErrorType.PROTOCOL,
252 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorType());
253 assertEquals(RestconfError.ErrorTag.DATA_MISSING,
254 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorTag());
258 public void testPatchMergePutContainer() throws Exception {
259 doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
260 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
262 final PatchEntity entityMerge =
263 new PatchEntity("edit1", MERGE, this.instanceIdContainer, this.buildBaseContainerForTests);
264 final List<PatchEntity> entities = new ArrayList<>();
266 entities.add(entityMerge);
268 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
269 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
270 final PatchContext patchContext = new PatchContext(iidContext, entities, "patchM");
271 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
272 final PatchStatusContext patchStatusContext =
273 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
275 for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
276 assertTrue(entity.isOk());
278 assertTrue(patchStatusContext.isOk());