efb9b05f16d47a0bb0eb61922a52147731c43813
[netconf.git] / restconf / restconf-nb-rfc8040 / src / test / java / org / opendaylight / restconf / nb / rfc8040 / rests / 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.nb.rfc8040.rests.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.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;
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.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;
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 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;
73
74     // Fields used when delete operation fails to reset transaction chain
75     private static Field handler;
76     private static Field broker;
77
78     @Before
79     public void setUp() throws Exception {
80         initMocks(this);
81
82         PatchDataTransactionUtilTest.handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
83         PatchDataTransactionUtilTest.broker = RestConnectorProvider.class.getDeclaredField("dataBroker");
84
85         PatchDataTransactionUtilTest.handler.setAccessible(true);
86         PatchDataTransactionUtilTest.handler.set(RestConnectorProvider.class, mock(TransactionChainHandler.class));
87
88         PatchDataTransactionUtilTest.broker.setAccessible(true);
89         PatchDataTransactionUtilTest.broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
90
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");
101
102         /* instance identifier for accessing container node "player" */
103         this.instanceIdContainer = YangInstanceIdentifier.builder()
104                 .node(baseQName)
105                 .node(containerPlayerQName)
106                 .build();
107
108         /* instance identifier for accessing leaf node "gap" */
109         this.instanceIdCreateAndDelete = instanceIdContainer.node(leafGapQName);
110
111         /* values that are used for creating leaf for testPatchDataCreateAndDelete test */
112         final LeafNode<?> buildGapLeaf = Builders.leafBuilder()
113                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafGapQName))
114                 .withValue(0.2)
115                 .build();
116
117         final ContainerNode buildPlayerContainer = Builders.containerBuilder()
118                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQName))
119                 .withChild(buildGapLeaf)
120                 .build();
121
122         this.buildBaseContainerForTests = Builders.containerBuilder()
123                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
124                 .withChild(buildPlayerContainer)
125                 .build();
126
127         this.targetNodeForCreateAndDelete = YangInstanceIdentifier.builder(this.instanceIdCreateAndDelete)
128                 .node(containerPlayerQName)
129                 .node(leafGapQName)
130                 .build();
131
132         /* instance identifier for accessing leaf node "name" in list "artist" */
133         this.instanceIdMerge = YangInstanceIdentifier.builder()
134                 .node(baseQName)
135                 .node(containerLibraryQName)
136                 .node(listArtistQName)
137                 .nodeWithKey(listArtistQName, QName.create(listArtistQName, "name"), "name of artist")
138                 .node(leafNameQName)
139                 .build();
140
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")
145                 .build();
146
147         final LeafNode<Object> contentDescription = Builders.leafBuilder()
148                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
149                 .withValue("description of artist")
150                 .build();
151
152         final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
153                 .withNodeIdentifier(nodeWithKey)
154                 .withChild(contentName)
155                 .withChild(contentDescription)
156                 .build();
157
158         this.buildArtistList = Builders.mapBuilder()
159                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listArtistQName))
160                 .withChild(mapEntryNode)
161                 .build();
162
163         this.targetNodeMerge = YangInstanceIdentifier.builder()
164                 .node(baseQName)
165                 .node(containerLibraryQName)
166                 .node(listArtistQName)
167                 .nodeWithKey(listArtistQName, leafNameQName, "name of artist")
168                 .build();
169
170         /* Mocks */
171         doReturn(this.rwTransaction).when(this.transactionChain).newReadWriteTransaction();
172         doReturn(Futures.immediateCheckedFuture(null)).when(this.rwTransaction).submit();
173     }
174
175     @Test
176     public void testPatchDataReplaceMergeAndRemove() {
177         doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
178                 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeMerge);
179
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<>();
185
186         entities.add(entityReplace);
187         entities.add(entityMerge);
188         entities.add(entityRemove);
189
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);
196
197         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
198             assertTrue(entity.isOk());
199         }
200         assertTrue(patchStatusContext.isOk());
201     }
202
203     @Test
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);
209
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<>();
215
216         entities.add(entityCreate);
217         entities.add(entityDelete);
218
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);
225
226         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
227             assertTrue("Edit " + entity.getEditId() + " failed", entity.isOk());
228         }
229         assertTrue(patchStatusContext.isOk());
230     }
231
232     @Test
233     public void deleteNonexistentDataTest() {
234         doReturn(Futures.immediateCheckedFuture(false))
235                 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
236
237         final PatchEntity entityDelete =
238                 new PatchEntity("edit", DELETE, this.targetNodeForCreateAndDelete);
239         final List<PatchEntity> entities = new ArrayList<>();
240
241         entities.add(entityDelete);
242
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);
249
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());
255     }
256
257     @Test
258     public void testPatchMergePutContainer() throws Exception {
259         doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
260                 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
261
262         final PatchEntity entityMerge =
263                 new PatchEntity("edit1", MERGE, this.instanceIdContainer, this.buildBaseContainerForTests);
264         final List<PatchEntity> entities = new ArrayList<>();
265
266         entities.add(entityMerge);
267
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);
274
275         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
276             assertTrue(entity.isOk());
277         }
278         assertTrue(patchStatusContext.isOk());
279     }
280 }