Do not reference individual builders
[netconf.git] / netconf / netconf-topology-singleton / src / test / java / org / opendaylight / netconf / topology / singleton / impl / NetconfNodeActorTest.java
index 145140011e7e61c52724f4fb746fd78d9003c303..c630197ccb716331790bff13843d7fbb762b760d 100644 (file)
@@ -8,8 +8,13 @@
 package org.opendaylight.netconf.topology.singleton.impl;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyCollection;
@@ -25,7 +30,8 @@ import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.MockitoAnnotations.initMocks;
+import static org.opendaylight.mdsal.common.api.CommitInfo.emptyFluentFuture;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
 
 import akka.actor.ActorRef;
 import akka.actor.ActorSystem;
@@ -38,28 +44,35 @@ import akka.testkit.TestActorRef;
 import akka.testkit.javadsl.TestKit;
 import akka.util.Timeout;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
 import com.google.common.io.ByteSource;
 import com.google.common.net.InetAddresses;
+import com.google.common.util.concurrent.FluentFuture;
 import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
 import java.io.InputStream;
 import java.net.InetSocketAddress;
+import java.time.Duration;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 import java.util.Scanner;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.controller.cluster.schema.provider.impl.YangTextSchemaSourceSerializationProxy;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMActionException;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
@@ -69,9 +82,14 @@ import org.opendaylight.mdsal.dom.api.DOMNotificationService;
 import org.opendaylight.mdsal.dom.api.DOMRpcException;
 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
+import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor;
+import org.opendaylight.netconf.topology.singleton.impl.utils.ClusteringActionException;
 import org.opendaylight.netconf.topology.singleton.impl.utils.ClusteringRpcException;
 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup;
 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup.NetconfTopologySetupBuilder;
@@ -84,19 +102,20 @@ import org.opendaylight.netconf.topology.singleton.messages.RegisterMountPoint;
 import org.opendaylight.netconf.topology.singleton.messages.UnregisterSlaveMountPoint;
 import org.opendaylight.yangtools.concepts.ObjectRegistration;
 import org.opendaylight.yangtools.util.concurrent.FluentFutures;
+import org.opendaylight.yangtools.yang.common.ErrorType;
 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.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
-import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
@@ -105,23 +124,20 @@ import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
-import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToIRTransformer;
 import scala.concurrent.Await;
 import scala.concurrent.Future;
-import scala.concurrent.duration.Duration;
 
