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;
48 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
50 public class PatchDataTransactionUtilTest {
52 private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
55 private DOMTransactionChain transactionChain;
58 private DOMDataReadWriteTransaction rWTransaction;
60 private SchemaContextRef refSchemaCtx;
61 private YangInstanceIdentifier iIDCreateAndDelete;
62 private YangInstanceIdentifier iIDMerge;
63 private ContainerNode buildBaseContainerForTests;
64 private YangInstanceIdentifier targetNodeForCreateAndDelete;
65 private YangInstanceIdentifier targetNodeMerge;
66 private MapNode buildArtistList;
68 // Fields used when delete operation fails to reset transaction chain
69 private static Field handler;
70 private static Field broker;
73 public void setUp() throws Exception {
76 PatchDataTransactionUtilTest.handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
77 PatchDataTransactionUtilTest.broker = RestConnectorProvider.class.getDeclaredField("dataBroker");
79 PatchDataTransactionUtilTest.handler.setAccessible(true);
80 PatchDataTransactionUtilTest.handler.set(RestConnectorProvider.class, mock(TransactionChainHandler.class));
82 PatchDataTransactionUtilTest.broker.setAccessible(true);
83 PatchDataTransactionUtilTest.broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
85 this.refSchemaCtx = new SchemaContextRef(
86 YangParserTestUtils.parseYangSources(TestRestconfUtils.loadFiles(PATH_FOR_NEW_SCHEMA_CONTEXT)));
87 final QName baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
88 final QName containerPlayerQName = QName.create(baseQName, "player");
89 final QName leafGapQName = QName.create(baseQName, "gap");
90 final QName containerLibraryQName = QName.create(baseQName, "library");
91 final QName listArtistQName = QName.create(baseQName, "artist");
92 final QName leafNameQName = QName.create(baseQName, "name");
93 final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey =
94 new YangInstanceIdentifier.NodeIdentifierWithPredicates(listArtistQName, leafNameQName, "name of artist");
96 /** instance identifier for accessing leaf node "gap" */
97 this.iIDCreateAndDelete = YangInstanceIdentifier.builder()
99 .node(containerPlayerQName)
103 /** values that are used for creating leaf for testPatchDataCreateAndDelete test */
104 final LeafNode buildGapLeaf = Builders.leafBuilder()
105 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafGapQName))
109 final ContainerNode buildPlayerContainer = Builders.containerBuilder()
110 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQName))
111 .withChild(buildGapLeaf)
114 this.buildBaseContainerForTests = Builders.containerBuilder()
115 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
116 .withChild(buildPlayerContainer)
119 this.targetNodeForCreateAndDelete = YangInstanceIdentifier.builder(this.iIDCreateAndDelete)
120 .node(containerPlayerQName)
124 /** instance identifier for accessing leaf node "name" in list "artist" */
125 this.iIDMerge = YangInstanceIdentifier.builder()
127 .node(containerLibraryQName)
128 .node(listArtistQName)
129 .nodeWithKey(listArtistQName, QName.create(listArtistQName, "name"), "name of artist")
133 /** values that are used for creating leaf for testPatchDataReplaceMergeAndRemove test */
134 final LeafNode<Object> contentName = Builders.leafBuilder()
135 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
136 .withValue("name of artist")
139 final LeafNode<Object> contentDescription = Builders.leafBuilder()
140 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
141 .withValue("description of artist")
144 final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
145 .withNodeIdentifier(nodeWithKey)
146 .withChild(contentName)
147 .withChild(contentDescription)
150 this.buildArtistList = Builders.mapBuilder()
151 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listArtistQName))
152 .withChild(mapEntryNode)
155 this.targetNodeMerge = YangInstanceIdentifier.builder()
157 .node(containerLibraryQName)
158 .node(listArtistQName)
159 .nodeWithKey(listArtistQName, leafNameQName, "name of artist")
163 doReturn(this.rWTransaction).when(this.transactionChain).newReadWriteTransaction();
164 doReturn(Futures.immediateCheckedFuture(null)).when(this.rWTransaction).submit();
168 public void testPatchDataReplaceMergeAndRemove() {
169 doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
170 .when(this.rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeMerge);
172 final PATCHEntity entityReplace = new PATCHEntity("edit1", "REPLACE", this.targetNodeMerge, this.buildArtistList);
173 final PATCHEntity entityMerge = new PATCHEntity("edit2", "MERGE", this.targetNodeMerge, this.buildArtistList);
174 final PATCHEntity entityRemove = new PATCHEntity("edit3", "REMOVE", this.targetNodeMerge);
175 final List<PATCHEntity> entities = new ArrayList<>();
177 entities.add(entityReplace);
178 entities.add(entityMerge);
179 entities.add(entityRemove);
181 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
182 new InstanceIdentifierContext<>(this.iIDMerge, null, null, this.refSchemaCtx.get());
183 final PATCHContext patchContext = new PATCHContext(iidContext, entities, "patchRMRm");
184 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
185 final PATCHStatusContext patchStatusContext =
186 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
188 for (final PATCHStatusEntity entity : patchStatusContext.getEditCollection()) {
189 assertTrue(entity.isOk());
191 assertTrue(patchStatusContext.isOk());
195 public void testPatchDataCreateAndDelete() throws Exception {
196 doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
197 .when(this.rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
199 final PATCHEntity entityCreate =
200 new PATCHEntity("edit1", "CREATE", this.targetNodeForCreateAndDelete, this.buildBaseContainerForTests);
201 final PATCHEntity entityDelete =
202 new PATCHEntity("edit2", "DELETE", this.targetNodeForCreateAndDelete);
203 final List<PATCHEntity> entities = new ArrayList<>();
205 entities.add(entityCreate);
206 entities.add(entityDelete);
208 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
209 new InstanceIdentifierContext<>(this.iIDCreateAndDelete, null, null, this.refSchemaCtx.get());
210 final PATCHContext patchContext = new PATCHContext(iidContext, entities, "patchCD");
211 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
212 final PATCHStatusContext patchStatusContext = PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
214 for (final PATCHStatusEntity entity : patchStatusContext.getEditCollection()) {
215 assertTrue(entity.isOk());
217 assertTrue(patchStatusContext.isOk());
221 public void deleteNonexistentDataTest() {
222 doReturn(Futures.immediateCheckedFuture(false))
223 .when(this.rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
225 final PATCHEntity entityDelete =
226 new PATCHEntity("edit", "DELETE", this.targetNodeForCreateAndDelete);
227 final List<PATCHEntity> entities = new ArrayList<>();
229 entities.add(entityDelete);
231 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
232 new InstanceIdentifierContext<>(this.iIDCreateAndDelete, null, null, this.refSchemaCtx.get());
233 final PATCHContext patchContext = new PATCHContext(iidContext, entities, "patchD");
234 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
235 final PATCHStatusContext patchStatusContext = PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
237 assertFalse(patchStatusContext.isOk());
238 assertEquals(RestconfError.ErrorType.PROTOCOL,
239 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorType());
240 assertEquals(RestconfError.ErrorTag.DATA_MISSING,
241 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorTag());
245 public void testPatchMergePutContainer() throws Exception {
246 doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
247 .when(this.rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
249 final PATCHEntity entityMerge =
250 new PATCHEntity("edit1", "MERGE", this.targetNodeForCreateAndDelete, this.buildBaseContainerForTests);
251 final List<PATCHEntity> entities = new ArrayList<>();
253 entities.add(entityMerge);
255 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
256 new InstanceIdentifierContext<>(this.iIDCreateAndDelete, null, null, this.refSchemaCtx.get());
257 final PATCHContext patchContext = new PATCHContext(iidContext, entities, "patchM");
258 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
259 final PATCHStatusContext patchStatusContext = PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
261 for (final PATCHStatusEntity entity : patchStatusContext.getEditCollection()) {
262 assertTrue(entity.isOk());
264 assertTrue(patchStatusContext.isOk());