Eliminate unnecessary blocking checks
[netconf.git] / restconf / restconf-nb-rfc8040 / src / test / java / org / opendaylight / restconf / nb / rfc8040 / rests / utils / PatchDataTransactionUtilTest.java
index efb9b05f16d47a0bb0eb61922a52147731c43813..70851175af08428095ded7318a55cc75ea8c41de 100644 (file)
@@ -5,64 +5,77 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.MockitoAnnotations.initMocks;
 import static org.opendaylight.restconf.common.patch.PatchEditOperation.CREATE;
 import static org.opendaylight.restconf.common.patch.PatchEditOperation.DELETE;
 import static org.opendaylight.restconf.common.patch.PatchEditOperation.MERGE;
 import static org.opendaylight.restconf.common.patch.PatchEditOperation.REMOVE;
 import static org.opendaylight.restconf.common.patch.PatchEditOperation.REPLACE;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateTrueFluentFuture;
 
-import com.google.common.util.concurrent.Futures;
-import java.lang.reflect.Field;
+import com.google.common.util.concurrent.SettableFuture;
 import java.util.ArrayList;
 import java.util.List;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
-import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.netconf.api.DocumentedException;
+import org.opendaylight.netconf.api.NetconfDocumentedException;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchEntity;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
-import org.opendaylight.restconf.nb.rfc8040.RestConnectorProvider;
 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
-import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.NetconfRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class PatchDataTransactionUtilTest {
-
     private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
-
     @Mock
     private DOMTransactionChain transactionChain;
-
     @Mock
-    private DOMDataReadWriteTransaction rwTransaction;
+    private DOMDataTreeReadWriteTransaction rwTransaction;
+    @Mock
+    private DOMDataBroker mockDataBroker;
+    @Mock
+    private NetconfDataTreeService netconfService;
 
-    private SchemaContextRef refSchemaCtx;
+    private TransactionChainHandler transactionChainHandler;
+    private EffectiveModelContext refSchemaCtx;
     private YangInstanceIdentifier instanceIdContainer;
     private YangInstanceIdentifier instanceIdCreateAndDelete;
     private YangInstanceIdentifier instanceIdMerge;
@@ -71,33 +84,21 @@ public class PatchDataTransactionUtilTest {
     private YangInstanceIdentifier targetNodeMerge;
     private MapNode buildArtistList;
 
-    // Fields used when delete operation fails to reset transaction chain
-    private static Field handler;
-    private static Field broker;
-
     @Before
     public void setUp() throws Exception {
-        initMocks(this);
+        doReturn(transactionChain).when(mockDataBroker).createTransactionChain(any());
+        transactionChainHandler = new TransactionChainHandler(mockDataBroker);
 
-        PatchDataTransactionUtilTest.handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
-        PatchDataTransactionUtilTest.broker = RestConnectorProvider.class.getDeclaredField("dataBroker");
-
-        PatchDataTransactionUtilTest.handler.setAccessible(true);
-        PatchDataTransactionUtilTest.handler.set(RestConnectorProvider.class, mock(TransactionChainHandler.class));
-
-        PatchDataTransactionUtilTest.broker.setAccessible(true);
-        PatchDataTransactionUtilTest.broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
-
-        this.refSchemaCtx = new SchemaContextRef(
-                YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles(PATH_FOR_NEW_SCHEMA_CONTEXT)));
+        this.refSchemaCtx = YangParserTestUtils.parseYangFiles(
+            TestRestconfUtils.loadFiles(PATH_FOR_NEW_SCHEMA_CONTEXT));
         final QName baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
         final QName containerPlayerQName = QName.create(baseQName, "player");
         final QName leafGapQName = QName.create(baseQName, "gap");
         final QName containerLibraryQName = QName.create(baseQName, "library");
         final QName listArtistQName = QName.create(baseQName, "artist");
         final QName leafNameQName = QName.create(baseQName, "name");
-        final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey =
-            new YangInstanceIdentifier.NodeIdentifierWithPredicates(listArtistQName, leafNameQName, "name of artist");
+        final NodeIdentifierWithPredicates nodeWithKey = NodeIdentifierWithPredicates.of(listArtistQName, leafNameQName,
+            "name of artist");
 
         /* instance identifier for accessing container node "player" */
         this.instanceIdContainer = YangInstanceIdentifier.builder()
@@ -110,17 +111,17 @@ public class PatchDataTransactionUtilTest {
 
         /* values that are used for creating leaf for testPatchDataCreateAndDelete test */
         final LeafNode<?> buildGapLeaf = Builders.leafBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafGapQName))
+                .withNodeIdentifier(new NodeIdentifier(leafGapQName))
                 .withValue(0.2)
                 .build();
 
         final ContainerNode buildPlayerContainer = Builders.containerBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQName))
