6c2eb4abb24cc0e761df98a00356399697515da9
[netconf.git] / restconf / restconf-nb-bierman02 / 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 import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.CREATE;
18 import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.DELETE;
19 import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.MERGE;
20 import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.REMOVE;
21 import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.REPLACE;
22
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.controller.md.sal.rest.common.TestRestconfUtils;
35 import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
36 import org.opendaylight.netconf.sal.restconf.impl.PatchEntity;
37 import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
38 import org.opendaylight.netconf.sal.restconf.impl.PatchStatusEntity;
39 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
40 import org.opendaylight.restconf.RestConnectorProvider;
41 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
42 import org.opendaylight.restconf.common.references.SchemaContextRef;
43 import org.opendaylight.restconf.handlers.TransactionChainHandler;
44 import org.opendaylight.restconf.restful.transaction.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;
54
55 public class PatchDataTransactionUtilTest {
56
57     private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
58
59     @Mock
60     private DOMTransactionChain transactionChain;
61
62     @Mock
63     private DOMDataReadWriteTransaction rwTransaction;
64
65     private SchemaContextRef refSchemaCtx;
66     private YangInstanceIdentifier instanceIdCreateAndDelete;
67     private YangInstanceIdentifier instanceIdMerge;
68     private ContainerNode buildBaseContainerForTests;
69     private YangInstanceIdentifier targetNodeForCreateAndDelete;
70     private YangInstanceIdentifier targetNodeMerge;
71     private MapNode buildArtistList;
72
73     // Fields used when delete operation fails to reset transaction chain
74     private static Field handler;
75     private static Field broker;
76
77     @Before
78     public void setUp() throws Exception {
79         initMocks(this);
80
81         PatchDataTransactionUtilTest.handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
82         PatchDataTransactionUtilTest.broker = RestConnectorProvider.class.getDeclaredField("dataBroker");
83
84         PatchDataTransactionUtilTest.handler.setAccessible(true);
85         PatchDataTransactionUtilTest.handler.set(RestConnectorProvider.class, mock(TransactionChainHandler.class));
86
87         PatchDataTransactionUtilTest.broker.setAccessible(true);
88         PatchDataTransactionUtilTest.broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
89
90         this.refSchemaCtx = new SchemaContextRef(
91                 YangParserTestUtils.parseYangSources(TestRestconfUtils.loadFiles(PATH_FOR_NEW_SCHEMA_CONTEXT)));
92         final QName baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
93         final QName containerPlayerQName = QName.create(baseQName, "player");
94         final QName leafGapQName = QName.create(baseQName, "gap");
95         final QName containerLibraryQName = QName.create(baseQName, "library");
96         final QName listArtistQName = QName.create(baseQName, "artist");
97         final QName leafNameQName = QName.create(baseQName, "name");
98         final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey =
99             new YangInstanceIdentifier.NodeIdentifierWithPredicates(listArtistQName, leafNameQName, "name of artist");
100
101         /* instance identifier for accessing leaf node "gap" */
102         this.instanceIdCreateAndDelete = YangInstanceIdentifier.builder()
103                 .node(baseQName)
104                 .node(containerPlayerQName)
105                 .node(leafGapQName)
106                 .build();
107
108         /* values that are used for creating leaf for testPatchDataCreateAndDelete test */
109         final LeafNode<?> buildGapLeaf = Builders.leafBuilder()
110                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafGapQName))
111                 .withValue(0.2)
112                 .build();
113
114         final ContainerNode buildPlayerContainer = Builders.containerBuilder()
115                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQName))
116                 .withChild(buildGapLeaf)
117                 .build();
118
119         this.buildBaseContainerForTests = Builders.containerBuilder()
120                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
121                 .withChild(buildPlayerContainer)
122                 .build();
123
124         this.targetNodeForCreateAndDelete = YangInstanceIdentifier.builder(this.instanceIdCreateAndDelete)
125                 .node(containerPlayerQName)
126                 .node(leafGapQName)
127                 .build();
128
129         /* instance identifier for accessing leaf node "name" in list "artist" */
130         this.instanceIdMerge = YangInstanceIdentifier.builder()
131                 .node(baseQName)
132                 .node(containerLibraryQName)
133                 .node(listArtistQName)
134                 .nodeWithKey(listArtistQName, QName.create(listArtistQName, "name"), "name of artist")
135                 .node(leafNameQName)
136                 .build();
137
138         /* values that are used for creating leaf for testPatchDataReplaceMergeAndRemove test */
139         final LeafNode<Object> contentName = Builders.leafBuilder()
140                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
141                 .withValue("name of artist")
142                 .build();
143
144         final LeafNode<Object> contentDescription = Builders.leafBuilder()
145                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
146                 .withValue("description of artist")
147                 .build();
148
149         final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
150                 .withNodeIdentifier(nodeWithKey)
151                 .withChild(contentName)
152                 .withChild(contentDescription)
153                 .build();
154
155         this.buildArtistList = Builders.mapBuilder()
156                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listArtistQName))
157                 .withChild(mapEntryNode)
158                 .build();
159
160         this.targetNodeMerge = YangInstanceIdentifier.builder()
161                 .node(baseQName)
162                 .node(containerLibraryQName)
163                 .node(listArtistQName)
164                 .nodeWithKey(listArtistQName, leafNameQName, "name of artist")
165                 .build();
166
167         /* Mocks */
168         doReturn(this.rwTransaction).when(this.transactionChain).newReadWriteTransaction();
169         doReturn(Futures.immediateCheckedFuture(null)).when(this.rwTransaction).submit();
170     }
171
172     @Test
173     public void testPatchDataReplaceMergeAndRemove() {
174         doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
175                 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeMerge);
176
177         final PatchEntity entityReplace =
178                 new PatchEntity("edit1", REPLACE, this.targetNodeMerge, this.buildArtistList);
179         final PatchEntity entityMerge = new PatchEntity("edit2", MERGE, this.targetNodeMerge, this.buildArtistList);
180         final PatchEntity entityRemove = new PatchEntity("edit3", REMOVE, this.targetNodeMerge);
181         final List<PatchEntity> entities = new ArrayList<>();
182
183         entities.add(entityReplace);
184         entities.add(entityMerge);
185         entities.add(entityRemove);
186
187         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
188                 new InstanceIdentifierContext<>(this.instanceIdMerge, null, null, this.refSchemaCtx.get());
189         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchRMRm");
190         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
191         final PatchStatusContext patchStatusContext =
192                 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
193
194         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
195             assertTrue(entity.isOk());
196         }
197         assertTrue(patchStatusContext.isOk());
198     }
199
200     @Test
201     public void testPatchDataCreateAndDelete() throws Exception {
202         doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
203                 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
204
205         final PatchEntity entityCreate =
206                 new PatchEntity("edit1", CREATE, this.targetNodeForCreateAndDelete, this.buildBaseContainerForTests);
207         final PatchEntity entityDelete =
208                 new PatchEntity("edit2", DELETE, this.targetNodeForCreateAndDelete);
209         final List<PatchEntity> entities = new ArrayList<>();
210
211         entities.add(entityCreate);
212         entities.add(entityDelete);
213
214         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
215                 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
216         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchCD");
217         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
218         final PatchStatusContext patchStatusContext =
219                 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
220
221         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
222             assertTrue(entity.isOk());
223         }
224         assertTrue(patchStatusContext.isOk());
225     }
226
227     @Test
228     public void deleteNonexistentDataTest() {
229         doReturn(Futures.immediateCheckedFuture(false))
230                 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
231
232         final PatchEntity entityDelete =
233                 new PatchEntity("edit", DELETE, this.targetNodeForCreateAndDelete);
234         final List<PatchEntity> entities = new ArrayList<>();
235
236         entities.add(entityDelete);
237
238         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
239                 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
240         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchD");
241         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
242         final PatchStatusContext patchStatusContext =
243                 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
244
245         assertFalse(patchStatusContext.isOk());
246         assertEquals(RestconfError.ErrorType.PROTOCOL,
247                 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorType());
248         assertEquals(RestconfError.ErrorTag.DATA_MISSING,
249                 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorTag());
250     }
251
252     @Test
253     public void testPatchMergePutContainer() throws Exception {
254         doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
255                 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
256
257         final PatchEntity entityMerge =
258                 new PatchEntity("edit1", MERGE, this.targetNodeForCreateAndDelete, this.buildBaseContainerForTests);
259         final List<PatchEntity> entities = new ArrayList<>();
260
261         entities.add(entityMerge);
262
263         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
264                 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
265         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchM");
266         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
267         final PatchStatusContext patchStatusContext =
268                 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
269
270         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
271             assertTrue(entity.isOk());
272         }
273         assertTrue(patchStatusContext.isOk());
274     }
275 }