283db9b2012b158f81726d65900680ba560ed493
[netconf.git] / restconf / sal-rest-connector / src / test / java / org / opendaylight / restconf / restful / utils / PatchDataTransactionUtilTest.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.restconf.restful.utils;
10
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
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
49 public class PatchDataTransactionUtilTest {
50
51     private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
52
53     @Mock
54     private DOMTransactionChain transactionChain;
55
56     @Mock
57     private DOMDataReadWriteTransaction rWTransaction;
58
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;
66
67     // Fields used when delete operation fails to reset transaction chain
68     private static Field handler;
69     private static Field broker;
70
71     @Before
72     public void setUp() throws Exception {
73         initMocks(this);
74
75         PatchDataTransactionUtilTest.handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
76         PatchDataTransactionUtilTest.broker = RestConnectorProvider.class.getDeclaredField("dataBroker");
77
78         PatchDataTransactionUtilTest.handler.setAccessible(true);
79         PatchDataTransactionUtilTest.handler.set(RestConnectorProvider.class, mock(TransactionChainHandler.class));
80
81         PatchDataTransactionUtilTest.broker.setAccessible(true);
82         PatchDataTransactionUtilTest.broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
83
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");
93
94         /** instance identifier for accessing leaf node "gap" */
95         iIDCreateAndDelete = YangInstanceIdentifier.builder()
96                 .node(baseQName)
97                 .node(containerPlayerQName)
98                 .node(leafGapQName)
99                 .build();
100
101         /** values that are used for creating leaf for testPatchDataCreateAndDelete test */
102         final LeafNode buildGapLeaf = Builders.leafBuilder()
103                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafGapQName))
104                 .withValue(0.2)
105                 .build();
106
107         final ContainerNode buildPlayerContainer = Builders.containerBuilder()
108                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQName))
109                 .withChild(buildGapLeaf)
110                 .build();
111
112         buildBaseContainerForTests = Builders.containerBuilder()
113                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
114                 .withChild(buildPlayerContainer)
115                 .build();
116
117         targetNodeForCreateAndDelete = YangInstanceIdentifier.builder(iIDCreateAndDelete)
118                 .node(containerPlayerQName)
119                 .node(leafGapQName)
120                 .build();
121
122         /** instance identifier for accessing leaf node "name" in list "artist" */
123         iIDMerge = YangInstanceIdentifier.builder()
124                 .node(baseQName)
125                 .node(containerLibraryQName)
126                 .node(listArtistQName)
127                 .nodeWithKey(listArtistQName, QName.create(listArtistQName, "name"), "name of artist")
128                 .node(leafNameQName)
129                 .build();
130
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")
135                 .build();
136
137         final LeafNode<Object> contentDescription = Builders.leafBuilder()
138                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
139                 .withValue("description of artist")
140                 .build();
141
142         final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
143                 .withNodeIdentifier(nodeWithKey)
144                 .withChild(contentName)
145                 .withChild(contentDescription)
146                 .build();
147
148         buildArtistList = Builders.mapBuilder()
149                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listArtistQName))
150                 .withChild(mapEntryNode)
151                 .build();
152
153         targetNodeMerge = YangInstanceIdentifier.builder()
154                 .node(baseQName)
155                 .node(containerLibraryQName)
156                 .node(listArtistQName)
157                 .nodeWithKey(listArtistQName, leafNameQName, "name of artist")
158                 .build();
159
160         /** Mocks */
161         doReturn(rWTransaction).when(transactionChain).newReadWriteTransaction();
162         doReturn(Futures.immediateCheckedFuture(null)).when(rWTransaction).submit();
163     }
164
165     @Test
166     public void testPatchDataReplaceMergeAndRemove() {
167         doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
168                 .when(rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, targetNodeMerge);
169
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<>();
174
175         entities.add(entityReplace);
176         entities.add(entityMerge);
177         entities.add(entityRemove);
178
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);
185
186         for (PATCHStatusEntity entity : patchStatusContext.getEditCollection()) {
187             assertTrue(entity.isOk());
188         }
189         assertTrue(patchStatusContext.isOk());
190     }
191
192     @Test
193     public void testPatchDataCreateAndDelete() throws Exception {
194         doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
195                 .when(rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, targetNodeForCreateAndDelete);
196
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<>();
202
203         entities.add(entityCreate);
204         entities.add(entityDelete);
205
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);
211
212         for (PATCHStatusEntity entity : patchStatusContext.getEditCollection()) {
213             assertTrue(entity.isOk());
214         }
215         assertTrue(patchStatusContext.isOk());
216     }
217
218     @Test
219     public void deleteNonexistentDataTest() {
220         doReturn(Futures.immediateCheckedFuture(false))
221                 .when(rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, targetNodeForCreateAndDelete);
222
223         final PATCHEntity entityDelete =
224                 new PATCHEntity("edit", "DELETE", targetNodeForCreateAndDelete);
225         final List<PATCHEntity> entities = new ArrayList<>();
226
227         entities.add(entityDelete);
228
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);
234
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());
240     }
241
242     @Test
243     public void testPatchMergePutContainer() throws Exception {
244         doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
245                 .when(rWTransaction).exists(LogicalDatastoreType.CONFIGURATION, targetNodeForCreateAndDelete);
246
247         final PATCHEntity entityMerge =
248                 new PATCHEntity("edit1", "MERGE", targetNodeForCreateAndDelete, buildBaseContainerForTests);
249         final List<PATCHEntity> entities = new ArrayList<>();
250
251         entities.add(entityMerge);
252
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);
258
259         for (PATCHStatusEntity entity : patchStatusContext.getEditCollection()) {
260             assertTrue(entity.isOk());
261         }
262         assertTrue(patchStatusContext.isOk());
263     }
264 }