+                .withNodeIdentifier(new NodeIdentifier(containerPlayerQName))
                 .withChild(buildGapLeaf)
                 .build();
 
         this.buildBaseContainerForTests = Builders.containerBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
+                .withNodeIdentifier(new NodeIdentifier(baseQName))
                 .withChild(buildPlayerContainer)
                 .build();
 
@@ -140,12 +141,12 @@ public class PatchDataTransactionUtilTest {
 
         /* values that are used for creating leaf for testPatchDataReplaceMergeAndRemove test */
         final LeafNode<Object> contentName = Builders.leafBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
+                .withNodeIdentifier(new NodeIdentifier(QName.create(baseQName, "name")))
                 .withValue("name of artist")
                 .build();
 
         final LeafNode<Object> contentDescription = Builders.leafBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
+                .withNodeIdentifier(new NodeIdentifier(QName.create(baseQName, "description")))
                 .withValue("description of artist")
                 .build();
 
@@ -169,14 +170,12 @@ public class PatchDataTransactionUtilTest {
 
         /* Mocks */
         doReturn(this.rwTransaction).when(this.transactionChain).newReadWriteTransaction();
-        doReturn(Futures.immediateCheckedFuture(null)).when(this.rwTransaction).submit();
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.rwTransaction).commit();
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
     }
 
     @Test
     public void testPatchDataReplaceMergeAndRemove() {
-        doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
-                .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeMerge);
-
         final PatchEntity entityReplace =
                 new PatchEntity("edit1", REPLACE, this.targetNodeMerge, this.buildArtistList);
         final PatchEntity entityMerge = new PatchEntity("edit2", MERGE, this.targetNodeMerge, this.buildArtistList);
@@ -188,24 +187,19 @@ public class PatchDataTransactionUtilTest {
         entities.add(entityRemove);
 
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
-                new InstanceIdentifierContext<>(this.instanceIdMerge, null, null, this.refSchemaCtx.get());
+                new InstanceIdentifierContext<>(this.instanceIdMerge, null, null, this.refSchemaCtx);
         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchRMRm");
-        final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
-        final PatchStatusContext patchStatusContext =
-                PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
 
-        for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
-            assertTrue(entity.isOk());
-        }
-        assertTrue(patchStatusContext.isOk());
+        patch(patchContext, new MdsalRestconfStrategy(transactionChainHandler), false);
+        patch(patchContext, new NetconfRestconfStrategy(netconfService), false);
     }
 
     @Test
-    public void testPatchDataCreateAndDelete() throws Exception {
-        doReturn(Futures.immediateCheckedFuture(false))
-                .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.instanceIdContainer);
-        doReturn(Futures.immediateCheckedFuture(true))
-        .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
+    public void testPatchDataCreateAndDelete() {
+        doReturn(immediateFalseFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
+            this.instanceIdContainer);
+        doReturn(immediateTrueFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
+            this.targetNodeForCreateAndDelete);
 
         final PatchEntity entityCreate =
                 new PatchEntity("edit1", CREATE, this.instanceIdContainer, this.buildBaseContainerForTests);
@@ -217,48 +211,39 @@ public class PatchDataTransactionUtilTest {
         entities.add(entityDelete);
 
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
-                new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
+                new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx);
         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchCD");
-        final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
-        final PatchStatusContext patchStatusContext =
-                PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
-
-        for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
-            assertTrue("Edit " + entity.getEditId() + " failed", entity.isOk());
-        }
-        assertTrue(patchStatusContext.isOk());
+        patch(patchContext, new MdsalRestconfStrategy(transactionChainHandler), true);
+        patch(patchContext, new NetconfRestconfStrategy(netconfService), true);
     }
 
     @Test
     public void deleteNonexistentDataTest() {
-        doReturn(Futures.immediateCheckedFuture(false))
-                .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
-
-        final PatchEntity entityDelete =
-                new PatchEntity("edit", DELETE, this.targetNodeForCreateAndDelete);
+        doReturn(immediateFalseFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
+            this.targetNodeForCreateAndDelete);
+        final NetconfDocumentedException exception = new NetconfDocumentedException("id",
+            DocumentedException.ErrorType.RPC, DocumentedException.ErrorTag.from("data-missing"),
+            DocumentedException.ErrorSeverity.ERROR);
+        final SettableFuture<? extends CommitInfo> ret = SettableFuture.create();
+        ret.setException(new TransactionCommitFailedException(
+            String.format("Commit of transaction %s failed", this), exception));
+
+        Mockito.when(this.netconfService.commit(any())).thenAnswer(invocation -> ret);
+
+        final PatchEntity entityDelete = new PatchEntity("edit", DELETE, this.targetNodeForCreateAndDelete);
         final List<PatchEntity> entities = new ArrayList<>();
 
         entities.add(entityDelete);
 
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
-                new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
+                new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx);
         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchD");
