9446e5c478bd8c9005764c1c3245018211edfb17
[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 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertTrue;
13 import static org.mockito.ArgumentMatchers.any;
14 import static org.mockito.Mockito.doReturn;
15 import static org.mockito.MockitoAnnotations.initMocks;
16 import static org.opendaylight.restconf.common.patch.PatchEditOperation.CREATE;
17 import static org.opendaylight.restconf.common.patch.PatchEditOperation.DELETE;
18 import static org.opendaylight.restconf.common.patch.PatchEditOperation.MERGE;
19 import static org.opendaylight.restconf.common.patch.PatchEditOperation.REMOVE;
20 import static org.opendaylight.restconf.common.patch.PatchEditOperation.REPLACE;
21 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture;
22 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateTrueFluentFuture;
23
24 import java.util.ArrayList;
25 import java.util.List;
26 import org.junit.Before;
27 import org.junit.Test;
28 import org.mockito.Mock;
29 import org.opendaylight.mdsal.common.api.CommitInfo;
30 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
31 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
32 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
33 import org.opendaylight.mdsal.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.TestRestconfUtils;
41 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
42 import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
43 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
44 import org.opendaylight.yangtools.yang.common.QName;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
46 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
50 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
51 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
52 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
53
54 public class PatchDataTransactionUtilTest {
55
56     private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
57
58     @Mock
59     private DOMTransactionChain transactionChain;
60
61     @Mock
62     private DOMDataTreeReadWriteTransaction rwTransaction;
63
64     @Mock
65     private DOMDataBroker mockDataBroker;
66
67     private TransactionChainHandler transactionChainHandler;
68     private SchemaContextRef refSchemaCtx;
69     private YangInstanceIdentifier instanceIdContainer;
70     private YangInstanceIdentifier instanceIdCreateAndDelete;
71     private YangInstanceIdentifier instanceIdMerge;
72     private ContainerNode buildBaseContainerForTests;
73     private YangInstanceIdentifier targetNodeForCreateAndDelete;
74     private YangInstanceIdentifier targetNodeMerge;
75     private MapNode buildArtistList;
76
77     @Before
78     public void setUp() throws Exception {
79         initMocks(this);
80
81         doReturn(transactionChain).when(mockDataBroker).createTransactionChain(any());
82         transactionChainHandler = new TransactionChainHandler(mockDataBroker);
83
84         this.refSchemaCtx = new SchemaContextRef(
85                 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles(PATH_FOR_NEW_SCHEMA_CONTEXT)));
86         final QName baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
87         final QName containerPlayerQName = QName.create(baseQName, "player");
88         final QName leafGapQName = QName.create(baseQName, "gap");
89         final QName containerLibraryQName = QName.create(baseQName, "library");
90         final QName listArtistQName = QName.create(baseQName, "artist");
91         final QName leafNameQName = QName.create(baseQName, "name");
92         final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey =
93             new YangInstanceIdentifier.NodeIdentifierWithPredicates(listArtistQName, leafNameQName, "name of artist");
94
95         /* instance identifier for accessing container node "player" */
96         this.instanceIdContainer = YangInstanceIdentifier.builder()
97                 .node(baseQName)
98                 .node(containerPlayerQName)
99                 .build();
100
101         /* instance identifier for accessing leaf node "gap" */
102         this.instanceIdCreateAndDelete = instanceIdContainer.node(leafGapQName);
103
104         /* values that are used for creating leaf for testPatchDataCreateAndDelete test */
105         final LeafNode<?> buildGapLeaf = Builders.leafBuilder()
106                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafGapQName))
107                 .withValue(0.2)
108                 .build();
109
110         final ContainerNode buildPlayerContainer = Builders.containerBuilder()
111                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQName))
112                 .withChild(buildGapLeaf)
113                 .build();
114
115         this.buildBaseContainerForTests = Builders.containerBuilder()
116                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
117                 .withChild(buildPlayerContainer)
118                 .build();
119
120         this.targetNodeForCreateAndDelete = YangInstanceIdentifier.builder(this.instanceIdCreateAndDelete)
121                 .node(containerPlayerQName)
122                 .node(leafGapQName)
123                 .build();
124
125         /* instance identifier for accessing leaf node "name" in list "artist" */
126         this.instanceIdMerge = YangInstanceIdentifier.builder()
127                 .node(baseQName)
128                 .node(containerLibraryQName)
129                 .node(listArtistQName)
130                 .nodeWithKey(listArtistQName, QName.create(listArtistQName, "name"), "name of artist")
131                 .node(leafNameQName)
132                 .build();
133
134         /* values that are used for creating leaf for testPatchDataReplaceMergeAndRemove test */
135         final LeafNode<Object> contentName = Builders.leafBuilder()
136                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
137                 .withValue("name of artist")
138                 .build();
139
140         final LeafNode<Object> contentDescription = Builders.leafBuilder()
141                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
142                 .withValue("description of artist")
143                 .build();
144
145         final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
146                 .withNodeIdentifier(nodeWithKey)
147                 .withChild(contentName)
148                 .withChild(contentDescription)
149                 .build();
150
151         this.buildArtistList = Builders.mapBuilder()
152                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listArtistQName))
153                 .withChild(mapEntryNode)
154                 .build();
155
156         this.targetNodeMerge = YangInstanceIdentifier.builder()
157                 .node(baseQName)
158                 .node(containerLibraryQName)
159                 .node(listArtistQName)
160                 .nodeWithKey(listArtistQName, leafNameQName, "name of artist")
161                 .build();
162
163         /* Mocks */
164         doReturn(this.rwTransaction).when(this.transactionChain).newReadWriteTransaction();
165         doReturn(CommitInfo.emptyFluentFuture()).when(this.rwTransaction).commit();
166     }
167
168     @Test
169     public void testPatchDataReplaceMergeAndRemove() {
170         doReturn(immediateFalseFluentFuture()).doReturn(immediateTrueFluentFuture())
171                 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeMerge);
172
173         final PatchEntity entityReplace =
174                 new PatchEntity("edit1", REPLACE, this.targetNodeMerge, this.buildArtistList);
175         final PatchEntity entityMerge = new PatchEntity("edit2", MERGE, this.targetNodeMerge, this.buildArtistList);
176         final PatchEntity entityRemove = new PatchEntity("edit3", REMOVE, this.targetNodeMerge);
177         final List<PatchEntity> entities = new ArrayList<>();
178
179         entities.add(entityReplace);
180         entities.add(entityMerge);
181         entities.add(entityRemove);
182
183         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
184                 new InstanceIdentifierContext<>(this.instanceIdMerge, null, null, this.refSchemaCtx.get());
185         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchRMRm");
186         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
187         final PatchStatusContext patchStatusContext =
188                 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
189
190         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
191             assertTrue(entity.isOk());
192         }
193         assertTrue(patchStatusContext.isOk());
194     }
195
196     @Test
197     public void testPatchDataCreateAndDelete() throws Exception {
198         doReturn(immediateFalseFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
199             this.instanceIdContainer);
200         doReturn(immediateTrueFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
201             this.targetNodeForCreateAndDelete);
202
203         final PatchEntity entityCreate =
204                 new PatchEntity("edit1", CREATE, this.instanceIdContainer, this.buildBaseContainerForTests);
205         final PatchEntity entityDelete =
206                 new PatchEntity("edit2", DELETE, this.targetNodeForCreateAndDelete);
207         final List<PatchEntity> entities = new ArrayList<>();
208
209         entities.add(entityCreate);
210         entities.add(entityDelete);
211
212         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
213                 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
214         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchCD");
215         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
216         final PatchStatusContext patchStatusContext =
217                 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
218
219         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
220             assertTrue("Edit " + entity.getEditId() + " failed", entity.isOk());
221         }
222         assertTrue(patchStatusContext.isOk());
223     }
224
225     @Test
226     public void deleteNonexistentDataTest() {
227         doReturn(immediateFalseFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
228             this.targetNodeForCreateAndDelete);
229
230         final PatchEntity entityDelete = new PatchEntity("edit", DELETE, this.targetNodeForCreateAndDelete);
231         final List<PatchEntity> entities = new ArrayList<>();
232
233         entities.add(entityDelete);
234
235         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
236                 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
237         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchD");
238         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
239         final PatchStatusContext patchStatusContext =
240                 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
241
242         assertFalse(patchStatusContext.isOk());
243         assertEquals(RestconfError.ErrorType.PROTOCOL,
244                 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorType());
245         assertEquals(RestconfError.ErrorTag.DATA_MISSING,
246                 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorTag());
247     }
248
249     @Test
250     public void testPatchMergePutContainer() throws Exception {
251         doReturn(immediateFalseFluentFuture()).doReturn(immediateTrueFluentFuture())
252                 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
253
254         final PatchEntity entityMerge =
255                 new PatchEntity("edit1", MERGE, this.instanceIdContainer, this.buildBaseContainerForTests);
256         final List<PatchEntity> entities = new ArrayList<>();
257
258         entities.add(entityMerge);
259
260         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
261                 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
262         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchM");
263         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
264         final PatchStatusContext patchStatusContext =
265                 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
266
267         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
268             assertTrue(entity.isOk());
269         }
270         assertTrue(patchStatusContext.isOk());
271     }
272 }