Add blueprint wiring for restconf connector
[netconf.git] / restconf / sal-rest-connector / src / test / java / org / opendaylight / controller / sal / restconf / impl / test / BrokerFacadeTest.java
index 670377069872328e06e85e80001698b9a2a51246..b52689f1c889e6b4b49a32092d0a25c3298dfb0a 100644 (file)
@@ -9,11 +9,14 @@
 package org.opendaylight.controller.sal.restconf.impl.test;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -30,6 +33,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.InOrder;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
@@ -45,9 +49,12 @@ import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
+import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
 import org.opendaylight.netconf.sal.restconf.impl.PutResult;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
@@ -56,8 +63,13 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.handlers.TransactionChainHandler;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -71,23 +83,29 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath;
  * @author Thomas Pantelis
  */
 public class BrokerFacadeTest {
-    @Mock private DOMDataBroker domDataBroker;
-    @Mock private DOMNotificationService domNotification;
-    @Mock private ConsumerSession context;
-    @Mock private DOMRpcService mockRpcService;
-    @Mock private DOMMountPoint mockMountInstance;
+
+    @Mock
+    private DOMDataBroker domDataBroker;
+    @Mock
+    private DOMNotificationService domNotification;
+    @Mock
+    private DOMRpcService mockRpcService;
+    @Mock
+    private DOMMountPoint mockMountInstance;
+    @Mock
+    private DOMDataReadOnlyTransaction readTransaction;
+    @Mock
+    private DOMDataWriteTransaction writeTransaction;
+    @Mock
+    private DOMDataReadWriteTransaction rwTransaction;
 
     private final BrokerFacade brokerFacade = BrokerFacade.getInstance();
     private final NormalizedNode<?, ?> dummyNode = createDummyNode("test:module", "2014-01-09", "interfaces");
     private final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> dummyNodeInFuture =
-            wrapDummyNode(dummyNode);
+            wrapDummyNode(this.dummyNode);
     private final QName qname = TestUtils.buildQName("interfaces","test:module", "2014-01-09");
-    private final SchemaPath type = SchemaPath.create(true, qname);
-    private final YangInstanceIdentifier instanceID = YangInstanceIdentifier.builder().node(qname).build();
-
-    @Mock private DOMDataReadOnlyTransaction rTransaction;
-    @Mock private DOMDataWriteTransaction wTransaction;
-    @Mock private DOMDataReadWriteTransaction rwTransaction;
+    private final SchemaPath type = SchemaPath.create(true, this.qname);
+    private final YangInstanceIdentifier instanceID = YangInstanceIdentifier.builder().node(this.qname).build();
 
     @Before
     public void setUp() throws Exception {
@@ -95,34 +113,34 @@ public class BrokerFacadeTest {
         this.brokerFacade.setDomDataBroker(this.domDataBroker);
         this.brokerFacade.setDomNotificationService(this.domNotification);
         this.brokerFacade.setRpcService(this.mockRpcService);
-        this.brokerFacade.setContext(this.context);
-        when(this.domDataBroker.newReadOnlyTransaction()).thenReturn(this.rTransaction);
-        when(this.domDataBroker.newWriteOnlyTransaction()).thenReturn(this.wTransaction);
+        when(this.domDataBroker.newReadOnlyTransaction()).thenReturn(this.readTransaction);
+        when(this.domDataBroker.newWriteOnlyTransaction()).thenReturn(this.writeTransaction);
         when(this.domDataBroker.newReadWriteTransaction()).thenReturn(this.rwTransaction);
 
         ControllerContext.getInstance().setSchemas(TestUtils.loadSchemaContext("/full-versions/test-module"));
     }
 
-    private CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> wrapDummyNode(
+    private static CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> wrapDummyNode(
             final NormalizedNode<?, ?> dummyNode) {
-        return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>> of(dummyNode));
+        return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>of(dummyNode));
     }
 
-    private CheckedFuture<Boolean, ReadFailedException> wrapExistence(final Boolean exists) {
+    private static CheckedFuture<Boolean, ReadFailedException> wrapExistence(final Boolean exists) {
         return Futures.immediateCheckedFuture(exists);
     }
 
     /**
-     * Value of this node shouldn't be important for testing purposes
+     * Value of this node shouldn't be important for testing purposes.
      */
-    private NormalizedNode<?, ?> createDummyNode(final String namespace, final String date, final String localName) {
+    private static NormalizedNode<?, ?> createDummyNode(final String namespace, final String date,
+            final String localName) {
         return Builders.containerBuilder()
                 .withNodeIdentifier(new NodeIdentifier(QName.create(namespace, date, localName))).build();
     }
 
     @Test
     public void testReadConfigurationData() {
-        when(this.rTransaction.read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class))).thenReturn(
+        when(this.readTransaction.read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class))).thenReturn(
                 this.dummyNodeInFuture);
 
         final NormalizedNode<?, ?> actualNode = this.brokerFacade.readConfigurationData(this.instanceID);
@@ -132,7 +150,7 @@ public class BrokerFacadeTest {
 
     @Test
     public void testReadOperationalData() {
-        when(this.rTransaction.read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class))).thenReturn(
+        when(this.readTransaction.read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class))).thenReturn(
                 this.dummyNodeInFuture);
 
         final NormalizedNode<?, ?> actualNode = this.brokerFacade.readOperationalData(this.instanceID);
