Move AbstractDOMDataBroker to mdsal-dom-spi
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / DOMRpcRouterTest.java
index 9c62d0daad69f2d23706954336d46394a842cf47..55f81585c852a9c500d90d96b379d1f8056820f7 100644 (file)
  */
 package org.opendaylight.mdsal.dom.broker;
 
-import static org.junit.Assert.assertFalse;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.opendaylight.mdsal.dom.broker.TestUtils.getTestRpcImplementation;
 
-import java.util.Collection;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.RejectedExecutionException;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension;
+import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension.AvailabilityListener;
+import org.opendaylight.mdsal.dom.api.DOMActionImplementation;
+import org.opendaylight.mdsal.dom.api.DOMActionInstance;
+import org.opendaylight.mdsal.dom.api.DOMActionNotAvailableException;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMActionService;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener;
 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
-import org.opendaylight.mdsal.dom.broker.util.TestModel;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
+import org.opendaylight.yangtools.concepts.Registration;
+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.spi.node.ImmutableNodes;
 
-public class DOMRpcRouterTest extends TestUtils {
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class DOMRpcRouterTest {
+    private static final YangInstanceIdentifier BAZ_PATH_BAD = YangInstanceIdentifier.of(
+        new NodeIdentifier(Actions.FOO), NodeIdentifierWithPredicates.of(Actions.FOO, Actions.BAR, "bad"));
+    private static final YangInstanceIdentifier BAZ_PATH_GOOD = YangInstanceIdentifier.of(
+        new NodeIdentifier(Actions.FOO), NodeIdentifierWithPredicates.of(Actions.FOO, Actions.BAR, "good"));
+
+    private static final DOMActionImplementation IMPL =
+        (type, path, input) -> Futures.immediateFuture(new SimpleDOMActionResult(
+            ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(Actions.OUTPUT)).build()));
 
     @Test
     public void registerRpcImplementation() {
-        try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
-            DOMRpcRoutingTable routingTable = rpcRouter.routingTable();
-            assertFalse(routingTable.getRpcs().containsKey(SchemaPath.ROOT));
-
-            rpcRouter.getRpcProviderService().registerRpcImplementation(getTestRpcImplementation(),
-                DOMRpcIdentifier.create(SchemaPath.ROOT, null));
-            routingTable = rpcRouter.routingTable();
-            assertTrue(routingTable.getRpcs().containsKey(SchemaPath.ROOT));
-
-            rpcRouter.getRpcProviderService().registerRpcImplementation(getTestRpcImplementation(),
-                DOMRpcIdentifier.create(SchemaPath.SAME, null));
-            routingTable = rpcRouter.routingTable();
-            assertTrue(routingTable.getRpcs().containsKey(SchemaPath.SAME));
+        try (DOMRpcRouter rpcRouter = rpcsRouter()) {
+            assertOperationKeys(rpcRouter);
+
+            final Registration fooReg = rpcRouter.rpcProviderService().registerRpcImplementation(
+                getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.FOO, null));
+            assertOperationKeys(rpcRouter, Rpcs.FOO);
+
+            final Registration barReg = rpcRouter.rpcProviderService().registerRpcImplementation(
+                getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.BAR, null));
+            assertOperationKeys(rpcRouter, Rpcs.FOO, Rpcs.BAR);
+
+            fooReg.close();
+            assertOperationKeys(rpcRouter, Rpcs.BAR);
+            barReg.close();
+            assertOperationKeys(rpcRouter);
         }
     }
 
     @Test