-public class NetconfNodeActorTest {
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class NetconfNodeActorTest extends AbstractBaseSchemasTest {
 
-    private static final Timeout TIMEOUT = new Timeout(Duration.create(5, "seconds"));
-    private static final RevisionSourceIdentifier SOURCE_IDENTIFIER1 = RevisionSourceIdentifier.create("yang1");
-    private static final RevisionSourceIdentifier SOURCE_IDENTIFIER2 = RevisionSourceIdentifier.create("yang2");
+    private static final Timeout TIMEOUT = Timeout.create(Duration.ofSeconds(5));
+    private static final SourceIdentifier SOURCE_IDENTIFIER1 = new SourceIdentifier("yang1");
+    private static final SourceIdentifier SOURCE_IDENTIFIER2 = new SourceIdentifier("yang2");
 
     private ActorSystem system = ActorSystem.create();
     private final TestKit testKit = new TestKit(system);
 
-    @Rule
-    public final ExpectedException exception = ExpectedException.none();
-
     private ActorRef masterRef;
     private RemoteDeviceId remoteDeviceId;
     private final SharedSchemaRepository masterSchemaRepository = new SharedSchemaRepository("master");
@@ -129,6 +145,9 @@ public class NetconfNodeActorTest {
     @Mock
     private DOMRpcService mockDOMRpcService;
 
+    @Mock
+    private DOMActionService mockDOMActionService;
+
     @Mock
     private DOMMountPointService mockMountPointService;
 
@@ -141,6 +160,9 @@ public class NetconfNodeActorTest {
     @Mock
     private DOMDataBroker mockDOMDataBroker;
 
+    @Mock
+    private NetconfDataTreeService netconfService;
+
     @Mock
     private SchemaSourceRegistration<?> mockSchemaSourceReg1;
 
@@ -159,21 +181,24 @@ public class NetconfNodeActorTest {
     @Mock
     private EffectiveModelContext mockSchemaContext;
 
+    @Mock
+    private SchemaResourcesDTO schemaResourceDTO;
+
     @Before
     public void setup() {
-        initMocks(this);
-
         remoteDeviceId = new RemoteDeviceId("netconf-topology",
                 new InetSocketAddress(InetAddresses.forString("127.0.0.1"), 9999));
 
         masterSchemaRepository.registerSchemaSourceListener(
-                TextToASTTransformer.create(masterSchemaRepository, masterSchemaRepository));
+                TextToIRTransformer.create(masterSchemaRepository, masterSchemaRepository));
 
+        doReturn(masterSchemaRepository).when(schemaResourceDTO).getSchemaRepository();
+        doReturn(mockRegistry).when(schemaResourceDTO).getSchemaRegistry();
         final NetconfTopologySetup setup = NetconfTopologySetupBuilder.create().setActorSystem(system)
-                .setIdleTimeout(Duration.apply(1, TimeUnit.SECONDS)).build();
+                .setIdleTimeout(Duration.ofSeconds(1)).setSchemaResourceDTO(schemaResourceDTO)
+                .setBaseSchemas(BASE_SCHEMAS).build();
 
-        final Props props = NetconfNodeActor.props(setup, remoteDeviceId, masterSchemaRepository,
-                masterSchemaRepository, TIMEOUT, mockMountPointService);
+        final Props props = NetconfNodeActor.props(setup, remoteDeviceId, TIMEOUT, mockMountPointService);
 
         masterRef = TestActorRef.create(system, props, "master_messages");
 
@@ -206,7 +231,8 @@ public class NetconfNodeActorTest {
         final RemoteDeviceId newRemoteDeviceId = new RemoteDeviceId("netconf-topology2",
                 new InetSocketAddress(InetAddresses.forString("127.0.0.2"), 9999));
 
-        final NetconfTopologySetup newSetup = NetconfTopologySetupBuilder.create().setActorSystem(system).build();
+        final NetconfTopologySetup newSetup = NetconfTopologySetupBuilder.create().setBaseSchemas(BASE_SCHEMAS)
+                .setSchemaResourceDTO(schemaResourceDTO).setActorSystem(system).build();
 
         masterRef.tell(new RefreshSetupMasterActorData(newSetup, newRemoteDeviceId), testKit.getRef());
 
@@ -214,7 +240,7 @@ public class NetconfNodeActorTest {
     }
 
     @Test
-    public void tesAskForMasterMountPoint() {
+    public void testAskForMasterMountPoint() {
 
         // Test with master not setup yet.
 
@@ -227,7 +253,7 @@ public class NetconfNodeActorTest {
 
         // Now initialize - master should send the RegisterMountPoint message.
 
-        List<SourceIdentifier> sourceIdentifiers = Lists.newArrayList(RevisionSourceIdentifier.create("testID"));
+        List<SourceIdentifier> sourceIdentifiers = List.of(new SourceIdentifier("testID"));
         initializeMaster(sourceIdentifiers);
 
         masterRef.tell(new AskForMasterMountPoint(kit.getRef()), kit.getRef());
@@ -258,9 +284,6 @@ public class NetconfNodeActorTest {
 
         doReturn(mockSchemaSourceReg1).when(mockRegistry).registerSchemaSource(any(), withSourceId(SOURCE_IDENTIFIER1));
 
-        doReturn(mockSchemaContextFactory).when(mockSchemaRepository)
-                .createEffectiveModelContextFactory();
-
         final SchemaSourceRegistration<?> newMockSchemaSourceReg = mock(SchemaSourceRegistration.class);
 
         final EffectiveModelContextFactory newMockSchemaContextFactory = mock(EffectiveModelContextFactory.class);
@@ -290,10 +313,11 @@ public class NetconfNodeActorTest {
         slaveRef.tell(new RegisterMountPoint(ImmutableList.of(SOURCE_IDENTIFIER1), masterRef), testKit.getRef());
 
         verify(mockMountPointBuilder, timeout(5000)).register();
-        verify(mockMountPointBuilder, after(500)).addInitialSchemaContext(mockSchemaContext);
-        verify(mockMountPointBuilder).addService(eq(DOMDataBroker.class), any());
+        verify(mockMountPointBuilder, after(500)).addService(eq(DOMDataBroker.class), any());
         verify(mockMountPointBuilder).addService(eq(DOMRpcService.class), any());
+        verify(mockMountPointBuilder).addService(eq(DOMActionService.class), any());
         verify(mockMountPointBuilder).addService(eq(DOMNotificationService.class), any());
+        verify(mockMountPointBuilder).addService(eq(DOMSchemaService.class), any());
         verify(mockSchemaSourceReg1).close();
         verify(mockRegistry, times(2)).registerSchemaSource(any(), withSourceId(SOURCE_IDENTIFIER1));
         verify(mockSchemaRepository, times(2)).createEffectiveModelContextFactory();
@@ -311,10 +335,17 @@ public class NetconfNodeActorTest {
     @SuppressWarnings("unchecked")
     @Test
     public void testRegisterMountPointWithSchemaFailures() throws Exception {
-        final NetconfTopologySetup setup = NetconfTopologySetupBuilder.create().setActorSystem(system).build();
-
-        final ActorRef slaveRef = system.actorOf(NetconfNodeActor.props(setup, remoteDeviceId, mockRegistry,
-                mockSchemaRepository, TIMEOUT, mockMountPointService));
+        SchemaResourcesDTO schemaResourceDTO2 = mock(SchemaResourcesDTO.class);
+        doReturn(mockRegistry).when(schemaResourceDTO2).getSchemaRegistry();
+        doReturn(mockSchemaRepository).when(schemaResourceDTO2).getSchemaRepository();
+        final NetconfTopologySetup setup = NetconfTopologySetupBuilder.create()
+                .setSchemaResourceDTO(schemaResourceDTO2)
+                .setBaseSchemas(BASE_SCHEMAS)
+                .setActorSystem(system)
+                .build();
+
+        final ActorRef slaveRef = system.actorOf(NetconfNodeActor.props(setup, remoteDeviceId, TIMEOUT,
+                mockMountPointService));
 
         // Test unrecoverable failure.
 
@@ -383,9 +414,32 @@ public class NetconfNodeActorTest {
         verify(mockSchemaRepository, times(2)).createEffectiveModelContextFactory();
     }
 
+    @Test(expected = MissingSchemaSourceException.class)
+    public void testMissingSchemaSourceOnMissingProvider() throws Exception {
+        final SharedSchemaRepository repository = new SharedSchemaRepository("test");
+
+        SchemaResourcesDTO schemaResourceDTO2 = mock(SchemaResourcesDTO.class);
+        doReturn(repository).when(schemaResourceDTO2).getSchemaRegistry();
+        doReturn(repository).when(schemaResourceDTO2).getSchemaRepository();
+        final NetconfTopologySetup setup = NetconfTopologySetupBuilder.create().setActorSystem(system)
+                .setSchemaResourceDTO(schemaResourceDTO2).setIdleTimeout(Duration.ofSeconds(1))
+                .setBaseSchemas(BASE_SCHEMAS).build();
+        final Props props = NetconfNodeActor.props(setup, remoteDeviceId, TIMEOUT, mockMountPointService);
+        ActorRef actor = TestActorRef.create(system, props, "master_messages_2");
+
+        final SourceIdentifier sourceIdentifier = new SourceIdentifier("testID");
+
+        final ProxyYangTextSourceProvider proxyYangProvider =
+                new ProxyYangTextSourceProvider(actor, system.dispatcher(), TIMEOUT);
+
+        final Future<YangTextSchemaSourceSerializationProxy> resolvedSchemaFuture =
+                proxyYangProvider.getYangTextSchemaSource(sourceIdentifier);
+        Await.result(resolvedSchemaFuture, TIMEOUT.duration());
+    }
+
     @Test
     public void testYangTextSchemaSourceRequest() throws Exception {
-        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("testID");
+        final SourceIdentifier sourceIdentifier = new SourceIdentifier("testID");
 
         final ProxyYangTextSourceProvider proxyYangProvider =
                 new ProxyYangTextSourceProvider(masterRef, system.dispatcher(), TIMEOUT);
@@ -409,24 +463,22 @@ public class NetconfNodeActorTest {
 
         // Test missing source failure.
 
-        exception.expect(MissingSchemaSourceException.class);
-
         schemaSourceReg.close();
 
-        final Future<YangTextSchemaSourceSerializationProxy> failedSchemaFuture =
-                proxyYangProvider.getYangTextSchemaSource(sourceIdentifier);
-
-        Await.result(failedSchemaFuture, TIMEOUT.duration());
+        final MissingSchemaSourceException ex = assertThrows(MissingSchemaSourceException.class,
+            () -> {
+                final Future<YangTextSchemaSourceSerializationProxy> failedSchemaFuture =
+                        proxyYangProvider.getYangTextSchemaSource(sourceIdentifier);
+                Await.result(failedSchemaFuture, TIMEOUT.duration());
+            });
+        assertThat(ex.getMessage(), startsWith("No providers registered for source"));
+        assertThat(ex.getMessage(), containsString(sourceIdentifier.toString()));
     }
 
     @Test
-    @SuppressWarnings({"checkstyle:AvoidHidingCauseException", "checkstyle:IllegalThrows"})
-    public void testSlaveInvokeRpc() throws Throwable {
-
-        final List<SourceIdentifier> sourceIdentifiers =
-                Lists.newArrayList(RevisionSourceIdentifier.create("testID"));
+    public void testSlaveInvokeRpc() throws Exception {
 
-        initializeMaster(sourceIdentifiers);
+        initializeMaster(List.of(new SourceIdentifier("testID")));
         registerSlaveMountPoint();
 
         ArgumentCaptor<DOMRpcService> domRPCServiceCaptor = ArgumentCaptor.forClass(DOMRpcService.class);
@@ -436,17 +488,16 @@ public class NetconfNodeActorTest {
         assertTrue(slaveDomRPCService instanceof ProxyDOMRpcService);
 
         final QName testQName = QName.create("", "TestQname");
-        final SchemaPath schemaPath = SchemaPath.create(true, testQName);
-        final NormalizedNode<?, ?> outputNode = ImmutableContainerNodeBuilder.create()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(testQName))
+        final ContainerNode outputNode = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(testQName))
                 .withChild(ImmutableNodes.leafNode(testQName, "foo")).build();
-        final RpcError rpcError = RpcResultBuilder.newError(RpcError.ErrorType.RPC, null, "Rpc invocation failed.");
+        final RpcError rpcError = RpcResultBuilder.newError(ErrorType.RPC, null, "Rpc invocation failed.");
 
         // RPC with no response output.
 
         doReturn(FluentFutures.immediateNullFluentFuture()).when(mockDOMRpcService).invokeRpc(any(), any());
 
-        DOMRpcResult result = slaveDomRPCService.invokeRpc(schemaPath, outputNode).get(2, TimeUnit.SECONDS);
+        DOMRpcResult result = slaveDomRPCService.invokeRpc(testQName, outputNode).get(2, TimeUnit.SECONDS);
 
         assertEquals(null, result);
 
@@ -455,7 +506,7 @@ public class NetconfNodeActorTest {
         doReturn(FluentFutures.immediateFluentFuture(new DefaultDOMRpcResult(outputNode)))
                 .when(mockDOMRpcService).invokeRpc(any(), any());
 
-        result = slaveDomRPCService.invokeRpc(schemaPath, outputNode).get(2, TimeUnit.SECONDS);
+        result = slaveDomRPCService.invokeRpc(testQName, outputNode).get(2, TimeUnit.SECONDS);
 
         assertEquals(outputNode, result.getResult());
         assertTrue(result.getErrors().isEmpty());
@@ -465,7 +516,7 @@ public class NetconfNodeActorTest {
         doReturn(FluentFutures.immediateFluentFuture(new DefaultDOMRpcResult(rpcError)))
                 .when(mockDOMRpcService).invokeRpc(any(), any());
 
-        result = slaveDomRPCService.invokeRpc(schemaPath, outputNode).get(2, TimeUnit.SECONDS);
+        result = slaveDomRPCService.invokeRpc(testQName, outputNode).get(2, TimeUnit.SECONDS);
 
         assertNull(result.getResult());
         assertEquals(rpcError, result.getErrors().iterator().next());
@@ -476,33 +527,80 @@ public class NetconfNodeActorTest {
                 .when(mockDOMRpcService).invokeRpc(any(), any());
 
         final DOMRpcResult resultOutputError =
-                slaveDomRPCService.invokeRpc(schemaPath, outputNode).get(2, TimeUnit.SECONDS);
+                slaveDomRPCService.invokeRpc(testQName, outputNode).get(2, TimeUnit.SECONDS);
 
         assertEquals(outputNode, resultOutputError.getResult());
         assertEquals(rpcError, resultOutputError.getErrors().iterator().next());
 
         // RPC failure.
+        doReturn(FluentFutures.immediateFailedFluentFuture(new ClusteringRpcException("mock")))
+            .when(mockDOMRpcService).invokeRpc(any(), any());
+        final ListenableFuture<? extends DOMRpcResult> future = slaveDomRPCService.invokeRpc(testQName, outputNode);
 
-        exception.expect(DOMRpcException.class);
+        final ExecutionException e = assertThrows(ExecutionException.class, () -> future.get(2, TimeUnit.SECONDS));
+        final Throwable cause = e.getCause();
+        assertThat(cause, instanceOf(DOMRpcException.class));
+        assertEquals("mock", cause.getMessage());
+    }
 
-        doReturn(FluentFutures.immediateFailedFluentFuture(new ClusteringRpcException("mock")))
-                .when(mockDOMRpcService).invokeRpc(any(), any());
+    @Test
+    public void testSlaveInvokeAction() throws Exception {
+        initializeMaster(List.of(new SourceIdentifier("testActionID")));
+        registerSlaveMountPoint();
 
-        try {
-            slaveDomRPCService.invokeRpc(schemaPath, outputNode).get(2, TimeUnit.SECONDS);
-        } catch (ExecutionException e) {
-            throw e.getCause();
-        }
+        ArgumentCaptor<DOMActionService> domActionServiceCaptor = ArgumentCaptor.forClass(DOMActionService.class);
+        verify(mockMountPointBuilder).addService(eq(DOMActionService.class), domActionServiceCaptor.capture());
+
+        final DOMActionService slaveDomActionService = domActionServiceCaptor.getValue();
+        assertTrue(slaveDomActionService instanceof ProxyDOMActionService);
+
+        final QName testQName = QName.create("test", "2019-08-16", "TestActionQname");
+        final Absolute schemaPath = Absolute.of(testQName);
+
+        final YangInstanceIdentifier yangIIdPath = YangInstanceIdentifier.create(new NodeIdentifier(testQName));
+
+        final DOMDataTreeIdentifier domDataTreeIdentifier = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
+            yangIIdPath);
+
+        final ContainerNode outputNode = Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(testQName))
+            .withChild(ImmutableNodes.leafNode(testQName, "foo")).build();
+
+        // Action with no response output.
+        doReturn(FluentFutures.immediateNullFluentFuture()).when(mockDOMActionService)
+            .invokeAction(any(), any(), any());
+        DOMActionResult result = slaveDomActionService.invokeAction(schemaPath, domDataTreeIdentifier, outputNode)
+            .get(2, TimeUnit.SECONDS);
+        assertEquals(null, result);
+
+        // Action with response output.
+        doReturn(FluentFutures.immediateFluentFuture(new SimpleDOMActionResult(outputNode))).when(mockDOMActionService)
+            .invokeAction(any(), any(), any());
+        result = slaveDomActionService.invokeAction(schemaPath, domDataTreeIdentifier, outputNode)
+            .get(2, TimeUnit.SECONDS);
+
+        assertEquals(outputNode, result.getOutput().get());
+        assertTrue(result.getErrors().isEmpty());
+
+        // Action failure.
+        doReturn(FluentFutures.immediateFailedFluentFuture(new ClusteringActionException("mock")))
+            .when(mockDOMActionService).invokeAction(any(), any(), any());
+        final ListenableFuture<? extends DOMActionResult> future = slaveDomActionService.invokeAction(schemaPath,
+            domDataTreeIdentifier, outputNode);
+
+        final ExecutionException e = assertThrows(ExecutionException.class, () -> future.get(2, TimeUnit.SECONDS));
+        final Throwable cause = e.getCause();
+        assertThat(cause, instanceOf(DOMActionException.class));
+        assertEquals("mock", cause.getMessage());
     }
 
     @Test
     public void testSlaveNewTransactionRequests() {
-
         doReturn(mock(DOMDataTreeReadTransaction.class)).when(mockDOMDataBroker).newReadOnlyTransaction();
         doReturn(mock(DOMDataTreeReadWriteTransaction.class)).when(mockDOMDataBroker).newReadWriteTransaction();
         doReturn(mock(DOMDataTreeWriteTransaction.class)).when(mockDOMDataBroker).newWriteOnlyTransaction();
 
-        initializeMaster(Collections.emptyList());
+        initializeMaster(List.of());
         registerSlaveMountPoint();
 
         ArgumentCaptor<DOMDataBroker> domDataBrokerCaptor = ArgumentCaptor.forClass(DOMDataBroker.class);
@@ -521,10 +619,58 @@ public class NetconfNodeActorTest {
         verify(mockDOMDataBroker).newWriteOnlyTransaction();
     }
 
+    @Test
+    public void testSlaveNewNetconfDataTreeServiceRequest() {
+        initializeMaster(List.of());
+        registerSlaveMountPoint();
+
+        ArgumentCaptor<NetconfDataTreeService> netconfCaptor = ArgumentCaptor.forClass(NetconfDataTreeService.class);
+        verify(mockMountPointBuilder).addService(eq(NetconfDataTreeService.class), netconfCaptor.capture());
+
+        final NetconfDataTreeService slaveNetconfService = netconfCaptor.getValue();
+        assertTrue(slaveNetconfService instanceof ProxyNetconfDataTreeService);
+
+        final YangInstanceIdentifier PATH = YangInstanceIdentifier.empty();
+        final LogicalDatastoreType STORE = LogicalDatastoreType.CONFIGURATION;
+        final ContainerNode NODE = Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(QName.create("", "cont")))
+            .build();
+
+        final FluentFuture<Optional<Object>> result = immediateFluentFuture(Optional.of(NODE));
+        doReturn(result).when(netconfService).get(PATH);
+        doReturn(result).when(netconfService).getConfig(PATH);
+        doReturn(emptyFluentFuture()).when(netconfService).commit();
+
+        slaveNetconfService.get(PATH);
+        slaveNetconfService.getConfig(PATH);
+        slaveNetconfService.lock();
+        slaveNetconfService.merge(STORE, PATH, NODE, Optional.empty());
+        slaveNetconfService.replace(STORE, PATH, NODE, Optional.empty());
+        slaveNetconfService.create(STORE, PATH, NODE, Optional.empty());
+        slaveNetconfService.delete(STORE, PATH);
+        slaveNetconfService.remove(STORE, PATH);
+        slaveNetconfService.discardChanges();
+        slaveNetconfService.commit();
+
+        verify(netconfService, timeout(1000)).get(PATH);
+        verify(netconfService, timeout(1000)).getConfig(PATH);
+        verify(netconfService, timeout(1000)).lock();
+        verify(netconfService, timeout(1000)).merge(STORE, PATH, NODE, Optional.empty());
+        verify(netconfService, timeout(1000)).replace(STORE, PATH, NODE, Optional.empty());
+        verify(netconfService, timeout(1000)).create(STORE, PATH, NODE, Optional.empty());
+        verify(netconfService, timeout(1000)).delete(STORE, PATH);
+        verify(netconfService, timeout(1000)).remove(STORE, PATH);
+        verify(netconfService, timeout(1000)).discardChanges();
+        verify(netconfService, timeout(1000)).commit();
+    }
+
     private ActorRef registerSlaveMountPoint() {
+        SchemaResourcesDTO schemaResourceDTO2 = mock(SchemaResourcesDTO.class);
+        doReturn(mockRegistry).when(schemaResourceDTO2).getSchemaRegistry();
+        doReturn(mockSchemaRepository).when(schemaResourceDTO2).getSchemaRepository();
         final ActorRef slaveRef = system.actorOf(NetconfNodeActor.props(
-                NetconfTopologySetupBuilder.create().setActorSystem(system).build(), remoteDeviceId, mockRegistry,
-                mockSchemaRepository, TIMEOUT, mockMountPointService));
+                NetconfTopologySetupBuilder.create().setSchemaResourceDTO(schemaResourceDTO2).setActorSystem(system)
+                .setBaseSchemas(BASE_SCHEMAS).build(), remoteDeviceId, TIMEOUT, mockMountPointService));
 
         doReturn(Futures.immediateFuture(mockSchemaContext))
                 .when(mockSchemaContextFactory).createEffectiveModelContext(anyCollection());
@@ -533,20 +679,19 @@ public class NetconfNodeActorTest {
                 masterRef), testKit.getRef());
 
         verify(mockMountPointBuilder, timeout(5000)).register();
-        verify(mockMountPointBuilder).addInitialSchemaContext(mockSchemaContext);
+        verify(mockMountPointBuilder).addService(eq(DOMSchemaService.class), any());
         verify(mockMountPointBuilder).addService(eq(DOMDataBroker.class), any());
+        verify(mockMountPointBuilder).addService(eq(NetconfDataTreeService.class), any());
         verify(mockMountPointBuilder).addService(eq(DOMRpcService.class), any());
         verify(mockMountPointBuilder).addService(eq(DOMNotificationService.class), any());
 
         testKit.expectMsgClass(Success.class);
-
         return slaveRef;
     }
 
     private void initializeMaster(final List<SourceIdentifier> sourceIdentifiers) {
-        masterRef.tell(new CreateInitialMasterActorData(mockDOMDataBroker, sourceIdentifiers,
-                mockDOMRpcService), testKit.getRef());
-
+        masterRef.tell(new CreateInitialMasterActorData(mockDOMDataBroker, netconfService, sourceIdentifiers,
+                mockDOMRpcService, mockDOMActionService), testKit.getRef());
         testKit.expectMsgClass(MasterActorDataInitialized.class);
     }
 
@@ -555,7 +700,6 @@ public class NetconfNodeActorTest {
 
         doNothing().when(mockMountPointReg).close();
 
-        doReturn(mockMountPointBuilder).when(mockMountPointBuilder).addInitialSchemaContext(any());
         doReturn(mockMountPointBuilder).when(mockMountPointBuilder).addService(any(), any());
         doReturn(mockMountPointReg).when(mockMountPointBuilder).register();
     }