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.restful.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;
18 import com.google.common.util.concurrent.Futures;
19 import java.lang.reflect.Field;
20 import java.util.ArrayList;
21 import java.util.List;
22 import org.junit.Before;
23 import org.junit.Test;
24 import org.mockito.Mock;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
27 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
28 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
29 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
30 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
31 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
32 import org.opendaylight.netconf.sal.restconf.impl.PATCHEntity;
33 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
34 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusEntity;
35 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
36 import org.opendaylight.restconf.RestConnectorProvider;
37 import org.opendaylight.restconf.common.references.SchemaContextRef;
38 import org.opendaylight.restconf.handlers.TransactionChainHandler;
39 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
40 import org.opendaylight.yangtools.yang.common.QName;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
46 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
47 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
49 public class PatchDataTransactionUtilTest {
51 private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
54 private DOMTransactionChain transactionChain;
57 private DOMDataReadWriteTransaction rWTransaction;
59 private SchemaContextRef refSchemaCtx;
60 private YangInstanceIdentifier iIDCreateAndDelete;
61 private YangInstanceIdentifier iIDMerge;
62 private ContainerNode buildBaseContainerForTests;
63 private YangInstanceIdentifier targetNodeForCreateAndDelete;
64 private YangInstanceIdentifier targetNodeMerge;
65 private MapNode buildArtistList;
67 // Fields used when delete operation fails to reset transaction chain
68 private static Field handler;
69 private static Field broker;
72 public void setUp() throws Exception {
75 PatchDataTransactionUtilTest.handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
76 PatchDataTransactionUtilTest.broker = RestConnectorProvider.class.getDeclaredField("dataBroker");
78 PatchDataTransactionUtilTest.handler.setAccessible(true);
79 PatchDataTransactionUtilTest.handler.set(RestConnectorProvider.class, mock(TransactionChainHandler.class));
81 PatchDataTransactionUtilTest.broker.setAccessible(true);
82 PatchDataTransactionUtilTest.broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
84 refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
85 final QName baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
86 final QName containerPlayerQName = QName.create(baseQName, "player");
87 final QName leafGapQName = QName.create(baseQName, "gap");
88 final QName containerLibraryQName = QName.create(baseQName, "library");
89 final QName listArtistQName = QName.create(baseQName, "artist");
90 final QName leafNameQName = QName.create(baseQName, "name");
91 final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey =
92 new YangInstanceIdentifier.NodeIdentifierWithPredicates(listArtistQName, leafNameQName, "name of artist");
94 /** instance identifier for accessing leaf node "gap" */
95 iIDCreateAndDelete = YangInstanceIdentifier.builder()
97 .node(containerPlayerQName)
101 /** values that are used for creating leaf for testPatchDataCreateAndDelete test */
102 final LeafNode buildGapLeaf = Builders.leafBuilder()
103 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafGapQName))
107 final ContainerNode buildPlayerContainer = Builders.containerBuilder()
108 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQName))
109 .withChild(buildGapLeaf)
112 buildBaseContainerForTests = Builders.containerBuilder()
113 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
114 .withChild(buildPlayerContainer)
117 targetNodeForCreateAndDelete = YangInstanceIdentifier.builder(iIDCreateAndDelete)
118 .node(containerPlayerQName)
122 /** instance identifier for accessing leaf node "name" in list "artist" */
123 iIDMerge = YangInstanceIdentifier.builder()
125 .node(containerLibraryQName)
126 .node(listArtistQName)
127 .nodeWithKey(listArtistQName, QName.create(listArtistQName, "name"), "name of artist")
131 /** values that are used for creating leaf for testPatchDataReplaceMergeAndRemove test */
132 final LeafNode<Object> contentName = Builders.leafBuilder()
133 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
134 .withValue("name of artist")
137 final LeafNode<Object> contentDescription = Builders.leafBuilder()
138 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
139 .withValue("description of artist")
142 final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
143 .withNodeIdentifier(nodeWithKey)
144 .withChild(contentName)
145 .withChild(contentDescription)
148 buildArtistList = Builders.mapBuilder()
149 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listArtistQName))
150 .withChild(mapEntryNode)
153 targetNodeMerge = YangInstanceIdentifier.builder()
155 .node(containerLibraryQName)
156 .node(listArtistQName)
157 .nodeWithKey(listArtistQName, leafNameQName, "name of artist")
161 doReturn(rWTransaction).when(transactionChain).newReadWriteTransaction();
162 doReturn(Futures.immediateCheckedFuture(null)).when(rWTransaction).submit();
166 public void testPatchDataReplaceMergeAndRemove() {
167 doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
168 .when(rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, targetNodeMerge);
170 final PATCHEntity entityReplace = new PATCHEntity("edit1", "REPLACE", targetNodeMerge, buildArtistList);
171 final PATCHEntity entityMerge = new PATCHEntity("edit2", "MERGE", targetNodeMerge, buildArtistList);
172 final PATCHEntity entityRemove = new PATCHEntity("edit3", "REMOVE", targetNodeMerge);
173 final List<PATCHEntity> entities = new ArrayList<>();
175 entities.add(entityReplace);
176 entities.add(entityMerge);
177 entities.add(entityRemove);
179 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
180 new InstanceIdentifierContext<>(iIDMerge, null, null, refSchemaCtx.get());
181 final PATCHContext patchContext = new PATCHContext(iidContext, entities, "patchRMRm");
182 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChain);
183 final PATCHStatusContext patchStatusContext =
184 PatchDataTransactionUtil.patchData(patchContext, wrapper, refSchemaCtx);
186 for (PATCHStatusEntity entity : patchStatusContext.getEditCollection()) {
187 assertTrue(entity.isOk());
189 assertTrue(patchStatusContext.isOk());
193 public void testPatchDataCreateAndDelete() throws Exception {
194 doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
195 .when(rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, targetNodeForCreateAndDelete);
197 final PATCHEntity entityCreate =
198 new PATCHEntity("edit1", "CREATE", targetNodeForCreateAndDelete, buildBaseContainerForTests);
199 final PATCHEntity entityDelete =
200 new PATCHEntity("edit2", "DELETE", targetNodeForCreateAndDelete);
201 final List<PATCHEntity> entities = new ArrayList<>();
203 entities.add(entityCreate);
204 entities.add(entityDelete);
206 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
207 new InstanceIdentifierContext<>(iIDCreateAndDelete, null, null, refSchemaCtx.get());
208 final PATCHContext patchContext = new PATCHContext(iidContext, entities, "patchCD");
209 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChain);
210 final PATCHStatusContext patchStatusContext = PatchDataTransactionUtil.patchData(patchContext, wrapper, refSchemaCtx);
212 for (PATCHStatusEntity entity : patchStatusContext.getEditCollection()) {
213 assertTrue(entity.isOk());
215 assertTrue(patchStatusContext.isOk());
219 public void deleteNonexistentDataTest() {
220 doReturn(Futures.immediateCheckedFuture(false))
221 .when(rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, targetNodeForCreateAndDelete);
223 final PATCHEntity entityDelete =
224 new PATCHEntity("edit", "DELETE", targetNodeForCreateAndDelete);
225 final List<PATCHEntity> entities = new ArrayList<>();
227 entities.add(entityDelete);
229 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
230 new InstanceIdentifierContext<>(iIDCreateAndDelete, null, null, refSchemaCtx.get());
231 final PATCHContext patchContext = new PATCHContext(iidContext, entities, "patchD");
232 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChain);
233 final PATCHStatusContext patchStatusContext = PatchDataTransactionUtil.patchData(patchContext, wrapper, refSchemaCtx);
235 assertFalse(patchStatusContext.isOk());
236 assertEquals(RestconfError.ErrorType.PROTOCOL,
237 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorType());
238 assertEquals(RestconfError.ErrorTag.DATA_MISSING,
239 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorTag());
243 public void testPatchMergePutContainer() throws Exception {
244 doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
245 .when(rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, targetNodeForCreateAndDelete);
247 final PATCHEntity entityMerge =
248 new PATCHEntity("edit1", "MERGE", targetNodeForCreateAndDelete, buildBaseContainerForTests);
249 final List<PATCHEntity> entities = new ArrayList<>();
251 entities.add(entityMerge);
253 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
254 new InstanceIdentifierContext<>(iIDCreateAndDelete, null, null, refSchemaCtx.get());
255 final PATCHContext patchContext = new PATCHContext(iidContext, entities, "patchM");
256 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChain);
257 final PATCHStatusContext patchStatusContext = PatchDataTransactionUtil.patchData(patchContext, wrapper, refSchemaCtx);
259 for (PATCHStatusEntity entity : patchStatusContext.getEditCollection()) {
260 assertTrue(entity.isOk());
262 assertTrue(patchStatusContext.isOk());