-    public void invokeRpc() {
-        try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
-            assertNotNull(rpcRouter.getRpcService().invokeRpc(SchemaPath.create(false, TestModel.TEST_QNAME), null));
+    public void registerRpcImplementations() {
+        try (DOMRpcRouter rpcRouter = rpcsRouter()) {
+            assertOperationKeys(rpcRouter);
+
+            final Registration fooReg = rpcRouter.rpcProviderService().registerRpcImplementations(
+                Map.of(DOMRpcIdentifier.create(Rpcs.FOO, null), getTestRpcImplementation()));
+            assertOperationKeys(rpcRouter, Rpcs.FOO);
+
+            final Registration barReg = rpcRouter.rpcProviderService().registerRpcImplementations(
+                Map.of(
+                    DOMRpcIdentifier.create(Rpcs.BAR, null), getTestRpcImplementation(),
+                    DOMRpcIdentifier.create(Rpcs.BAZ, null), getTestRpcImplementation()));
+            assertOperationKeys(rpcRouter, Rpcs.FOO, Rpcs.BAR, Rpcs.BAZ);
+
+            fooReg.close();
+            assertOperationKeys(rpcRouter, Rpcs.BAR, Rpcs.BAZ);
+            barReg.close();
+            assertOperationKeys(rpcRouter);
         }
     }
 
+
+    private static void assertOperationKeys(final DOMRpcRouter router, final QName... keys) {
+        assertEquals(Set.of(keys), router.routingTable().getOperations().keySet());
+    }
+
     @Test
-    public void registerRpcListener() {
-        try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
-            final DOMRpcAvailabilityListener listener = mock(DOMRpcAvailabilityListener.class);
+    public void testFailedInvokeRpc() {
+        try (var rpcRouter = rpcsRouter()) {
+            final ListenableFuture<?> future = rpcRouter.rpcService().invokeRpc(Rpcs.FOO, null);
+            final Throwable cause = assertThrows(ExecutionException.class, () -> Futures.getDone(future)).getCause();
+            assertThat(cause, instanceOf(DOMRpcImplementationNotAvailableException.class));
+            assertEquals("No implementation of RPC (rpcs)foo available", cause.getMessage());
+        }
+    }
+
+    @Test
+    public void testRpcListener() {
+        try (var rpcRouter = new DOMRpcRouter()) {
+            assertEquals(List.of(), rpcRouter.listeners());
+
+            final var listener = mock(DOMRpcAvailabilityListener.class);
+            doCallRealMethod().when(listener).acceptsImplementation(any());
+            doNothing().when(listener).onRpcAvailable(any());
+            doNothing().when(listener).onRpcUnavailable(any());
 
-            final Collection<?> listenersOriginal = rpcRouter.listeners();
+            final var reg = rpcRouter.rpcService().registerRpcListener(listener);
+            assertNotNull(reg);
+            assertEquals(List.of(reg), rpcRouter.listeners());
 
-            assertNotNull(rpcRouter.getRpcService().registerRpcListener(listener));
+            final var implReg = rpcRouter.rpcProviderService().registerRpcImplementation(
+                getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.FOO, null));
+            verify(listener, timeout(1000)).onRpcAvailable(any());
 
-            final Collection<?> listenersChanged = rpcRouter.listeners();
-            assertNotEquals(listenersOriginal, listenersChanged);
-            assertTrue(listenersOriginal.isEmpty());
-            assertFalse(listenersChanged.isEmpty());
+            implReg.close();
+            verify(listener, timeout(1000)).onRpcUnavailable(any());
+
+            reg.close();
+            assertEquals(List.of(), rpcRouter.listeners());
+        }
+    }
+
+    @Test
+    public void testActionListener() {
+        try (var rpcRouter = new DOMRpcRouter()) {
+            assertEquals(List.of(), rpcRouter.actionListeners());
+
+            final var listener = mock(AvailabilityListener.class);
+            final var availability = rpcRouter.actionService().extension(DOMActionAvailabilityExtension.class);
+            assertNotNull(availability);
+            final var reg = availability.registerAvailabilityListener(listener);
+            assertNotNull(reg);
+            assertEquals(List.of(reg), rpcRouter.actionListeners());
+
+            // FIXME: register implementation and verify notification
+
+            reg.close();
+            assertEquals(List.of(), rpcRouter.actionListeners());
         }
     }
 
     @Test
     public void onGlobalContextUpdated() {
         try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
-
             final DOMRpcRoutingTable routingTableOriginal = rpcRouter.routingTable();
+            rpcRouter.onModelContextUpdated(TestModel.createTestContext());
+            assertNotEquals(routingTableOriginal, rpcRouter.routingTable());
+        }
+    }
+
+    @Test
+    public void testClose() {
+        final var reg = mock(Registration.class);
+        doNothing().when(reg).close();
+        final DOMSchemaService schema = mock(DOMSchemaService.class);
+        doReturn(reg).when(schema).registerSchemaContextListener(any());
+
+        final var rpcRouter = new DOMRpcRouter(schema);
+        rpcRouter.close();
+
+        final var svc = rpcRouter.rpcProviderService();
+        assertThrows(RejectedExecutionException.class, () -> svc.registerRpcImplementation(getTestRpcImplementation(),
+            DOMRpcIdentifier.create(Rpcs.FOO, null)));
+    }
+
+    @Test
+    public void testActionInstanceRouting() throws ExecutionException {
+        try (var rpcRouter = actionsRouter()) {
+            final var actionProvider = rpcRouter.actionProviderService();
+            assertNotNull(actionProvider);
+            final var actionConsumer = rpcRouter.actionService();
+            assertNotNull(actionConsumer);
+
+            try (var reg = actionProvider.registerActionImplementation(IMPL,
+                DOMActionInstance.of(Actions.BAZ_TYPE, LogicalDatastoreType.OPERATIONAL, BAZ_PATH_GOOD))) {
 
-            rpcRouter.onGlobalContextUpdated(TestModel.createTestContext());
+                assertAvailable(actionConsumer, BAZ_PATH_GOOD);
+                assertUnavailable(actionConsumer, BAZ_PATH_BAD);
+            }
 
-            final DOMRpcRoutingTable routingTableChanged = rpcRouter.routingTable();
-            assertNotEquals(routingTableOriginal, routingTableChanged);
+            assertUnavailable(actionConsumer, BAZ_PATH_BAD);
+            assertUnavailable(actionConsumer, BAZ_PATH_GOOD);
         }
     }
 