-        final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
-        final PatchStatusContext patchStatusContext =
-                PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
-
-        assertFalse(patchStatusContext.isOk());
-        assertEquals(RestconfError.ErrorType.PROTOCOL,
-                patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorType());
-        assertEquals(RestconfError.ErrorTag.DATA_MISSING,
-                patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorTag());
+        deleteMdsal(patchContext, new MdsalRestconfStrategy(transactionChainHandler));
+        deleteNetconf(patchContext, new NetconfRestconfStrategy(netconfService));
     }
 
     @Test
-    public void testPatchMergePutContainer() throws Exception {
-        doReturn(Futures.immediateCheckedFuture(false)).doReturn(Futures.immediateCheckedFuture(true))
-                .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
-
+    public void testPatchMergePutContainer() {
         final PatchEntity entityMerge =
                 new PatchEntity("edit1", MERGE, this.instanceIdContainer, this.buildBaseContainerForTests);
         final List<PatchEntity> entities = new ArrayList<>();
@@ -266,15 +251,45 @@ public class PatchDataTransactionUtilTest {
         entities.add(entityMerge);
 
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
-                new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx.get());
+                new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx);
         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchM");
-        final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, this.transactionChain);
-        final PatchStatusContext patchStatusContext =
-                PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
+        patch(patchContext, new MdsalRestconfStrategy(transactionChainHandler), false);
+        patch(patchContext, new NetconfRestconfStrategy(netconfService), false);
+    }
 
+    private void patch(final PatchContext patchContext, final RestconfStrategy strategy,
+                       final boolean failed) {
+        final PatchStatusContext patchStatusContext =
+                PatchDataTransactionUtil.patchData(patchContext, strategy, this.refSchemaCtx);
         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
-            assertTrue(entity.isOk());
+            if (failed) {
+                assertTrue("Edit " + entity.getEditId() + " failed", entity.isOk());
+            } else {
+                assertTrue(entity.isOk());
+            }
         }
         assertTrue(patchStatusContext.isOk());
     }
-}
\ No newline at end of file
+
+    private void deleteMdsal(final PatchContext patchContext, final RestconfStrategy strategy) {
+        final PatchStatusContext patchStatusContext =
+                PatchDataTransactionUtil.patchData(patchContext, strategy, this.refSchemaCtx);
+
+        assertFalse(patchStatusContext.isOk());
+        assertEquals(RestconfError.ErrorType.PROTOCOL,
+                patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorType());
+        assertEquals(RestconfError.ErrorTag.DATA_MISSING,
+                patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorTag());
+    }
+
+    private void deleteNetconf(PatchContext patchContext, RestconfStrategy strategy) {
+        final PatchStatusContext patchStatusContext =
+            PatchDataTransactionUtil.patchData(patchContext, strategy, this.refSchemaCtx);
+
+        assertFalse(patchStatusContext.isOk());
+        assertEquals(RestconfError.ErrorType.PROTOCOL,
+            patchStatusContext.getGlobalErrors().get(0).getErrorType());
+        assertEquals(RestconfError.ErrorTag.DATA_MISSING,
+            patchStatusContext.getGlobalErrors().get(0).getErrorTag());
+    }
+}