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
8 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
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;
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.YangInstanceIdentifier.NodeIdentifier;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
48 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
52 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
53 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
54 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
56 public class PatchDataTransactionUtilTest {
58 private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
61 private DOMTransactionChain transactionChain;
64 private DOMDataTreeReadWriteTransaction rwTransaction;
67 private DOMDataBroker mockDataBroker;
69 private TransactionChainHandler transactionChainHandler;
70 private SchemaContextRef refSchemaCtx;
71 private YangInstanceIdentifier instanceIdContainer;
72 private YangInstanceIdentifier instanceIdCreateAndDelete;
73 private YangInstanceIdentifier instanceIdMerge;
74 private ContainerNode buildBaseContainerForTests;
75 private YangInstanceIdentifier targetNodeForCreateAndDelete;
76 private YangInstanceIdentifier targetNodeMerge;
77 private MapNode buildArtistList;
80 public void setUp() throws Exception {
83 doReturn(transactionChain).when(mockDataBroker).createTransactionChain(any());
84 transactionChainHandler = new TransactionChainHandler(mockDataBroker);
86 this.refSchemaCtx = new SchemaContextRef(
87 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles(PATH_FOR_NEW_SCHEMA_CONTEXT)));
88 final QName baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
89 final QName containerPlayerQName = QName.create(baseQName, "player");
90 final QName leafGapQName = QName.create(baseQName, "gap");
91 final QName containerLibraryQName = QName.create(baseQName, "library");
92 final QName listArtistQName = QName.create(baseQName, "artist");
93 final QName leafNameQName = QName.create(baseQName, "name");
94 final NodeIdentifierWithPredicates nodeWithKey = NodeIdentifierWithPredicates.of(listArtistQName, leafNameQName,
97 /* instance identifier for accessing container node "player" */
98 this.instanceIdContainer = YangInstanceIdentifier.builder()
100 .node(containerPlayerQName)
103 /* instance identifier for accessing leaf node "gap" */
104 this.instanceIdCreateAndDelete = instanceIdContainer.node(leafGapQName);
106 /* values that are used for creating leaf for testPatchDataCreateAndDelete test */
107 final LeafNode<?> buildGapLeaf = Builders.leafBuilder()
108 .withNodeIdentifier(new NodeIdentifier(leafGapQName))
112 final ContainerNode buildPlayerContainer = Builders.containerBuilder()
113 .withNodeIdentifier(new NodeIdentifier(containerPlayerQName))
114 .withChild(buildGapLeaf)
117 this.buildBaseContainerForTests = Builders.containerBuilder()
118 .withNodeIdentifier(new NodeIdentifier(baseQName))
119 .withChild(buildPlayerContainer)
122 this.targetNodeForCreateAndDelete = YangInstanceIdentifier.builder(this.instanceIdCreateAndDelete)
123 .node(containerPlayerQName)
127 /* instance identifier for accessing leaf node "name" in list "artist" */
128 this.instanceIdMerge = YangInstanceIdentifier.builder()
130 .node(containerLibraryQName)
131 .node(listArtistQName)
132 .nodeWithKey(listArtistQName, QName.create(listArtistQName, "name"), "name of artist")
136 /* values that are used for creating leaf for testPatchDataReplaceMergeAndRemove test */
137 final LeafNode<Object> contentName = Builders.leafBuilder()
138 .withNodeIdentifier(new NodeIdentifier(QName.create(baseQName, "name")))
139 .withValue("name of artist")
142 final LeafNode<Object> contentDescription = Builders.leafBuilder()
143 .withNodeIdentifier(new NodeIdentifier(QName.create(baseQName, "description")))
144 .withValue("description of artist")
147 final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
148 .withNodeIdentifier(nodeWithKey)
149 .withChild(contentName)
150 .withChild(contentDescription)
153 this.buildArtistList = Builders.mapBuilder()
154 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listArtistQName))
155 .withChild(mapEntryNode)
158 this.targetNodeMerge = YangInstanceIdentifier.builder()
160 .node(containerLibraryQName)
161 .node(listArtistQName)
162 .nodeWithKey(listArtistQName, leafNameQName, "name of artist")
166 doReturn(this.rwTransaction).when(this.transactionChain).newReadWriteTransaction();
167 doReturn(CommitInfo.emptyFluentFuture()).when(this.rwTransaction).commit();
171 public void testPatchDataReplaceMergeAndRemove() {
172 doReturn(immediateFalseFluentFuture()).doReturn(immediateTrueFluentFuture())
173 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeMerge);
175 final PatchEntity entityReplace =
176 new PatchEntity("edit1", REPLACE, this.targetNodeMerge, this.buildArtistList);
177 final PatchEntity entityMerge = new PatchEntity("edit2", MERGE, this.targetNodeMerge, this.buildArtistList);
178 final PatchEntity entityRemove = new PatchEntity("edit3", REMOVE, this.targetNodeMerge);
179 final List<PatchEntity> entities = new ArrayList<>();
181 entities.add(entityReplace);
182 entities.add(entityMerge);
183 entities.add(entityRemove);
185 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
186 new InstanceIdentifierContext<>(this.instanceIdMerge, null, null, this.refSchemaCtx.get());
187 final PatchContext patchContext = new PatchContext(iidContext, entities, "patchRMRm");
188 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
189 final PatchStatusContext patchStatusContext =
190 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
192 for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
193 assertTrue(entity.isOk());
195 assertTrue(patchStatusContext.isOk());
199 public void testPatchDataCreateAndDelete() throws Exception {
200 doReturn(immediateFalseFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
201 this.instanceIdContainer);
202 doReturn(immediateTrueFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
203 this.targetNodeForCreateAndDelete);
205 final PatchEntity entityCreate =
206 new PatchEntity("edit1", CREATE, this.instanceIdContainer, this.buildBaseContainerForTests);
207 final PatchEntity entityDelete =
208 new PatchEntity("edit2", DELETE, this.targetNodeForCreateAndDelete);
209 final List<PatchEntity> entities = new ArrayList<>();
211 entities.add(entityCreate);
212 entities.add(entityDelete);
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, transactionChainHandler);
218 final PatchStatusContext patchStatusContext =
219 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
221 for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
222 assertTrue("Edit " + entity.getEditId() + " failed", entity.isOk());
224 assertTrue(patchStatusContext.isOk());
228 public void deleteNonexistentDataTest() {
229 doReturn(immediateFalseFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
230 this.targetNodeForCreateAndDelete);
232 final PatchEntity entityDelete = new PatchEntity("edit", DELETE, this.targetNodeForCreateAndDelete);
233 final List<PatchEntity> entities = new ArrayList<>();
235 entities.add(entityDelete);
237 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
238 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
239 final PatchContext patchContext = new PatchContext(iidContext, entities, "patchD");
240 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
241 final PatchStatusContext patchStatusContext =
242 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
244 assertFalse(patchStatusContext.isOk());
245 assertEquals(RestconfError.ErrorType.PROTOCOL,
246 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorType());
247 assertEquals(RestconfError.ErrorTag.DATA_MISSING,
248 patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorTag());
252 public void testPatchMergePutContainer() throws Exception {
253 doReturn(immediateFalseFluentFuture()).doReturn(immediateTrueFluentFuture())
254 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
256 final PatchEntity entityMerge =
257 new PatchEntity("edit1", MERGE, this.instanceIdContainer, this.buildBaseContainerForTests);
258 final List<PatchEntity> entities = new ArrayList<>();
260 entities.add(entityMerge);
262 final InstanceIdentifierContext<? extends SchemaNode> iidContext =
263 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
264 final PatchContext patchContext = new PatchContext(iidContext, entities, "patchM");
265 final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
266 final PatchStatusContext patchStatusContext =
267 PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
269 for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
270 assertTrue(entity.isOk());
272 assertTrue(patchStatusContext.isOk());