-    @Test(expected = RejectedExecutionException.class)
-    public void close() {
-        final DOMRpcRouter rpcRouter = new DOMRpcRouter();
-        rpcRouter.close();
-        rpcRouter.getRpcProviderService().registerRpcImplementation(getTestRpcImplementation(),
-            DOMRpcIdentifier.create(SchemaPath.ROOT, null));
+    @Test
+    public void testActionDatastoreRouting() throws ExecutionException {
+        try (var rpcRouter = actionsRouter()) {
+            final var actionProvider = rpcRouter.actionProviderService();
+            assertNotNull(actionProvider);
+            final var actionConsumer = rpcRouter.actionService();
+            assertNotNull(actionConsumer);
+
+            try (var reg = actionProvider.registerActionImplementation(IMPL,
+                DOMActionInstance.of(Actions.BAZ_TYPE, LogicalDatastoreType.OPERATIONAL,
+                    YangInstanceIdentifier.of()))) {
+
+                assertAvailable(actionConsumer, BAZ_PATH_GOOD);
+                assertAvailable(actionConsumer, BAZ_PATH_BAD);
+            }
+
+            assertUnavailable(actionConsumer, BAZ_PATH_BAD);
+            assertUnavailable(actionConsumer, BAZ_PATH_GOOD);
+        }
+    }
+
+    private static DOMRpcRouter actionsRouter() {
+        final DOMRpcRouter router = new DOMRpcRouter();
+        router.onModelContextUpdated(Actions.CONTEXT);
+        return router;
+    }
+
+    private static DOMRpcRouter rpcsRouter() {
+        final DOMRpcRouter router = new DOMRpcRouter();
+        router.onModelContextUpdated(Rpcs.CONTEXT);
+        return router;
+    }
+
+    private static void assertAvailable(final DOMActionService actionService, final YangInstanceIdentifier path)
+            throws ExecutionException {
+        final DOMActionResult result = Futures.getDone(invokeBaz(actionService, path));
+        assertEquals(List.of(), result.getErrors());
+    }
+
+    private static void assertUnavailable(final DOMActionService actionService, final YangInstanceIdentifier path) {
+        final ListenableFuture<? extends DOMActionResult> future = invokeBaz(actionService, path);
+        final ExecutionException ex = assertThrows(ExecutionException.class, () -> Futures.getDone(future));
+        assertThat(ex.getCause(), instanceOf(DOMActionNotAvailableException.class));
+    }
+
+    private static ListenableFuture<? extends DOMActionResult> invokeBaz(final DOMActionService actionService,
+            final YangInstanceIdentifier path) {
+        return actionService.invokeAction(Actions.BAZ_TYPE,
+            DOMDataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL, path),
+            ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(Actions.INPUT)).build());
     }
 }