@@ -147,6 +165,26 @@ public class BrokerFacadeTest {
         this.brokerFacade.readOperationalData(this.instanceID);
     }
 
+    @Test
+    public void test503() throws Exception {
+        final RpcError error = RpcResultBuilder.newError(
+                RpcError.ErrorType.TRANSPORT,
+                ErrorTag.RESOURCE_DENIED.getTagValue(),
+                "Master is down. Please try again.");
+        final ReadFailedException exception503 = new ReadFailedException("Read from transaction failed", error);
+        doReturn(Futures.immediateFailedCheckedFuture(exception503))
+                .when(readTransaction).read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class));
+        try {
+            brokerFacade.readConfigurationData(this.instanceID, "explicit");
+            fail("This test should fail.");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("getErrorTag", ErrorTag.RESOURCE_DENIED_TRANSPORT, e.getErrors().get(0).getErrorTag());
+            assertEquals("getErrorType", ErrorType.TRANSPORT, e.getErrors().get(0).getErrorType());
+            assertEquals("getErrorMessage", "Master is down. Please try again.",
+                    e.getErrors().get(0).getErrorMessage());
+        }
+    }
+
     @Test
     public void testInvokeRpc() throws Exception {
         final DOMRpcResult expResult = mock(DOMRpcResult.class);
@@ -162,7 +200,7 @@ public class BrokerFacadeTest {
 
     @Test(expected = RestconfDocumentedException.class)
     public void testInvokeRpcWithNoConsumerSession() {
-        this.brokerFacade.setContext(null);
+        brokerFacade.setDomDataBroker(null);
         this.brokerFacade.invokeRpc(this.type, this.dummyNode);
     }
 
@@ -180,7 +218,7 @@ public class BrokerFacadeTest {
         when(this.rwTransaction.read(LogicalDatastoreType.CONFIGURATION, this.instanceID)).thenReturn(readFuture);
 
         final PutResult result = this.brokerFacade.commitConfigurationDataPut(mock(SchemaContext.class),
-                this.instanceID, this.dummyNode);
+                this.instanceID, this.dummyNode, null, null);
 
         final Future<Void> actualFuture = result.getFutureOfPutData();
 
@@ -203,7 +241,7 @@ public class BrokerFacadeTest {
         when(this.rwTransaction.submit()).thenReturn(expFuture);
 
         final CheckedFuture<Void, TransactionCommitFailedException> actualFuture = this.brokerFacade
-                .commitConfigurationDataPost(mock(SchemaContext.class), this.instanceID, this.dummyNode);
+                .commitConfigurationDataPost(mock(SchemaContext.class), this.instanceID, this.dummyNode, null, null);
 
         assertSame("commitConfigurationDataPost", expFuture, actualFuture);
 
@@ -221,7 +259,8 @@ public class BrokerFacadeTest {
                 .thenReturn(successFuture);
         try {
             // Schema context is only necessary for ensuring parent structure
-            this.brokerFacade.commitConfigurationDataPost((SchemaContext) null, this.instanceID, this.dummyNode);
+            this.brokerFacade.commitConfigurationDataPost((SchemaContext) null, this.instanceID, this.dummyNode, null,
+                    null);
         } catch (final RestconfDocumentedException e) {
             assertEquals("getErrorTag", RestconfError.ErrorTag.DATA_EXISTS, e.getErrors().get(0).getErrorTag());
             throw e;
@@ -277,7 +316,7 @@ public class BrokerFacadeTest {
     /**
      * Prepare conditions to test delete operation. Data to delete exists or does not exist according to value of
      * {@code assumeDataExists} parameter.
-     * @param assumeDataExists
+     * @param assumeDataExists boolean to assume if data exists
      */
     private void prepareDataForDelete(final boolean assumeDataExists) {
         when(this.rwTransaction.exists(LogicalDatastoreType.CONFIGURATION, this.instanceID))
@@ -286,7 +325,8 @@ public class BrokerFacadeTest {
 
     @Test
     public void testRegisterToListenDataChanges() {
-        final ListenerAdapter listener = Notificator.createListener(this.instanceID, "stream");
+        final ListenerAdapter listener = Notificator.createListener(this.instanceID, "stream",
+                NotificationOutputType.XML);
 
         @SuppressWarnings("unchecked")
         final ListenerRegistration<DOMDataChangeListener> mockRegistration = mock(ListenerRegistration.class);
@@ -311,7 +351,7 @@ public class BrokerFacadeTest {
      * Create, register, close and remove notification listener.
      */
     @Test
-    public void testRegisterToListenNotificationChanges() {
+    public void testRegisterToListenNotificationChanges() throws Exception {
         // create test notification listener
         final String identifier = "create-notification-stream/toaster:toastDone";
         final SchemaPath path = SchemaPath.create(true,
@@ -335,8 +375,105 @@ public class BrokerFacadeTest {
         // registrations should be invoked only once
         verify(this.domNotification, times(1)).registerNotificationListener(listener, listener.getSchemaPath());
 
+        final DOMTransactionChain transactionChain = mock(DOMTransactionChain.class);
+        final DOMDataWriteTransaction wTx = mock(DOMDataWriteTransaction.class);
+        final CheckedFuture<Void, TransactionCommitFailedException> checked = Futures.immediateCheckedFuture(null);
+        when(wTx.submit()).thenReturn(checked);
+        when(transactionChain.newWriteOnlyTransaction()).thenReturn(wTx);
+        final TransactionChainHandler transactionChainHandler = new TransactionChainHandler(transactionChain);
+        final SchemaContextHandler schemaHandler = Mockito.mock(SchemaContextHandler.class);
+        final SchemaContext schCtx = TestUtils.loadSchemaContext("/modules");
+        when(schemaHandler.get()).thenReturn(schCtx);
+        listener.setCloseVars(transactionChainHandler, schemaHandler);
         // close and remove test notification listener
         listener.close();
-        Notificator.removeNotificationListenerIfNoSubscriberExists(listener);
+        Notificator.removeListenerIfNoSubscriberExists(listener);
+    }
+
+    /**
+     * Test Patch method on the server with no data.
+     */
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testPatchConfigurationDataWithinTransactionServer() throws Exception {
+        final PatchContext patchContext = mock(PatchContext.class);
+        final InstanceIdentifierContext identifierContext = mock(InstanceIdentifierContext.class);
+        final CheckedFuture<Void, TransactionCommitFailedException> expFuture = Futures.immediateCheckedFuture(null);
+
+        when(patchContext.getData()).thenReturn(Lists.newArrayList());
+        when(patchContext.getInstanceIdentifierContext()).thenReturn(identifierContext);
+
+        // no mount point
+        when(identifierContext.getMountPoint()).thenReturn(null);
+
+        when(this.rwTransaction.submit()).thenReturn(expFuture);
+
+        final PatchStatusContext status = this.brokerFacade.patchConfigurationDataWithinTransaction(patchContext);
+
+        // assert success
+        assertTrue("Patch operation should be successful on server", status.isOk());
+    }
+
+    /**
+     * Test Patch method on mounted device with no data.
+     */
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testPatchConfigurationDataWithinTransactionMount() throws Exception {
+        final PatchContext patchContext = mock(PatchContext.class);
+        final InstanceIdentifierContext identifierContext = mock(InstanceIdentifierContext.class);
+        final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
+        final DOMDataBroker mountDataBroker = mock(DOMDataBroker.class);
+        final DOMDataReadWriteTransaction transaction = mock(DOMDataReadWriteTransaction.class);
+        final CheckedFuture<Void, TransactionCommitFailedException> expFuture = Futures.immediateCheckedFuture(null);
+
+        when(patchContext.getData()).thenReturn(Lists.newArrayList());
+        when(patchContext.getInstanceIdentifierContext()).thenReturn(identifierContext);
+
+        // return mount point with broker
+        when(identifierContext.getMountPoint()).thenReturn(mountPoint);
+        when(mountPoint.getService(DOMDataBroker.class)).thenReturn(Optional.of(mountDataBroker));
+        when(mountDataBroker.newReadWriteTransaction()).thenReturn(transaction);
+        when(transaction.submit()).thenReturn(expFuture);
+
+        final PatchStatusContext status = this.brokerFacade.patchConfigurationDataWithinTransaction(patchContext);
+
+        // assert success
+        assertTrue("Patch operation should be successful on mounted device", status.isOk());
+    }
+
+    /**
+     * Negative test for Patch operation when mounted device does not support {@link DOMDataBroker service.}
+     * Patch operation should fail with global error.
+     */
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testPatchConfigurationDataWithinTransactionMountFail() throws Exception {
+        final PatchContext patchContext = mock(PatchContext.class);
+        final InstanceIdentifierContext identifierContext = mock(InstanceIdentifierContext.class);
+        final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
+        final DOMDataBroker mountDataBroker = mock(DOMDataBroker.class);
+        final DOMDataReadWriteTransaction transaction = mock(DOMDataReadWriteTransaction.class);
+        final CheckedFuture<Void, TransactionCommitFailedException> expFuture = Futures.immediateCheckedFuture(null);
+
+        when(patchContext.getData()).thenReturn(Lists.newArrayList());
+        when(patchContext.getInstanceIdentifierContext()).thenReturn(identifierContext);
+        when(identifierContext.getMountPoint()).thenReturn(mountPoint);
+
+        // missing broker on mounted device
+        when(mountPoint.getService(DOMDataBroker.class)).thenReturn(Optional.absent());
+
+        when(mountDataBroker.newReadWriteTransaction()).thenReturn(transaction);
+        when(transaction.submit()).thenReturn(expFuture);
+
+        final PatchStatusContext status = this.brokerFacade.patchConfigurationDataWithinTransaction(patchContext);
+
+        // assert not successful operation with error
+        assertNotNull(status.getGlobalErrors());
+        assertEquals(1, status.getGlobalErrors().size());
+        assertEquals(ErrorType.APPLICATION, status.getGlobalErrors().get(0).getErrorType());
+        assertEquals(ErrorTag.OPERATION_FAILED, status.getGlobalErrors().get(0).getErrorTag());
+
+        assertFalse("Patch operation should fail on mounted device without Broker", status.isOk());
     }
 }